From 5aa620440abba3a14cc85e5702add2252368f148 Mon Sep 17 00:00:00 2001 From: Stanislas Gueniffey Date: Wed, 15 Jul 2020 14:03:44 +0200 Subject: [PATCH 1/2] Add drinking water layer --- Customizations/AllKnownLayouts.ts | 20 +++--- Customizations/Layers/DrinkingWater.ts | 61 ++++++++++++++++++ Customizations/Layouts/DrinkingWater.ts | 26 ++++++++ index.ts | 86 ++++++++++++------------- 4 files changed, 141 insertions(+), 52 deletions(-) create mode 100644 Customizations/Layers/DrinkingWater.ts create mode 100644 Customizations/Layouts/DrinkingWater.ts diff --git a/Customizations/AllKnownLayouts.ts b/Customizations/AllKnownLayouts.ts index 9a8728e84..dbd0a3666 100644 --- a/Customizations/AllKnownLayouts.ts +++ b/Customizations/AllKnownLayouts.ts @@ -1,22 +1,24 @@ -import {Groen} from "./Layouts/Groen"; -import {Toilets} from "./Layouts/Toilets"; -import {GRB} from "./Layouts/GRB"; -import {Statues} from "./Layouts/Statues"; -import {Bookcases} from "./Layouts/Bookcases"; +import { Groen } from "./Layouts/Groen"; +import { Toilets } from "./Layouts/Toilets"; +import { GRB } from "./Layouts/GRB"; +import { Statues } from "./Layouts/Statues"; +import { Bookcases } from "./Layouts/Bookcases"; import Cyclofix from "./Layouts/Cyclofix"; -import {All} from "./Layouts/All"; -import {Layout} from "./Layout"; +import { DrinkingWater } from "./Layouts/DrinkingWater"; +import { All } from "./Layouts/All"; +import { Layout } from "./Layout"; export class AllKnownLayouts { public static allSets: any = AllKnownLayouts.AllLayouts(); - private static AllLayouts() : any{ + private static AllLayouts(): any { const all = new All(); - const layouts : Layout[] = [ + const layouts: Layout[] = [ new Groen(), new GRB(), new Cyclofix(), new Bookcases(), + new DrinkingWater(), all /*new Toilets(), new Statues(), diff --git a/Customizations/Layers/DrinkingWater.ts b/Customizations/Layers/DrinkingWater.ts new file mode 100644 index 000000000..56984a39b --- /dev/null +++ b/Customizations/Layers/DrinkingWater.ts @@ -0,0 +1,61 @@ +import { LayerDefinition } from "../LayerDefinition"; +import { And, Or, Tag } from "../../Logic/TagsFilter"; +import { OperatorTag } from "../Questions/OperatorTag"; +import * as L from "leaflet"; +import FixedText from "../Questions/FixedText"; +import { BikeParkingType } from "../Questions/BikeParkingType"; +import { TagRenderingOptions } from "../TagRendering"; +import { ImageCarouselWithUploadConstructor } from "../../UI/Image/ImageCarouselWithUpload"; + +export class DrinkingWaterLayer extends LayerDefinition { + + constructor() { + super(); + this.name = "drinking_water"; + this.icon = "./assets/bug.svg"; + + this.overpassFilter = new Or([ + new And([ + new Tag("amenity", "drinking_water") + ]) + ]); + + + this.newElementTags = [ + new Tag("amenity", "drinking_water"), + ]; + this.maxAllowedOverlapPercentage = 10; + + this.minzoom = 13; + this.style = this.generateStyleFunction(); + this.title = new FixedText("Drinking water"); + this.elementsToShow = [ + new OperatorTag(), + new BikeParkingType() + ]; + this.elementsToShow = [new ImageCarouselWithUploadConstructor(), new TagRenderingOptions({ + question: "How easy is it to fill water bottles?", + mappings: [ + { k: new Tag("bottle", "yes"), txt: "It is easy to refill water bottles" }, + { k: new Tag("bottle", "no"), txt: "Water bottles may not fit" } + ], + })]; + + } + + + private generateStyleFunction() { + const self = this; + return function (properties: any) { + + return { + color: "#00bb00", + icon: new L.icon({ + iconUrl: self.icon, + iconSize: [40, 40] + }) + }; + }; + } + +} \ No newline at end of file diff --git a/Customizations/Layouts/DrinkingWater.ts b/Customizations/Layouts/DrinkingWater.ts new file mode 100644 index 000000000..41360dde1 --- /dev/null +++ b/Customizations/Layouts/DrinkingWater.ts @@ -0,0 +1,26 @@ +import { Layout } from "../Layout"; +import { DrinkingWaterLayer } from "../Layers/DrinkingWater"; + +export class DrinkingWater extends Layout { + constructor() { + super("drinkingwater", + "Drinking Water Spots", + [new DrinkingWaterLayer()], + 10, + 50.8435, + 4.3688, + + + "

Drinking water

\n" + + "\n" + + "

" + + "Help with creating a map of drinking water points!" + + , + "

Start by creating an account\n" + + " or by " + + " logging in.

", + "Start by clicking a pin and answering the questions"); + } + +} \ No newline at end of file diff --git a/index.ts b/index.ts index 73b8cea11..d8190aa9c 100644 --- a/index.ts +++ b/index.ts @@ -1,27 +1,27 @@ -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"; -import {Basemap} from "./Logic/Basemap"; -import {PendingChanges} from "./UI/PendingChanges"; -import {CenterMessageBox} from "./UI/CenterMessageBox"; -import {Helpers} from "./Helpers"; -import {Tag, TagUtils} from "./Logic/TagsFilter"; -import {FilteredLayer} from "./Logic/FilteredLayer"; -import {LayerUpdater} from "./Logic/LayerUpdater"; -import {UIElement} from "./UI/UIElement"; -import {MessageBoxHandler} from "./UI/MessageBoxHandler"; -import {Overpass} from "./Logic/Overpass"; -import {FeatureInfoBox} from "./UI/FeatureInfoBox"; -import {GeoLocationHandler} from "./Logic/GeoLocationHandler"; -import {StrayClickHandler} from "./Logic/StrayClickHandler"; -import {SimpleAddUI} from "./UI/SimpleAddUI"; -import {VariableUiElement} from "./UI/Base/VariableUIElement"; -import {SearchAndGo} from "./UI/SearchAndGo"; -import {CollapseButton} from "./UI/Base/CollapseButton"; -import {AllKnownLayouts} from "./Customizations/AllKnownLayouts"; -import {All} from "./Customizations/Layouts/All"; +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"; +import { Basemap } from "./Logic/Basemap"; +import { PendingChanges } from "./UI/PendingChanges"; +import { CenterMessageBox } from "./UI/CenterMessageBox"; +import { Helpers } from "./Helpers"; +import { Tag, TagUtils } from "./Logic/TagsFilter"; +import { FilteredLayer } from "./Logic/FilteredLayer"; +import { LayerUpdater } from "./Logic/LayerUpdater"; +import { UIElement } from "./UI/UIElement"; +import { MessageBoxHandler } from "./UI/MessageBoxHandler"; +import { Overpass } from "./Logic/Overpass"; +import { FeatureInfoBox } from "./UI/FeatureInfoBox"; +import { GeoLocationHandler } from "./Logic/GeoLocationHandler"; +import { StrayClickHandler } from "./Logic/StrayClickHandler"; +import { SimpleAddUI } from "./UI/SimpleAddUI"; +import { VariableUiElement } from "./UI/Base/VariableUIElement"; +import { SearchAndGo } from "./UI/SearchAndGo"; +import { CollapseButton } from "./UI/Base/CollapseButton"; +import { AllKnownLayouts } from "./Customizations/AllKnownLayouts"; +import { All } from "./Customizations/Layouts/All"; @@ -29,9 +29,9 @@ import {All} from "./Customizations/Layouts/All"; // --------------------- Read the URL parameters ----------------- // @ts-ignore -if(location.href.startsWith("http://buurtnatuur.be")){ +if (location.href.startsWith("http://buurtnatuur.be")) { // Reload the https version. This is important for the 'locate me' button - window.location.replace("https://buurtnatuur.be"); + window.location.replace("https://buurtnatuur.be"); } @@ -40,7 +40,7 @@ let dryRun = false; if (location.hostname === "localhost" || location.hostname === "127.0.0.1") { // Set to true if testing and changes should NOT be saved - // dryRun = true; + dryRun = true; // 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 Overpass.testUrl = null; // "http://127.0.0.1:8080/test.json"; @@ -57,11 +57,11 @@ for (const k in AllKnownLayouts.allSets) { const possibleParts = layout.locationContains ?? []; console.log(layout.locationContains) for (const locationMatch of possibleParts) { - if(locationMatch === ""){ + if (locationMatch === "") { continue } - console.log(layout.name," -> ", locationMatch, window.location.href.indexOf(locationMatch)) - if(window.location.href.toLowerCase().indexOf(locationMatch.toLowerCase()) >= 0){ + console.log(layout.name, " -> ", locationMatch, window.location.href.indexOf(locationMatch)) + if (window.location.href.toLowerCase().indexOf(locationMatch.toLowerCase()) >= 0) { defaultQuest = layout.name; console.log("Detected a default by URL: ", layout.name, "matches", locationMatch) } @@ -78,7 +78,7 @@ if (window.location.search) { if (paramDict.quests) { defaultQuest = paramDict.quests } - if(paramDict.test){ + if (paramDict.test) { dryRun = true; } } @@ -164,7 +164,7 @@ for (const layer of questSetToRender.layers) { }; minZoom = Math.max(minZoom, layer.minzoom); - + const flayer = layer.asLayer(bm, allElements, changes, osmConnection.userDetails, selectedElement, generateInfo); @@ -185,14 +185,14 @@ const layerUpdater = new LayerUpdater(bm, minZoom, flayers); new StrayClickHandler(bm, selectedElement, leftMessage, () => { - return new SimpleAddUI(bm.Location, - bm.LastClickLocation, - changes, - selectedElement, - layerUpdater.runningQuery, - osmConnection.userDetails, - addButtons); - } + return new SimpleAddUI(bm.Location, + bm.LastClickLocation, + changes, + selectedElement, + layerUpdater.runningQuery, + osmConnection.userDetails, + addButtons); +} ); /** @@ -216,7 +216,7 @@ selectedElement.addCallback((data) => { break; } } - } +} ); @@ -239,7 +239,7 @@ var welcomeMessage = () => { login = questSetToRender.welcomeBackMessage; } return "
" + - questSetToRender.welcomeMessage + login + questSetToRender.welcomeTail+ + questSetToRender.welcomeMessage + login + questSetToRender.welcomeTail + "
"; }), function () { @@ -250,7 +250,7 @@ leftMessage.setData(welcomeMessage); welcomeMessage().AttachTo("messagesbox"); -var messageBox = new MessageBoxHandler(leftMessage, () => {selectedElement.setData(undefined)}); +var messageBox = new MessageBoxHandler(leftMessage, () => { selectedElement.setData(undefined) }); new CenterMessageBox( minZoom, From 45351d9dd1a59512ec09d310846dc0a669ad39a3 Mon Sep 17 00:00:00 2001 From: Stanislas Gueniffey Date: Wed, 15 Jul 2020 15:55:08 +0200 Subject: [PATCH 2/2] Add control on FilteredLayer to show/hide layers --- Customizations/AllKnownLayouts.ts | 13 ++++++-- .../{DrinkingWater.ts => WalkByBrussels.ts} | 9 ++++-- Logic/FilteredLayer.ts | 31 +++++++++++++------ index.ts | 6 ++-- 4 files changed, 43 insertions(+), 16 deletions(-) rename Customizations/Layouts/{DrinkingWater.ts => WalkByBrussels.ts} (69%) diff --git a/Customizations/AllKnownLayouts.ts b/Customizations/AllKnownLayouts.ts index dbd0a3666..7f1c5588c 100644 --- a/Customizations/AllKnownLayouts.ts +++ b/Customizations/AllKnownLayouts.ts @@ -4,7 +4,7 @@ import { GRB } from "./Layouts/GRB"; import { Statues } from "./Layouts/Statues"; import { Bookcases } from "./Layouts/Bookcases"; import Cyclofix from "./Layouts/Cyclofix"; -import { DrinkingWater } from "./Layouts/DrinkingWater"; +import { WalkByBrussels } from "./Layouts/WalkByBrussels"; import { All } from "./Layouts/All"; import { Layout } from "./Layout"; @@ -18,7 +18,7 @@ export class AllKnownLayouts { new GRB(), new Cyclofix(), new Bookcases(), - new DrinkingWater(), + new WalkByBrussels(), all /*new Toilets(), new Statues(), @@ -31,4 +31,13 @@ export class AllKnownLayouts { } return allSets; } + + public static GetSets(layoutNames): any { + const all = new All(); + for (const name of layoutNames) { + all.layers = all.layers.concat(AllKnownLayouts.allSets[name].layers); + } + + return all; + } } diff --git a/Customizations/Layouts/DrinkingWater.ts b/Customizations/Layouts/WalkByBrussels.ts similarity index 69% rename from Customizations/Layouts/DrinkingWater.ts rename to Customizations/Layouts/WalkByBrussels.ts index 41360dde1..88126a1d6 100644 --- a/Customizations/Layouts/DrinkingWater.ts +++ b/Customizations/Layouts/WalkByBrussels.ts @@ -1,11 +1,14 @@ import { Layout } from "../Layout"; import { DrinkingWaterLayer } from "../Layers/DrinkingWater"; +import { NatureReserves } from "../Layers/NatureReserves"; +import { Park } from "../Layers/Park"; +import { BikeParkings } from "../Layers/BikeParkings"; -export class DrinkingWater extends Layout { +export class WalkByBrussels extends Layout { constructor() { - super("drinkingwater", + super("walkbybrussels", "Drinking Water Spots", - [new DrinkingWaterLayer()], + [new DrinkingWaterLayer(), new BikeParkings(), new Park(), new NatureReserves()], 10, 50.8435, 4.3688, diff --git a/Logic/FilteredLayer.ts b/Logic/FilteredLayer.ts index 87ba7c669..ee936f7c3 100644 --- a/Logic/FilteredLayer.ts +++ b/Logic/FilteredLayer.ts @@ -1,11 +1,11 @@ -import {Basemap} from "./Basemap"; -import {TagsFilter, TagUtils} from "./TagsFilter"; -import {UIEventSource} from "../UI/UIEventSource"; -import {ElementStorage} from "./ElementStorage"; -import {Changes} from "./Changes"; +import { Basemap } from "./Basemap"; +import { TagsFilter, TagUtils } from "./TagsFilter"; +import { UIEventSource } from "../UI/UIEventSource"; +import { ElementStorage } from "./ElementStorage"; +import { Changes } from "./Changes"; import L from "leaflet" -import {GeoOperations} from "./GeoOperations"; -import {UIElement} from "../UI/UIElement"; +import { GeoOperations } from "./GeoOperations"; +import { UIElement } from "../UI/UIElement"; /*** * A filtered layer is a layer which offers a 'set-data' function @@ -20,6 +20,7 @@ export class FilteredLayer { public readonly name: string; public readonly filters: TagsFilter; + public readonly isDisplayed: UIEventSource = new UIEventSource(true); private readonly _map: Basemap; private readonly _maxAllowedOverlap: number; @@ -65,6 +66,16 @@ export class FilteredLayer { this._style = style; this._storage = storage; this._maxAllowedOverlap = maxAllowedOverlap; + const self = this; + this.isDisplayed.addCallback(function (isDisplayed) { + if (self._geolayer !== undefined && self._geolayer !== null) { + if (isDisplayed) { + self._geolayer.addTo(self._map.map); + } else { + self._map.map.removeLayer(self._geolayer); + } + } + }) } @@ -174,7 +185,7 @@ export class FilteredLayer { radius: 25, color: style.color }); - + } else { marker = L.marker(latLng, { icon: style.icon @@ -206,7 +217,9 @@ export class FilteredLayer { } }); - this._geolayer.addTo(this._map.map); + if (this.isDisplayed.data) { + this._geolayer.addTo(this._map.map); + } } diff --git a/index.ts b/index.ts index d8190aa9c..8842d38a2 100644 --- a/index.ts +++ b/index.ts @@ -137,7 +137,7 @@ const bm = new Basemap("leafletDiv", locationControl, new VariableUiElement( // ------------- Setup the layers ------------------------------- - +const controls = {}; const addButtons: { name: string, icon: string, @@ -168,6 +168,8 @@ for (const layer of questSetToRender.layers) { const flayer = layer.asLayer(bm, allElements, changes, osmConnection.userDetails, selectedElement, generateInfo); + controls[layer.name] = flayer.isDisplayed; + const addButton = { name: layer.name, icon: layer.icon, @@ -273,4 +275,4 @@ new GeoLocationHandler(bm).AttachTo("geolocate-button"); // --------------- Send a ping to start various action -------- locationControl.ping(); -messageBox.update(); +messageBox.update(); \ No newline at end of file