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-07-24 17:53:09 +02:00
import { LocalStorageSource } from "./Logic/LocalStorageSource" ;
2020-07-25 18:00:08 +02:00
import { Button } from "./UI/Base/Button" ;
2020-07-29 15:05:19 +02:00
import { TabbedComponent } from "./UI/Base/TabbedComponent" ;
import { ShareScreen } from "./UI/ShareScreen" ;
import { InitUiElements } from "./InitUiElements" ;
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-29 15:05:19 +02:00
const testing = QueryParameters . GetQueryParameter ( "test" , "true" ) ;
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-29 15:05:19 +02:00
let defaultLayout = "bookcases"
2020-07-13 16:18:04 +02:00
2020-07-26 02:01:34 +02:00
const path = window . location . pathname . split ( "/" ) . slice ( - 1 ) [ 0 ] ;
if ( path !== "index.html" ) {
defaultLayout = path . substr ( 0 , path . length - 5 ) ;
console . log ( "Using" , defaultLayout )
}
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-29 15:05:19 +02:00
defaultLayout = QueryParameters . GetQueryParameter ( "layout" , defaultLayout ) . data ;
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-07-26 02:01:34 +02:00
if ( layoutToUse === undefined ) {
console . log ( "Incorrect layout" )
}
2020-07-15 13:15:36 +02:00
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-29 19:02:36 +02:00
const zoom = QueryParameters . GetQueryParameter ( "z" , undefined )
2020-07-24 17:53:09 +02:00
. syncWith ( LocalStorageSource . Get ( "zoom" ) ) ;
2020-07-29 19:02:36 +02:00
const lat = QueryParameters . GetQueryParameter ( "lat" , undefined )
2020-07-24 17:53:09 +02:00
. syncWith ( LocalStorageSource . Get ( "lat" ) ) ;
2020-07-29 19:02:36 +02:00
const lon = QueryParameters . GetQueryParameter ( "lon" , undefined )
2020-07-24 17:53:09 +02:00
. syncWith ( LocalStorageSource . Get ( "lon" ) ) ;
2020-07-22 23:47:04 +02:00
2020-07-29 16:05:02 +02:00
const featureSwitchUserbadge = QueryParameters . GetQueryParameter ( "fs-userbadge" , "" + layoutToUse . enableUserBadge ) ;
const featureSwitchSearch = QueryParameters . GetQueryParameter ( "fs-search" , "" + layoutToUse . enableSearch ) ;
2020-07-29 15:05:19 +02:00
const featureSwitchWelcomeMessage = QueryParameters . GetQueryParameter ( "fs-welcome-message" , "true" ) ;
2020-07-29 16:05:02 +02:00
const featureSwitchLayers = QueryParameters . GetQueryParameter ( "fs-layers" , "" + layoutToUse . enableLayers ) ;
const featureSwitchAddNew = QueryParameters . GetQueryParameter ( "fs-add-new" , "" + layoutToUse . enableAdd ) ;
2020-07-29 15:05:19 +02:00
const featureSwitchIframe = QueryParameters . GetQueryParameter ( "fs-iframe" , "false" ) ;
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 (
2020-07-29 15:05:19 +02:00
QueryParameters . GetQueryParameter ( "test" , "false" ) . data === "true"
2020-07-24 01:12:57 +02:00
) ;
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-24 17:34:15 +02:00
Locale . language . addCallback ( ( currentLanguage ) = > {
console . log ( "REsetting languate to" , layoutToUse . supportedLanguages [ 0 ] )
if ( layoutToUse . supportedLanguages . indexOf ( currentLanguage ) < 0 ) {
// The current language is not supported -> switch to a supported one
Locale . language . setData ( layoutToUse . supportedLanguages [ 0 ] ) ;
}
} ) . ping ( )
2020-07-21 00:07:04 +02:00
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
2020-07-29 15:05:19 +02:00
2020-06-24 00:35:19 +02:00
// ------------- Setup the layers -------------------------------
const addButtons : {
2020-07-29 18:35:46 +02:00
name : string | UIElement ,
description : string | 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
2020-07-26 02:01:34 +02:00
const flayer = FilteredLayer . fromDefinition ( layer , bm , allElements , changes , osmConnection . userDetails , selectedElement , generateInfo ) ;
2020-06-24 00:35:19 +02:00
2020-07-29 18:35:46 +02:00
for ( const preset of layer . presets ) {
2020-07-29 19:43:15 +02:00
if ( preset . icon === undefined ) {
const tags = { } ;
for ( const tag of preset . tags ) {
const k = tag . key ;
if ( typeof ( k ) === "string" ) {
tags [ k ] = tag . value ;
}
}
preset . icon = layer . style ( tags ) . icon . iconUrl ;
}
2020-07-29 18:35:46 +02:00
const addButton = {
name : preset.title ,
description : preset.description ,
icon : preset.icon ,
tags : preset.tags ,
layerToAddTo : flayer
}
addButtons . push ( addButton ) ;
2020-06-24 00:35:19 +02:00
}
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
2020-07-25 18:00:08 +02:00
const closedFilterButton = ` <button id="filter__button" class="filter__button shadow"> ${ Img . closedFilterButton } </button> ` ;
2020-07-22 17:54:59 +02:00
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-29 15:05:19 +02:00
InitUiElements . OnlyIf ( featureSwitchLayers , ( ) = > {
2020-07-29 18:35:46 +02:00
const checkbox = new CheckBox ( layerControl , closedFilterButton ) ;
checkbox . AttachTo ( "filter__selection" ) ;
bm . Location . addCallback ( ( ) = > {
checkbox . isEnabled . setData ( false ) ;
} ) ;
2020-07-29 15:05:19 +02:00
} ) ;
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 ------------
2020-07-27 11:45:26 +02:00
document . title = Translations . W ( layoutToUse . title ) . InnerRender ( ) ;
2020-07-24 01:12:57 +02:00
Locale . language . addCallback ( e = > {
2020-07-27 11:45:26 +02:00
document . title = Translations . W ( layoutToUse . title ) . InnerRender ( ) ;
2020-07-24 01:12:57 +02:00
} )
2020-07-21 00:07:04 +02:00
2020-06-29 03:12:44 +02:00
2020-07-29 15:05:19 +02:00
InitUiElements . OnlyIf ( featureSwitchAddNew , ( ) = > {
new StrayClickHandler ( bm , selectedElement , fullScreenMessage , ( ) = > {
return new SimpleAddUI ( bm . Location ,
bm . LastClickLocation ,
changes ,
selectedElement ,
layerUpdater . runningQuery ,
osmConnection . userDetails ,
addButtons ) ;
}
) ;
} ) ;
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 ) = > {
2020-07-29 19:43:15 +02:00
if ( feature ? . feature ? . properties === undefined ) {
return ;
}
2020-07-22 01:07:32 +02:00
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-29 15:05:19 +02:00
InitUiElements . OnlyIf ( featureSwitchUserbadge , ( ) = > {
2020-06-24 00:35:19 +02:00
2020-07-29 15:05:19 +02:00
new UserBadge ( osmConnection . userDetails ,
pendingChanges ,
Locale . CreateLanguagePicker ( layoutToUse ) ,
bm )
. AttachTo ( 'userbadge' ) ;
2020-07-25 18:00:08 +02:00
} ) ;
2020-07-24 01:12:57 +02:00
2020-07-29 15:05:19 +02:00
InitUiElements . OnlyIf ( ( featureSwitchSearch ) , ( ) = > {
new SearchAndGo ( bm ) . AttachTo ( "searchbox" ) ;
} ) ;
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
2020-07-29 15:05:19 +02:00
InitUiElements . OnlyIf ( featureSwitchWelcomeMessage , ( ) = > {
InitUiElements . InitWelcomeMessage ( layoutToUse , osmConnection , bm , fullScreenMessage )
} ) ;
2020-07-29 16:46:45 +02:00
if ( ( window != window . top && featureSwitchWelcomeMessage . data === "false" ) || featureSwitchIframe . data !== "false" ) {
console . log ( "WELCOME? " , featureSwitchWelcomeMessage . data )
new FixedUiElement ( ` <a href=' ${ window . location } ' target='_blank'><span class='iframe-escape'><img src='assets/pop-out.svg'></span></a> ` ) . AttachTo ( "top-right" )
2020-07-29 15:05:19 +02:00
}
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