diff --git a/Customizations/Layers/BikeParkings.ts b/Customizations/Layers/BikeParkings.ts index 2f032c2d5..7dc1bd9df 100644 --- a/Customizations/Layers/BikeParkings.ts +++ b/Customizations/Layers/BikeParkings.ts @@ -2,7 +2,7 @@ import {LayerDefinition} from "../LayerDefinition"; import {And, Or, Tag} from "../../Logic/TagsFilter"; import {OperatorTag} from "../Questions/OperatorTag"; import * as L from "leaflet"; -import FixedName from "../Questions/FixedName"; +import FixedText from "../Questions/FixedText"; import { BikeParkingType } from "../Questions/BikeParkingType"; export class BikeParkings extends LayerDefinition { @@ -26,7 +26,7 @@ export class BikeParkings extends LayerDefinition { this.minzoom = 13; this.style = this.generateStyleFunction(); - this.title = new FixedName("fietsparking"); + this.title = new FixedText("fietsparking"); this.elementsToShow = [ new OperatorTag(), new BikeParkingType() diff --git a/Customizations/Layers/BikePumps.ts b/Customizations/Layers/BikePumps.ts index 4da60d6ba..d214f1337 100644 --- a/Customizations/Layers/BikePumps.ts +++ b/Customizations/Layers/BikePumps.ts @@ -3,7 +3,8 @@ import {And, Or, Tag} from "../../Logic/TagsFilter"; import {OperatorTag} from "../Questions/OperatorTag"; import * as L from "leaflet"; import { PumpManual } from "../Questions/PumpManual"; -import FixedName from "../Questions/FixedName"; +import FixedText from "../Questions/FixedText"; +import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload"; export class BikePumps extends LayerDefinition { @@ -14,24 +15,25 @@ export class BikePumps extends LayerDefinition { this.overpassFilter = new Or([ new And([ - new Tag("amenity", "compressed_air"), - new Tag("bicycle", "yes"), + new Tag("amenity", "bicycle_repair_station"), + new Tag("service:bicycle:pump", "yes"), ]) ] ); this.newElementTags = [ - new Tag("amenity", "compressed_air"), - new Tag("bicycle", "yes"), + new Tag("amenity", "bicycle_repair_station"), + new Tag("service:bicycle:pump", "yes"), // new Tag("fixme", "Toegevoegd met MapComplete, geometry nog uit te tekenen") ]; this.maxAllowedOverlapPercentage = 10; this.minzoom = 13; this.style = this.generateStyleFunction(); - this.title = new FixedName("pomp"); + this.title = new FixedText("Pomp"); this.elementsToShow = [ + new ImageCarouselWithUploadConstructor(), // new NameQuestion(), // new AccessTag(), new OperatorTag(), diff --git a/Customizations/Layers/Bos.ts b/Customizations/Layers/Bos.ts index b9b809c81..a396550ef 100644 --- a/Customizations/Layers/Bos.ts +++ b/Customizations/Layers/Bos.ts @@ -7,6 +7,7 @@ import {TagRenderingOptions} from "../TagRendering"; import {NameQuestion} from "../Questions/NameQuestion"; import {NameInline} from "../Questions/NameInline"; import {DescriptionQuestion} from "../Questions/DescriptionQuestion"; +import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload"; export class Bos extends LayerDefinition { @@ -33,6 +34,7 @@ export class Bos extends LayerDefinition { this.style = this.generateStyleFunction(); this.title = new NameInline("bos"); this.elementsToShow = [ + new ImageCarouselWithUploadConstructor(), new NameQuestion(), new AccessTag(), new OperatorTag(), diff --git a/Customizations/Layers/GhostBike.ts b/Customizations/Layers/GhostBike.ts new file mode 100644 index 000000000..f88d2748d --- /dev/null +++ b/Customizations/Layers/GhostBike.ts @@ -0,0 +1,36 @@ +import {LayerDefinition} from "../LayerDefinition"; +import {Tag} from "../../Logic/TagsFilter"; +import {FixedUiElement} from "../../UI/Base/FixedUiElement"; +import {TagRenderingOptions} from "../TagRendering"; +import FixedText from "../Questions/FixedText"; +import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload"; +import L from "leaflet"; + +export class GhostBike extends LayerDefinition { + constructor() { + super(); + this.name = "ghost bike"; + this.overpassFilter = new Tag("memorial", "ghost_bike") + this.title = new FixedText("Ghost bike"); + + this.elementsToShow = [ + new FixedText("A ghost bike is a memorial for a cyclist who died in a traffic accident," + + " in the form of a white bicycle placed permanently near the accident location."), + new ImageCarouselWithUploadConstructor(), + + + ]; + + this.style = (tags: any) => { + return { + color: "#000000", + icon: L.icon({ + iconUrl: 'assets/ghost_bike.svg', + iconSize: [40, 40], + iconAnchor: [20, 20], + }) + } + }; + + } +} \ No newline at end of file diff --git a/Customizations/Layers/NatureReserves.ts b/Customizations/Layers/NatureReserves.ts index f6b1abfb4..5d492922e 100644 --- a/Customizations/Layers/NatureReserves.ts +++ b/Customizations/Layers/NatureReserves.ts @@ -6,6 +6,7 @@ import {OperatorTag} from "../Questions/OperatorTag"; import {NameQuestion} from "../Questions/NameQuestion"; import {NameInline} from "../Questions/NameInline"; import {DescriptionQuestion} from "../Questions/DescriptionQuestion"; +import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload"; export class NatureReserves extends LayerDefinition { @@ -23,6 +24,7 @@ export class NatureReserves extends LayerDefinition { this.title = new NameInline("natuurreservaat"); this.style = this.generateStyleFunction(); this.elementsToShow = [ + new ImageCarouselWithUploadConstructor(), new NameQuestion(), new AccessTag(), new OperatorTag(), diff --git a/Customizations/Layers/Park.ts b/Customizations/Layers/Park.ts index aee1a23de..e2814a777 100644 --- a/Customizations/Layers/Park.ts +++ b/Customizations/Layers/Park.ts @@ -7,6 +7,7 @@ import {TagRenderingOptions} from "../TagRendering"; import {NameQuestion} from "../Questions/NameQuestion"; import {NameInline} from "../Questions/NameInline"; import {DescriptionQuestion} from "../Questions/DescriptionQuestion"; +import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload"; export class Park extends LayerDefinition { @@ -58,6 +59,7 @@ export class Park extends LayerDefinition { this.style = this.generateStyleFunction(); this.title = new NameInline("park"); this.elementsToShow = [ + new ImageCarouselWithUploadConstructor(), new NameQuestion(), this.accessByDefault, this.operatorByDefault, diff --git a/Customizations/Layouts/Cyclofix.ts b/Customizations/Layouts/Cyclofix.ts index 7ba29ada6..88b08f754 100644 --- a/Customizations/Layouts/Cyclofix.ts +++ b/Customizations/Layouts/Cyclofix.ts @@ -2,13 +2,14 @@ import {Layout} from "../Layout"; import {GrbToFix} from "../Layers/GrbToFix"; import { BikePumps } from "../Layers/BikePumps"; import { BikeParkings } from "../Layers/BikeParkings"; +import {GhostBike} from "../Layers/GhostBike"; export default class Cyclofix extends Layout { constructor() { super( "pomp", "Grb import fix tool", - [new BikePumps(), new BikeParkings()], + [new BikePumps(), new BikeParkings(), new GhostBike()], 15, 51.2083, 3.2279, diff --git a/Customizations/Questions/FixedName.ts b/Customizations/Questions/FixedText.ts similarity index 79% rename from Customizations/Questions/FixedName.ts rename to Customizations/Questions/FixedText.ts index 0210f9cf4..178fb53f4 100644 --- a/Customizations/Questions/FixedName.ts +++ b/Customizations/Questions/FixedText.ts @@ -1,6 +1,6 @@ import { TagRenderingOptions } from "../TagRendering"; -export default class FixedName extends TagRenderingOptions { +export default class FixedText extends TagRenderingOptions { constructor(category: string) { super({ mappings: [ diff --git a/Logic/OsmConnection.ts b/Logic/OsmConnection.ts index 45d5bcedd..e35d2543d 100644 --- a/Logic/OsmConnection.ts +++ b/Logic/OsmConnection.ts @@ -13,7 +13,6 @@ export class UserDetails { public osmConnection: OsmConnection; public dryRun: boolean; home: { lon: number; lat: number }; - } export class OsmConnection { @@ -121,6 +120,27 @@ export class OsmConnection { } public preferences = new UIEventSource({}); + public preferenceSources : any = {} + + public GetPreference(key: string) : UIEventSource{ + if(this.preferenceSources[key] !== undefined){ + return this.preferenceSources[key]; + } + const pref = new UIEventSource(undefined); + pref.addCallback((v) => { + this.SetPreference(key, v); + }); + + this.preferences.addCallback((prefs) => { + if (prefs[key] !== undefined) { + pref.setData(prefs[key]); + } + }); + + this.preferenceSources[key] = pref; + return pref; + } + private UpdatePreferences() { const self = this; this.auth.xhr({ @@ -142,7 +162,7 @@ export class OsmConnection { }); } - public SetPreference(k:string, v:string) { + private SetPreference(k:string, v:string) { if(!this.userDetails.data.loggedIn){ console.log("Not saving preference: user not logged in"); return; diff --git a/README.md b/README.md index 594264c0f..9994bee2e 100644 --- a/README.md +++ b/README.md @@ -94,3 +94,6 @@ Trash icon by Dave Gandy, CC-BY-SA https://commons.wikimedia.org/wiki/File:Home-icon.svg Home icon by Timothy Miller, CC-BY-SA 3.0 + +https://commons.wikimedia.org/wiki/File:Map_icons_by_Scott_de_Jonge_-_bicycle-store.svg +Bicycle logo, Scott de Jonge \ No newline at end of file diff --git a/UI/FeatureInfoBox.ts b/UI/FeatureInfoBox.ts index b35841718..2c7832c13 100644 --- a/UI/FeatureInfoBox.ts +++ b/UI/FeatureInfoBox.ts @@ -15,29 +15,25 @@ import {TagDependantUIElement} from "../Customizations/UIElementConstructor"; export class FeatureInfoBox extends UIElement { private _tagsES: UIEventSource; + private _changes: Changes; + private _userDetails: UIEventSource; private _title: UIElement; private _osmLink: UIElement; - - - private _questions: QuestionPicker; - - private _changes: Changes; - private _userDetails: UIEventSource; - private _imageElement: ImageCarousel; - private _pictureUploader: UIElement; private _wikipedialink: UIElement; - private _infoboxes: TagDependantUIElement[]; + + private _infoboxes: TagDependantUIElement[]; + private _questions: QuestionPicker; + constructor( tagsES: UIEventSource, title: TagRenderingOptions, elementsToShow: TagRenderingOptions[], changes: Changes, - userDetails: UIEventSource, - preferedPictureLicense: UIEventSource + userDetails: UIEventSource ) { super(tagsES); this._tagsES = tagsES; @@ -45,9 +41,9 @@ export class FeatureInfoBox extends UIElement { this._userDetails = userDetails; this.ListenTo(userDetails); - this._imageElement = new ImageCarousel(this._tagsES, changes); - + this._infoboxes = []; + elementsToShow = elementsToShow ?? [] for (const tagRenderingOption of elementsToShow) { this._infoboxes.push( tagRenderingOption.construct(this._tagsES, this._changes)); @@ -60,11 +56,9 @@ export class FeatureInfoBox extends UIElement { ) this._title = new TagRenderingOptions(title.options).construct(this._tagsES, this._changes); - this._osmLink =new OsmLink().construct(this._tagsES, this._changes); this._wikipedialink = new WikipediaLink().construct(this._tagsES, this._changes); - this._pictureUploader = new OsmImageUploadHandler(tagsES, userDetails, preferedPictureLicense, - changes, this._imageElement.slideshow).getUI(); + } @@ -110,10 +104,6 @@ export class FeatureInfoBox extends UIElement { "" + "
" + - - this._imageElement.Render() + - this._pictureUploader.Render() + - new VerticalCombine(info, "infobox-information ").Render() + questionsHtml + @@ -126,8 +116,6 @@ export class FeatureInfoBox extends UIElement { Activate() { super.Activate(); - this._imageElement.Activate(); - this._pictureUploader.Activate(); for (const infobox of this._infoboxes) { infobox.Activate(); } @@ -135,8 +123,6 @@ export class FeatureInfoBox extends UIElement { Update() { super.Update(); - this._imageElement.Update(); - this._pictureUploader.Update(); this._title.Update(); for (const infobox of this._infoboxes) { infobox.Update(); diff --git a/UI/Image/ImageCarousel.ts b/UI/Image/ImageCarousel.ts index f8f69c9d4..9d4e1fd00 100644 --- a/UI/Image/ImageCarousel.ts +++ b/UI/Image/ImageCarousel.ts @@ -8,8 +8,29 @@ import {Changes} from "../../Logic/Changes"; import {VariableUiElement} from "../Base/VariableUIElement"; import {ConfirmDialog} from "../ConfirmDialog"; import {UserDetails} from "../../Logic/OsmConnection"; +import {TagDependantUIElement, TagDependantUIElementConstructor} from "../../Customizations/UIElementConstructor"; + +export class ImageCarouselConstructor implements TagDependantUIElementConstructor{ + IsKnown(properties: any): boolean { + return true; + } + + IsQuestioning(properties: any): boolean { + return false; + } + + Priority(): number { + return 0; + } + + construct(tags: UIEventSource, changes: Changes): TagDependantUIElement { + return new ImageCarousel(tags, changes); + } + +} + +export class ImageCarousel extends TagDependantUIElement { -export class ImageCarousel extends UIElement { private readonly searcher: ImageSearcher; @@ -98,6 +119,18 @@ export class ImageCarousel extends UIElement { ""; } + IsKnown(): boolean { + return true; + } + + IsQuestioning(): boolean { + return false; + } + + Priority(): number { + return 0; + } + InnerUpdate(htmlElement: HTMLElement) { super.InnerUpdate(htmlElement); this._deleteButton.Update(); diff --git a/UI/Image/ImageCarouselWithUpload.ts b/UI/Image/ImageCarouselWithUpload.ts new file mode 100644 index 000000000..231044aae --- /dev/null +++ b/UI/Image/ImageCarouselWithUpload.ts @@ -0,0 +1,71 @@ +import {TagDependantUIElement, TagDependantUIElementConstructor} from "../../Customizations/UIElementConstructor"; +import {ImageCarousel} from "./ImageCarousel"; +import {OsmImageUploadHandler} from "../../Logic/OsmImageUploadHandler"; +import {UIEventSource} from "../UIEventSource"; +import {Changes} from "../../Logic/Changes"; +import {UserDetails} from "../../Logic/OsmConnection"; +import {ImageUploadFlow} from "../ImageUploadFlow"; + +export class ImageCarouselWithUploadConstructor implements TagDependantUIElementConstructor{ + IsKnown(properties: any): boolean { + return true; + } + + IsQuestioning(properties: any): boolean { + return false; + } + + Priority(): number { + return 0; + } + + construct(tags: UIEventSource, changes: Changes): TagDependantUIElement { + return new ImageCarouselWithUpload(tags, changes); + } +} + +class ImageCarouselWithUpload extends TagDependantUIElement { + private _imageElement: ImageCarousel; + private _pictureUploader: ImageUploadFlow; + + constructor(tags: UIEventSource, changes: Changes) { + super(tags); + this._imageElement = new ImageCarousel(tags, changes); + const userDetails = changes.login.userDetails; + const license = changes.login.GetPreference( "mapcomplete-pictures-license"); + this._pictureUploader = new OsmImageUploadHandler(tags, + userDetails, license, + changes, this._imageElement.slideshow).getUI(); + + } + + protected InnerRender(): string { + return this._imageElement.Render() + + this._pictureUploader.Render(); + } + + Activate() { + super.Activate(); + this._imageElement.Activate(); + this._pictureUploader.Activate(); + } + + Update() { + super.Update(); + this._imageElement.Update(); + this._pictureUploader.Update(); + } + + IsKnown(): boolean { + return true; + } + + IsQuestioning(): boolean { + return false; + } + + Priority(): number { + return 0; + } + +} \ No newline at end of file diff --git a/assets/ghost_bike.svg b/assets/ghost_bike.svg new file mode 100644 index 000000000..1befcb799 --- /dev/null +++ b/assets/ghost_bike.svg @@ -0,0 +1,92 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/index.ts b/index.ts index 252cf5c50..73b8cea11 100644 --- a/index.ts +++ b/index.ts @@ -102,7 +102,6 @@ const leftMessage = new UIEventSource<() => UIElement>(undefined); const selectedElement = new UIEventSource(undefined); -const preferedPictureLicense = new UIEventSource(undefined); const locationControl = new UIEventSource<{ lat: number, lon: number, zoom: number }>({ zoom: questSetToRender.startzoom, @@ -137,21 +136,6 @@ const bm = new Basemap("leafletDiv", locationControl, new VariableUiElement( )); -// ------------- Tie together user settings and UI ----------- - - -const picturesPrefName = "mapcomplete-pictures-license"; -preferedPictureLicense.addCallback((license) => { - osmConnection.SetPreference(picturesPrefName, license); -}); - -osmConnection.preferences.addCallback((prefs) => { - if (prefs[picturesPrefName] !== undefined) { - preferedPictureLicense.setData(prefs[picturesPrefName]); - } -}) - - // ------------- Setup the layers ------------------------------- const addButtons: { @@ -175,8 +159,7 @@ for (const layer of questSetToRender.layers) { layer.title, layer.elementsToShow, changes, - osmConnection.userDetails, - preferedPictureLicense + osmConnection.userDetails ) }; @@ -228,8 +211,7 @@ selectedElement.addCallback((data) => { layer.title, layer.elementsToShow, changes, - osmConnection.userDetails, - preferedPictureLicense + osmConnection.userDetails )); break; }