2020-06-24 00:35:19 +02:00
import { OsmConnection } from "./Logic/OsmConnection" ;
import { Changes } from "./Logic/Changes" ;
import { ElementStorage } from "./Logic/ElementStorage" ;
import { UIEventSource } from "./UI/UIEventSource" ;
import { UserBadge } from "./UI/UserBadge" ;
2020-07-22 15:44:47 +02:00
import { Basemap , BaseLayers } from "./Logic/Basemap" ;
2020-06-24 00:35:19 +02:00
import { PendingChanges } from "./UI/PendingChanges" ;
import { CenterMessageBox } from "./UI/CenterMessageBox" ;
import { Helpers } from "./Helpers" ;
2020-06-29 03:12:44 +02:00
import { Tag , TagUtils } from "./Logic/TagsFilter" ;
2020-06-24 00:35:19 +02:00
import { FilteredLayer } from "./Logic/FilteredLayer" ;
import { LayerUpdater } from "./Logic/LayerUpdater" ;
2020-06-27 03:06:51 +02:00
import { UIElement } from "./UI/UIElement" ;
2020-07-21 02:55:28 +02:00
import { FullScreenMessageBoxHandler } from "./UI/FullScreenMessageBoxHandler" ;
2020-06-27 03:06:51 +02:00
import { FeatureInfoBox } from "./UI/FeatureInfoBox" ;
2020-06-28 02:42:22 +02:00
import { GeoLocationHandler } from "./Logic/GeoLocationHandler" ;
2020-06-29 03:12:44 +02:00
import { StrayClickHandler } from "./Logic/StrayClickHandler" ;
import { SimpleAddUI } from "./UI/SimpleAddUI" ;
import { VariableUiElement } from "./UI/Base/VariableUIElement" ;
2020-07-01 02:12:33 +02:00
import { SearchAndGo } from "./UI/SearchAndGo" ;
2020-07-05 18:59:47 +02:00
import { AllKnownLayouts } from "./Customizations/AllKnownLayouts" ;
2020-07-24 01:12:57 +02:00
import { CheckBox } from "./UI/Input/CheckBox" ;
2020-07-20 12:39:43 +02:00
import Translations from "./UI/i18n/Translations" ;
import Locale from "./UI/i18n/Locale" ;
2020-07-21 02:55:28 +02:00
import { Layout , WelcomeMessage } from "./Customizations/Layout" ;
2020-07-21 00:07:04 +02:00
import { DropDown } from "./UI/Input/DropDown" ;
import { FixedUiElement } from "./UI/Base/FixedUiElement" ;
2020-07-22 15:17:29 +02:00
import { LayerSelection } from "./UI/LayerSelection" ;
2020-07-22 14:57:35 +02:00
import Combine from "./UI/Base/Combine" ;
2020-07-22 17:54:59 +02:00
import { Img } from "./UI/Img" ;
2020-07-22 23:47:04 +02:00
import { QueryParameters } from "./Logic/QueryParameters" ;
2020-07-24 01:12:57 +02:00
import { Utils } from "./Utils" ;
2020-06-24 00:35:19 +02:00
2020-07-11 11:50:03 +02:00
2020-07-24 01:12:57 +02:00
// --------------------- Special actions based on the parameters -----------------
2020-07-11 11:50:03 +02:00
2020-07-12 23:19:05 +02:00
// @ts-ignore
2020-07-15 14:03:44 +02:00
if ( location . href . startsWith ( "http://buurtnatuur.be" ) ) {
2020-07-11 11:50:03 +02:00
// Reload the https version. This is important for the 'locate me' button
2020-07-15 14:03:44 +02:00
window . location . replace ( "https://buurtnatuur.be" ) ;
2020-07-11 11:50:03 +02:00
}
2020-06-25 03:39:31 +02:00
if ( location . hostname === "localhost" || location . hostname === "127.0.0.1" ) {
// Set to true if testing and changes should NOT be saved
2020-07-24 01:12:57 +02:00
const testing = QueryParameters . GetQueryParameter ( "test" ) ;
2020-07-24 13:46:03 +02:00
testing . setData ( testing . data ? ? "true" )
2020-06-25 03:39:31 +02:00
// If you have a testfile somewhere, enable this to spoof overpass
// This should be hosted independantly, e.g. with `cd assets; webfsd -p 8080` + a CORS plugin to disable cors rules
2020-07-17 18:57:07 +02:00
//Overpass.testUrl = "http://127.0.0.1:8080/streetwidths.geojson";
2020-06-25 03:39:31 +02:00
}
2020-06-24 00:35:19 +02:00
// ----------------- SELECT THE RIGHT QUESTSET -----------------
2020-07-20 16:57:46 +02:00
let defaultLayout = "walkbybrussels"
2020-07-13 16:18:04 +02:00
2020-07-15 13:15:36 +02:00
// Run over all questsets. If a part of the URL matches a searched-for part in the layout, it'll take that as the default
2020-07-13 16:18:04 +02:00
for ( const k in AllKnownLayouts . allSets ) {
const layout = AllKnownLayouts . allSets [ k ] ;
const possibleParts = layout . locationContains ? ? [ ] ;
for ( const locationMatch of possibleParts ) {
2020-07-15 13:15:36 +02:00
if ( locationMatch === "" ) {
2020-07-13 16:18:04 +02:00
continue
}
2020-07-15 13:15:36 +02:00
if ( window . location . href . toLowerCase ( ) . indexOf ( locationMatch . toLowerCase ( ) ) >= 0 ) {
defaultLayout = layout . name ;
2020-07-13 16:18:04 +02:00
}
}
}
2020-07-22 23:47:04 +02:00
defaultLayout = QueryParameters . GetQueryParameter ( "layout" ) . data ? ? defaultLayout ;
2020-07-05 18:59:47 +02:00
2020-07-24 01:12:57 +02:00
const layoutToUse : Layout = AllKnownLayouts . allSets [ defaultLayout ] ? ? AllKnownLayouts [ "all" ] ;
2020-07-15 13:15:36 +02:00
console . log ( "Using layout: " , layoutToUse . name ) ;
2020-06-24 00:35:19 +02:00
// ----------------- Setup a few event sources -------------
// The message that should be shown at the center of the screen
const centerMessage = new UIEventSource < string > ( "" ) ;
// The countdown: if set to e.g. ten, it'll start counting down. When reaching zero, changes will be saved. NB: this is implemented later, not in the eventSource
const secondsTillChangesAreSaved = new UIEventSource < number > ( 0 ) ;
2020-07-21 00:07:04 +02:00
// const leftMessage = new UIEventSource<() => UIElement>(undefined);
// This message is shown full screen on mobile devices
const fullScreenMessage = new UIEventSource < UIElement > ( undefined ) ;
2020-06-27 03:06:51 +02:00
2020-07-22 01:07:32 +02:00
// The latest element that was selected - used to generate the right UI at the right place
2020-07-22 23:47:04 +02:00
const selectedElement = new UIEventSource < { feature : any } > ( undefined ) ;
2020-06-27 03:06:51 +02:00
2020-07-22 23:47:04 +02:00
const zoom = QueryParameters . GetQueryParameter ( "z" ) ;
const lat = QueryParameters . GetQueryParameter ( "lat" ) ;
const lon = QueryParameters . GetQueryParameter ( "lon" ) ;
2020-07-24 01:12:57 +02:00
2020-06-29 03:12:44 +02:00
const locationControl = new UIEventSource < { lat : number , lon : number , zoom : number } > ( {
2020-07-24 01:12:57 +02:00
zoom : Utils.asFloat ( zoom . data ) ? ? layoutToUse . startzoom ,
lat : Utils.asFloat ( lat . data ) ? ? layoutToUse . startLat ,
lon : Utils.asFloat ( lon . data ) ? ? layoutToUse . startLon
2020-06-24 00:35:19 +02:00
} ) ;
2020-07-22 23:47:04 +02:00
locationControl . addCallback ( ( latlonz ) = > {
zoom . setData ( latlonz . zoom . toString ( ) ) ;
lat . setData ( latlonz . lat . toString ( ) . substr ( 0 , 6 ) ) ;
lon . setData ( latlonz . lon . toString ( ) . substr ( 0 , 6 ) ) ;
} )
2020-06-24 00:35:19 +02:00
// ----------------- Prepare the important objects -----------------
2020-07-24 01:12:57 +02:00
const osmConnection = new OsmConnection (
QueryParameters . GetQueryParameter ( "test" ) . data === "true"
) ;
2020-07-21 00:07:04 +02:00
Locale . language . syncWith ( osmConnection . GetPreference ( "language" ) ) ;
2020-07-21 02:55:28 +02:00
// @ts-ignore
2020-07-21 00:07:04 +02:00
window . setLanguage = function ( language : string ) {
Locale . language . setData ( language )
}
2020-07-05 18:59:47 +02:00
const saveTimeout = 30000 ; // After this many milliseconds without changes, saves are sent of to OSM
2020-06-24 00:35:19 +02:00
const allElements = new ElementStorage ( ) ;
2020-06-27 03:06:51 +02:00
const changes = new Changes (
2020-07-15 13:15:36 +02:00
"Beantwoorden van vragen met #MapComplete voor vragenset #" + layoutToUse . name ,
2020-07-05 18:59:47 +02:00
osmConnection , allElements ) ;
2020-06-29 03:12:44 +02:00
const bm = new Basemap ( "leafletDiv" , locationControl , new VariableUiElement (
locationControl . map ( ( location ) = > {
const mapComplete = "<a href='https://github.com/pietervdvn/MapComplete' target='_blank'>Mapcomple</a> " +
" " +
"<a href='https://github.com/pietervdvn/MapComplete/issues' target='_blank'><img src='./assets/bug.svg' alt='Report bug' class='small-userbadge-icon'></a>" ;
let editHere = "" ;
if ( location !== undefined ) {
editHere = " | " +
"<a href='https://www.openstreetmap.org/edit?editor=id#map=" + location . zoom + "/" + location . lat + "/" + location . lon + "' target='_blank'>" +
"<img src='./assets/pencil.svg' alt='edit here' class='small-userbadge-icon'>" +
"</a>"
}
return mapComplete + editHere ;
} )
) ) ;
2020-06-24 00:35:19 +02:00
// ------------- Setup the layers -------------------------------
const addButtons : {
2020-07-22 00:50:30 +02:00
name : UIElement ,
2020-06-24 00:35:19 +02:00
icon : string ,
tags : Tag [ ] ,
layerToAddTo : FilteredLayer
} [ ]
= [ ] ;
const flayers : FilteredLayer [ ] = [ ]
2020-07-01 21:21:29 +02:00
let minZoom = 0 ;
2020-07-15 13:15:36 +02:00
for ( const layer of layoutToUse . layers ) {
2020-06-24 00:35:19 +02:00
2020-07-22 01:07:32 +02:00
const generateInfo = ( tagsES , feature ) = > {
2020-07-01 16:32:17 +02:00
return new FeatureInfoBox (
2020-07-22 01:07:32 +02:00
feature ,
2020-07-01 16:32:17 +02:00
tagsES ,
2020-07-05 18:59:47 +02:00
layer . title ,
2020-07-01 16:32:17 +02:00
layer . elementsToShow ,
changes ,
2020-07-14 20:18:44 +02:00
osmConnection . userDetails
2020-07-01 16:32:17 +02:00
)
} ;
2020-07-01 21:21:29 +02:00
minZoom = Math . max ( minZoom , layer . minzoom ) ;
2020-07-15 13:15:36 +02:00
const flayer = layer . asLayer ( bm , allElements , changes , osmConnection . userDetails , selectedElement , generateInfo ) ;
2020-06-24 00:35:19 +02:00
const addButton = {
2020-07-22 00:50:30 +02:00
name : Translations.W ( layer . name ) ,
2020-06-24 00:35:19 +02:00
icon : layer.icon ,
tags : layer.newElementTags ,
layerToAddTo : flayer
}
addButtons . push ( addButton ) ;
flayers . push ( flayer ) ;
}
2020-07-01 21:21:29 +02:00
const layerUpdater = new LayerUpdater ( bm , minZoom , flayers ) ;
2020-06-24 00:35:19 +02:00
2020-07-24 01:12:57 +02:00
// --------------- Setting up layer selection ui --------
2020-07-22 17:54:59 +02:00
const closedFilterButton = ` <button id="filter__button" class="filter__button filter__button--shadow"> ${ Img . closedFilterButton } </button> ` ;
const openFilterButton = `
< button id = "filter__button" class = "filter__button" > $ { Img . openFilterButton } < / button > ` ;
2020-07-24 14:46:25 +02:00
let baseLayerOptions = BaseLayers . baseLayers . map ( ( layer ) = > { return { value : layer , shown : layer.name } } ) ;
2020-07-23 16:28:19 +02:00
const backgroundMapPicker = new Combine ( [ new DropDown ( ` Background map ` , baseLayerOptions , bm . CurrentLayer ) , openFilterButton ] ) ;
const layerSelection = new Combine ( [ ` <p class="filter__label">Maplayers</p> ` , new LayerSelection ( flayers ) ] ) ;
let layerControl = backgroundMapPicker ;
2020-07-22 17:54:59 +02:00
if ( flayers . length > 1 ) {
2020-07-24 01:12:57 +02:00
layerControl = new Combine ( [ layerSelection , backgroundMapPicker ] ) ;
2020-07-22 17:54:59 +02:00
}
2020-07-23 16:28:19 +02:00
new CheckBox ( layerControl , closedFilterButton ) . AttachTo ( "filter__selection" ) ;
2020-06-24 00:35:19 +02:00
2020-07-23 16:00:49 +02:00
2020-07-24 01:12:57 +02:00
// ------------------ Setup various other UI elements ------------
document . title = layoutToUse . title . InnerRender ( ) ;
Locale . language . addCallback ( e = > {
document . title = layoutToUse . title . InnerRender ( ) ;
} )
let languagePicker = new DropDown ( "" , layoutToUse . supportedLanguages . map ( lang = > {
return { value : lang , shown : lang }
}
) , Locale . language ) ;
2020-07-21 00:07:04 +02:00
2020-06-29 03:12:44 +02:00
2020-07-21 00:07:04 +02:00
new StrayClickHandler ( bm , selectedElement , fullScreenMessage , ( ) = > {
2020-07-24 01:12:57 +02:00
return new SimpleAddUI ( bm . Location ,
bm . LastClickLocation ,
changes ,
selectedElement ,
layerUpdater . runningQuery ,
osmConnection . userDetails ,
addButtons ) ;
2020-06-29 03:12:44 +02:00
}
) ;
2020-06-27 03:06:51 +02:00
/ * *
2020-07-24 01:12:57 +02:00
* Show the questions and information for the selected element
* This is given to the div which renders fullscreen on mobile devices
2020-06-27 03:06:51 +02:00
* /
2020-07-22 01:07:32 +02:00
selectedElement . addCallback ( ( feature ) = > {
const data = feature . feature . properties ;
2020-06-29 03:12:44 +02:00
// Which is the applicable set?
2020-07-15 13:15:36 +02:00
for ( const layer of layoutToUse . layers ) {
2020-06-29 03:12:44 +02:00
const applicable = layer . overpassFilter . matches ( TagUtils . proprtiesToKV ( data ) ) ;
if ( applicable ) {
// This layer is the layer that gives the questions
2020-07-21 00:07:04 +02:00
const featureBox = new FeatureInfoBox (
2020-07-22 01:07:32 +02:00
feature . feature ,
2020-07-21 00:07:04 +02:00
allElements . getElement ( data . id ) ,
layer . title ,
layer . elementsToShow ,
changes ,
osmConnection . userDetails
) ;
fullScreenMessage . setData ( featureBox ) ;
2020-06-29 03:12:44 +02:00
break ;
2020-06-27 03:06:51 +02:00
}
2020-06-29 03:12:44 +02:00
}
2020-06-27 03:06:51 +02:00
}
) ;
2020-06-24 00:35:19 +02:00
2020-06-29 03:12:44 +02:00
2020-07-24 01:12:57 +02:00
const pendingChanges = new PendingChanges ( changes , secondsTillChangesAreSaved , ) ;
2020-07-23 16:00:49 +02:00
2020-07-21 00:07:04 +02:00
new UserBadge ( osmConnection . userDetails ,
pendingChanges ,
2020-07-23 16:00:49 +02:00
languagePicker ,
2020-07-21 00:07:04 +02:00
bm )
2020-06-24 00:35:19 +02:00
. AttachTo ( 'userbadge' ) ;
2020-07-01 02:12:33 +02:00
new SearchAndGo ( bm ) . AttachTo ( "searchbox" ) ;
2020-07-24 15:52:21 +02:00
const welcome = new WelcomeMessage ( layoutToUse , osmConnection ) . onClick ( ( ) = > { } ) ;
2020-07-24 01:12:57 +02:00
2020-07-24 14:46:25 +02:00
const help = new FixedUiElement ( ` <div class='collapse-button-img'><img src='assets/help.svg' alt='help'></div> ` ) ;
const close = new FixedUiElement ( ` <div class='collapse-button-img'><img src='assets/close.svg' alt='close'></div> ` ) ;
2020-07-24 01:12:57 +02:00
new CheckBox (
new Combine ( [
2020-07-24 14:46:25 +02:00
new Combine ( [ "<span class='collapse-button'>" , close , "</span>" ] ) ,
2020-07-24 01:12:57 +02:00
welcome ] ) ,
new Combine ( [ "<span class='open-button'>" , help , "</span>" ] )
, true
) . AttachTo ( "messagesbox" )
2020-06-24 00:35:19 +02:00
2020-07-21 02:55:28 +02:00
new FullScreenMessageBoxHandler ( fullScreenMessage , ( ) = > {
2020-07-16 14:56:19 +02:00
selectedElement . setData ( undefined )
2020-07-21 02:55:28 +02:00
} ) . update ( ) ;
2020-07-24 15:52:21 +02:00
fullScreenMessage . setData ( new WelcomeMessage ( layoutToUse , osmConnection ) )
2020-07-21 02:55:28 +02:00
2020-06-24 00:35:19 +02:00
new CenterMessageBox (
2020-07-01 21:21:29 +02:00
minZoom ,
2020-06-24 00:35:19 +02:00
centerMessage ,
osmConnection ,
locationControl ,
layerUpdater . runningQuery )
. AttachTo ( "centermessage" ) ;
2020-06-27 03:06:51 +02:00
Helpers . SetupAutoSave ( changes , secondsTillChangesAreSaved , saveTimeout ) ;
2020-06-24 00:35:19 +02:00
Helpers . LastEffortSave ( changes ) ;
2020-06-27 03:06:51 +02:00
osmConnection . registerActivateOsmAUthenticationClass ( ) ;
2020-06-24 00:35:19 +02:00
2020-06-28 02:42:22 +02:00
new GeoLocationHandler ( bm ) . AttachTo ( "geolocate-button" ) ;
2020-07-24 15:52:21 +02:00
locationControl . ping ( )
2020-07-22 15:44:47 +02:00