forked from MapComplete/MapComplete
		
	Various bug fixes and updates
This commit is contained in:
		
							parent
							
								
									97ec893479
								
							
						
					
					
						commit
						e069b31e4e
					
				
					 29 changed files with 482 additions and 148 deletions
				
			
		|  | @ -17,6 +17,7 @@ import * as viewpoint from "../../assets/layers/viewpoint/viewpoint.json" | ||||||
| import * as bike_parking from "../../assets/layers/bike_parking/bike_parking.json" | import * as bike_parking from "../../assets/layers/bike_parking/bike_parking.json" | ||||||
| import * as bike_repair_station from "../../assets/layers/bike_repair_station/bike_repair_station.json" | import * as bike_repair_station from "../../assets/layers/bike_repair_station/bike_repair_station.json" | ||||||
| import * as birdhides from "../../assets/layers/bird_hide/birdhides.json" | import * as birdhides from "../../assets/layers/bird_hide/birdhides.json" | ||||||
|  | import * as nature_reserve from "../../assets/layers/nature_reserve/nature_reserve.json" | ||||||
| 
 | 
 | ||||||
| import {Utils} from "../../Utils"; | import {Utils} from "../../Utils"; | ||||||
| 
 | 
 | ||||||
|  | @ -34,6 +35,7 @@ export class FromJSON { | ||||||
|             FromJSON.Layer(bike_parking), |             FromJSON.Layer(bike_parking), | ||||||
|             FromJSON.Layer(bike_repair_station), |             FromJSON.Layer(bike_repair_station), | ||||||
|             FromJSON.Layer(birdhides), |             FromJSON.Layer(birdhides), | ||||||
|  |             FromJSON.Layer(nature_reserve), | ||||||
|         ]; |         ]; | ||||||
| 
 | 
 | ||||||
|         for (const layer of sharedLayersList) { |         for (const layer of sharedLayersList) { | ||||||
|  | @ -102,7 +104,6 @@ export class FromJSON { | ||||||
|     public static TagRenderingWithDefault(json: TagRenderingConfigJson | string, propertyName, defaultValue: string): TagDependantUIElementConstructor { |     public static TagRenderingWithDefault(json: TagRenderingConfigJson | string, propertyName, defaultValue: string): TagDependantUIElementConstructor { | ||||||
|         if (json === undefined) { |         if (json === undefined) { | ||||||
|             if(defaultValue !== undefined){ |             if(defaultValue !== undefined){ | ||||||
|                 console.log(`Using default value ${defaultValue} for  ${propertyName}`) |  | ||||||
|                 return FromJSON.TagRendering(defaultValue, propertyName); |                 return FromJSON.TagRendering(defaultValue, propertyName); | ||||||
|             } |             } | ||||||
|             throw `Tagrendering ${propertyName} is undefined...` |             throw `Tagrendering ${propertyName} is undefined...` | ||||||
|  | @ -207,7 +208,7 @@ export class FromJSON { | ||||||
|         return new Tag(tag[0], tag[1]); |         return new Tag(tag[0], tag[1]); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static Tag(json: AndOrTagConfigJson | string, context: string): TagsFilter { |     public static Tag(json: AndOrTagConfigJson | string, context: string = ""): TagsFilter { | ||||||
|         if(json === undefined){ |         if(json === undefined){ | ||||||
|             throw "Error while parsing a tag: nothing defined. Make sure all the tags are defined and at least one tag is present in a complex expression" |             throw "Error while parsing a tag: nothing defined. Make sure all the tags are defined and at least one tag is present in a complex expression" | ||||||
|         } |         } | ||||||
|  | @ -286,7 +287,6 @@ export class FromJSON { | ||||||
| 
 | 
 | ||||||
|     private static LayerUncaught(json: LayerConfigJson): LayerDefinition { |     private static LayerUncaught(json: LayerConfigJson): LayerDefinition { | ||||||
| 
 | 
 | ||||||
|         console.log("Parsing layer", json) |  | ||||||
|         const tr = FromJSON.Translation; |         const tr = FromJSON.Translation; | ||||||
|         const overpassTags = FromJSON.Tag(json.overpassTags, "overpasstags for layer "+json.id); |         const overpassTags = FromJSON.Tag(json.overpassTags, "overpasstags for layer "+json.id); | ||||||
|         const icon = FromJSON.TagRenderingWithDefault(json.icon, "icon", "./assets/bug.svg"); |         const icon = FromJSON.TagRenderingWithDefault(json.icon, "icon", "./assets/bug.svg"); | ||||||
|  | @ -381,6 +381,7 @@ export class FromJSON { | ||||||
| 
 | 
 | ||||||
|             } |             } | ||||||
|         ); |         ); | ||||||
|  |         layer.maxAllowedOverlapPercentage = json.hideUnderlayingFeaturesMinPercentage; | ||||||
|         return layer; |         return layer; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -70,6 +70,14 @@ export interface LayerConfigJson { | ||||||
|      */ |      */ | ||||||
|     wayHandling?: number; |     wayHandling?: number; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Consider that we want to show 'Nature Reserves' and 'Forests'. Now, ofter, there are pieces of forest mapped _in_ the nature reserve. | ||||||
|  |      * Now, showing those pieces of forest overlapping with the nature reserve truly clutters the map and is very user-unfriendly. | ||||||
|  |      *  | ||||||
|  |      * The features are placed layer by layer. If a feature below a feature on this layer overlaps for more then 'x'-percent, the underlying feature is hidden. | ||||||
|  |      */ | ||||||
|  |     hideUnderlayingFeaturesMinPercentage?:number; | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Presets for this layer |      * Presets for this layer | ||||||
|      */ |      */ | ||||||
|  |  | ||||||
|  | @ -129,47 +129,5 @@ export class LayerDefinition { | ||||||
|         this.style = options.style; |         this.style = options.style; | ||||||
|         this.wayHandling = options.wayHandling ?? LayerDefinition.WAYHANDLING_DEFAULT; |         this.wayHandling = options.wayHandling ?? LayerDefinition.WAYHANDLING_DEFAULT; | ||||||
|     } |     } | ||||||
| 
 |      | ||||||
| /* |  | ||||||
|     ToJson() { |  | ||||||
| 
 |  | ||||||
|         function t(translation: string | Translation | UIElement) { |  | ||||||
|             if (translation === undefined) { |  | ||||||
|                 return undefined; |  | ||||||
|             } |  | ||||||
|             if (typeof (translation) === "string") { |  | ||||||
|                 return translation; |  | ||||||
|             } |  | ||||||
|             if (translation instanceof Translation && translation.translations !== undefined) { |  | ||||||
|                 return translation.translations; |  | ||||||
|             } |  | ||||||
|             return translation.InnerRender(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         function tr(tagRendering : TagRenderingOptions) : TagRenderingConfigJson{ |  | ||||||
|             const o = tagRendering.options; |  | ||||||
|             return { |  | ||||||
|                 key: o.freeform.key, |  | ||||||
|                 render: o.freeform.renderTemplate, |  | ||||||
|                 type: o.freeform.template. |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         const layerConfig  : LayerConfigJson = { |  | ||||||
|             name: t(this.name), |  | ||||||
|             description: t(this.description), |  | ||||||
|             maxAllowedOverlapPercentage: this.maxAllowedOverlapPercentage, |  | ||||||
|             presets: this.presets, |  | ||||||
|             icon: this.icon, |  | ||||||
|             minzoom: this.minzoom, |  | ||||||
|             overpassFilter: this.overpassFilter, |  | ||||||
|             title: this.title, |  | ||||||
|             elementsToShow: this.elementsToShow, |  | ||||||
|             style: this.style, |  | ||||||
|             wayHandling: this.wayHandling, |  | ||||||
| 
 |  | ||||||
|         }; |  | ||||||
|          |  | ||||||
|         return JSON.stringify(layerConfig) |  | ||||||
|     }*/ |  | ||||||
| } | } | ||||||
|  | @ -35,7 +35,7 @@ export class Bos extends LayerDefinition { | ||||||
| 
 | 
 | ||||||
|         this.minzoom = 13; |         this.minzoom = 13; | ||||||
|         this.style = this.generateStyleFunction(); |         this.style = this.generateStyleFunction(); | ||||||
|         this.title = new NameInline("bos"); |         this.title = new NameInline("Bos"); | ||||||
|         this.elementsToShow = [ |         this.elementsToShow = [ | ||||||
|             new ImageCarouselWithUploadConstructor(), |             new ImageCarouselWithUploadConstructor(), | ||||||
|             new NameQuestion(), |             new NameQuestion(), | ||||||
|  |  | ||||||
|  | @ -26,7 +26,7 @@ export class NatureReserves extends LayerDefinition { | ||||||
|         } |         } | ||||||
|         ]; |         ]; | ||||||
|         this.minzoom = 13; |         this.minzoom = 13; | ||||||
|         this.title = new NameInline("natuurreservaat"); |         this.title = new NameInline("Natuurreservaat"); | ||||||
|         this.style = this.generateStyleFunction(); |         this.style = this.generateStyleFunction(); | ||||||
|         this.elementsToShow = [ |         this.elementsToShow = [ | ||||||
|             new ImageCarouselWithUploadConstructor(), |             new ImageCarouselWithUploadConstructor(), | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ export class Park extends LayerDefinition { | ||||||
|         question: "Is dit park publiek toegankelijk?", |         question: "Is dit park publiek toegankelijk?", | ||||||
|         mappings: [ |         mappings: [ | ||||||
|             {k: new Tag("access", "yes"), txt: "Publiek toegankelijk"}, |             {k: new Tag("access", "yes"), txt: "Publiek toegankelijk"}, | ||||||
|             {k: new Tag("access", ""), txt: "Publiek toegankelijk"}, |             {k: new Tag("access", ""), txt: "Publiek toegankelijk", hideInAnswer: true}, | ||||||
|             {k: new Tag("access", "no"), txt: "Niet publiek toegankelijk"}, |             {k: new Tag("access", "no"), txt: "Niet publiek toegankelijk"}, | ||||||
|             {k: new Tag("access", "private"), txt: "Niet publiek toegankelijk, want privaat"}, |             {k: new Tag("access", "private"), txt: "Niet publiek toegankelijk, want privaat"}, | ||||||
|             {k: new Tag("access", "guided"), txt: "Enkel toegankelijk met een gids of op een activiteit"}, |             {k: new Tag("access", "guided"), txt: "Enkel toegankelijk met een gids of op een activiteit"}, | ||||||
|  | @ -59,7 +59,7 @@ export class Park extends LayerDefinition { | ||||||
| 
 | 
 | ||||||
|         this.minzoom = 13; |         this.minzoom = 13; | ||||||
|         this.style = this.generateStyleFunction(); |         this.style = this.generateStyleFunction(); | ||||||
|         this.title = new NameInline("park"); |         this.title = new NameInline("Park"); | ||||||
|         this.elementsToShow = [ |         this.elementsToShow = [ | ||||||
|             new ImageCarouselWithUploadConstructor(), |             new ImageCarouselWithUploadConstructor(), | ||||||
|             new NameQuestion(), |             new NameQuestion(), | ||||||
|  |  | ||||||
|  | @ -168,12 +168,12 @@ export class Widths extends LayerDefinition { | ||||||
| 
 | 
 | ||||||
|             let dashArray = undefined; |             let dashArray = undefined; | ||||||
|             if (props.onewayBike) { |             if (props.onewayBike) { | ||||||
|                 dashArray = [20, 8] |                 dashArray = [5, 6] | ||||||
|             } |             } | ||||||
|             return { |             return { | ||||||
|                 icon: null, |                 icon: null, | ||||||
|                 color: c, |                 color: c, | ||||||
|                 weight: 9, |                 weight: 5, | ||||||
|                 dashArray: dashArray |                 dashArray: dashArray | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| import {NatureReserves} from "../Layers/NatureReserves"; |  | ||||||
| import {Park} from "../Layers/Park"; | import {Park} from "../Layers/Park"; | ||||||
| import {Bos} from "../Layers/Bos"; | import {Bos} from "../Layers/Bos"; | ||||||
| import {Layout} from "../Layout"; | import {Layout} from "../Layout"; | ||||||
|  | import {NatureReserves} from "../Layers/NatureReserves"; | ||||||
| 
 | 
 | ||||||
| export class Groen extends Layout { | export class Groen extends Layout { | ||||||
|      |      | ||||||
|  |  | ||||||
|  | @ -41,7 +41,7 @@ export class OnlyShowIfConstructor implements TagDependantUIElementConstructor{ | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     GetContent(tags: any): Translation { |     GetContent(tags: any): Translation { | ||||||
|         if(this.IsKnown(tags)){ |         if(!this.IsKnown(tags)){ | ||||||
|             return undefined; |             return undefined; | ||||||
|         } |         } | ||||||
|         return this._embedded.GetContent(tags); |         return this._embedded.GetContent(tags); | ||||||
|  |  | ||||||
|  | @ -17,7 +17,7 @@ export class AccessTag extends TagRenderingOptions { | ||||||
|             {k: new And([new Tag("access", "no"), new Tag("fee", "")]), txt: "Niet toegankelijk"}, |             {k: new And([new Tag("access", "no"), new Tag("fee", "")]), txt: "Niet toegankelijk"}, | ||||||
|             {k: new And([new Tag("access", "private"), new Tag("fee", "")]), txt: "Niet toegankelijk, want privegebied"}, |             {k: new And([new Tag("access", "private"), new Tag("fee", "")]), txt: "Niet toegankelijk, want privegebied"}, | ||||||
|             {k: new And([new Tag("access", "permissive"), new Tag("fee", "")]), txt: "Toegankelijk, maar het is privegebied"}, |             {k: new And([new Tag("access", "permissive"), new Tag("fee", "")]), txt: "Toegankelijk, maar het is privegebied"}, | ||||||
|             {k: new And([new Tag("access", "guided"), new Tag("fee", "")]), txt: "Enkel met gids of op activiteit"}, |             {k: new And([new Tag("access", "guided"), new Tag("fee", "")]), txt: "Enkel met een gids of tijdens een activiteit toegankelijk"}, | ||||||
|             { |             { | ||||||
|                 k: new And([new Tag("access", "yes"), |                 k: new And([new Tag("access", "yes"), | ||||||
|                     new Tag("fee", "yes")]), |                     new Tag("fee", "yes")]), | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| import {Tag} from "../../Logic/Tags"; | import {RegexTag, Tag} from "../../Logic/Tags"; | ||||||
| import Translations from "../../UI/i18n/Translations"; | import Translations from "../../UI/i18n/Translations"; | ||||||
| import {TagRenderingOptions} from "../TagRenderingOptions"; | import {TagRenderingOptions} from "../TagRenderingOptions"; | ||||||
| import Translation from "../../UI/i18n/Translation"; | import Translation from "../../UI/i18n/Translation"; | ||||||
|  | @ -8,18 +8,10 @@ export class NameInline extends TagRenderingOptions{ | ||||||
|      |      | ||||||
|     constructor(category: string | Translation ) { |     constructor(category: string | Translation ) { | ||||||
|         super({ |         super({ | ||||||
|             question: "", |  | ||||||
| 
 |  | ||||||
|             freeform: { |  | ||||||
|                 renderTemplate: "{name}", |  | ||||||
|                 template: Translations.t.general.nameInlineQuestion.Subs({category: category}), |  | ||||||
|                 key: "name", |  | ||||||
|                 extraTags: new Tag("noname", "") // Remove 'noname=yes'
 |  | ||||||
|             }, |  | ||||||
| 
 |  | ||||||
|             mappings: [ |             mappings: [ | ||||||
|                 {k: new Tag("noname","yes"), txt: Translations.t.general.noNameCategory.Subs({category: category})}, |                 {k: new Tag("noname", "yes"), txt: Translations.t.general.noNameCategory.Subs({category: category})}, | ||||||
|                 {k: null, txt: category} |                 {k: new RegexTag("name", /.+/), txt: "{name}"}, | ||||||
|  |                 {k:new Tag("name",""), txt: category} | ||||||
|             ] |             ] | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -3,31 +3,29 @@ | ||||||
|  * One is a big 'name-question', the other is the 'edit name' in the title. |  * One is a big 'name-question', the other is the 'edit name' in the title. | ||||||
|  * THis one is the big question |  * THis one is the big question | ||||||
|  */ |  */ | ||||||
| import {Tag} from "../../Logic/Tags"; | import {And, Tag} from "../../Logic/Tags"; | ||||||
| import {TagRenderingOptions} from "../TagRenderingOptions"; | import {TagRenderingOptions} from "../TagRenderingOptions"; | ||||||
| 
 | 
 | ||||||
| export class NameQuestion extends TagRenderingOptions{ | export class NameQuestion extends TagRenderingOptions { | ||||||
|      | 
 | ||||||
|     static options =  { |  | ||||||
|         priority: 10, // Move this last on the priority list, in order to prevent ppl to enter access restrictions and descriptions
 |  | ||||||
|         question: "Wat is de <i>officiële</i> naam van dit gebied?<br><span class='question-subtext'>" + |  | ||||||
|             "Zelf een naam bedenken wordt afgeraden.<br/>" + |  | ||||||
|             "Een beschrijving van het gebied geven kan in een volgende stap.<br/>" + |  | ||||||
|             "</span>", |  | ||||||
|         freeform: { |  | ||||||
|             key: "name", |  | ||||||
|             template: "De naam is $$$", |  | ||||||
|             renderTemplate: "", // We don't actually render it, only ask
 |  | ||||||
|             placeholder: "", |  | ||||||
|             extraTags: new Tag("noname","") |  | ||||||
|         }, |  | ||||||
|         mappings: [ |  | ||||||
|             {k: new Tag("noname", "yes"), txt: "Dit gebied heeft geen naam"}, |  | ||||||
|         ] |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     constructor() { |     constructor() { | ||||||
|         super(NameQuestion.options); |         super({ | ||||||
|  |             priority: 10, // Move this last on the priority list, in order to prevent ppl to enter access restrictions and descriptions
 | ||||||
|  |             question: "Wat is de <i>officiële</i> naam van dit gebied?<br><span class='question-subtext'>" + | ||||||
|  |                 "Zelf een naam bedenken wordt afgeraden.<br/>" + | ||||||
|  |                 "Een beschrijving van het gebied geven kan in een volgende stap.<br/>" + | ||||||
|  |                 "</span>", | ||||||
|  |             freeform: { | ||||||
|  |                 key: "name", | ||||||
|  |                 template: "De naam is $$$", | ||||||
|  |                 renderTemplate: "Dit gebied heet <i>{name}</i>", | ||||||
|  |                 placeholder: "", | ||||||
|  |                 extraTags: new Tag("noname", "") | ||||||
|  |             }, | ||||||
|  |             mappings: [ | ||||||
|  |                 {k: new And([new Tag("name", ""), new Tag("noname", "yes")]), txt: "Dit gebied heeft geen naam"}, | ||||||
|  |             ] | ||||||
|  |         }); | ||||||
|     } |     } | ||||||
|      | 
 | ||||||
| } | } | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| import {Img} from "../../UI/Img"; | import {Img} from "../../UI/Img"; | ||||||
| import {Tag} from "../../Logic/Tags"; | import {RegexTag, Tag} from "../../Logic/Tags"; | ||||||
| import {TagRenderingOptions} from "../TagRenderingOptions"; | import {TagRenderingOptions} from "../TagRenderingOptions"; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -18,7 +18,7 @@ export class OsmLink extends TagRenderingOptions { | ||||||
|             placeholder: "", |             placeholder: "", | ||||||
|         }, |         }, | ||||||
|         mappings: [ |         mappings: [ | ||||||
|             {k: new Tag("id", "node/-1"), txt: "<span class='alert'>Uploading</span>"} |             {k: new RegexTag("id", /node\/-.+/), txt: "<span class='alert'>Uploading</span>"} | ||||||
|         ] |         ] | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -18,17 +18,16 @@ import Translation from "../UI/i18n/Translation"; | ||||||
| import Combine from "../UI/Base/Combine"; | import Combine from "../UI/Base/Combine"; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| export class  | export class TagRendering extends UIElement implements TagDependantUIElement { | ||||||
| TagRendering extends UIElement implements TagDependantUIElement { |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     private readonly _priority: number; |     private readonly _priority: number; | ||||||
|     private readonly _question: string | Translation; |     private readonly _question: string | Translation; | ||||||
|     private readonly _mapping: { k: TagsFilter, txt: string | UIElement, priority?: number }[]; |     private readonly _mapping: { k: TagsFilter, txt: string | UIElement, priority?: number }[]; | ||||||
| 
 | 
 | ||||||
|     private currentTags : UIEventSource<any> ; |     private currentTags: UIEventSource<any>; | ||||||
|      | 
 | ||||||
|      | 
 | ||||||
|     private readonly _freeform: { |     private readonly _freeform: { | ||||||
|         key: string, |         key: string, | ||||||
|         template: string | UIElement, |         template: string | UIElement, | ||||||
|  | @ -110,7 +109,6 @@ TagRendering extends UIElement implements TagDependantUIElement { | ||||||
| 
 | 
 | ||||||
|         for (const choice of options.mappings ?? []) { |         for (const choice of options.mappings ?? []) { | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|             let choiceSubbed = { |             let choiceSubbed = { | ||||||
|                 k: choice.k?.substituteValues(this.currentTags.data), |                 k: choice.k?.substituteValues(this.currentTags.data), | ||||||
|                 txt: choice.txt, |                 txt: choice.txt, | ||||||
|  | @ -225,7 +223,7 @@ TagRendering extends UIElement implements TagDependantUIElement { | ||||||
|                 } |                 } | ||||||
|                 previousTexts.push(this.ApplyTemplate(mapping.txt)); |                 previousTexts.push(this.ApplyTemplate(mapping.txt)); | ||||||
|                  |                  | ||||||
|                 elements.push(this.InputElementForMapping(mapping)); |                 elements.push(this.InputElementForMapping(mapping, mapping.substitute)); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|          |          | ||||||
|  | @ -247,14 +245,26 @@ TagRendering extends UIElement implements TagDependantUIElement { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     private InputElementForMapping(mapping: { k: TagsFilter, txt: (string | Translation) }) { |     private InputElementForMapping(mapping: { k: TagsFilter, txt: (string | Translation) }, substituteValues: boolean) { | ||||||
|         return new FixedInputElement(this.ApplyTemplate(mapping.txt), |         if (substituteValues) { | ||||||
|             mapping.k.substituteValues(this.currentTags.data) | 
 | ||||||
|         ); |             return new FixedInputElement(this.ApplyTemplate(mapping.txt), | ||||||
|  |                 mapping.k.substituteValues(this.currentTags.data), | ||||||
|  |                 (t0, t1) => t0.isEquivalent(t1) | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |         return new FixedInputElement(this.ApplyTemplate(mapping.txt),mapping.k, | ||||||
|  |             (t0, t1) => t0.isEquivalent(t1)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     private InputForFreeForm(freeform): InputElement<TagsFilter> { |     private InputForFreeForm(freeform :  { | ||||||
|  |         key: string, | ||||||
|  |         template: string | Translation, | ||||||
|  |         renderTemplate: string | Translation, | ||||||
|  |         placeholder?: string | Translation, | ||||||
|  |         extraTags?: TagsFilter, | ||||||
|  |     }): InputElement<TagsFilter> { | ||||||
|         if (freeform?.template === undefined) { |         if (freeform?.template === undefined) { | ||||||
|             return undefined; |             return undefined; | ||||||
|         } |         } | ||||||
|  | @ -283,7 +293,7 @@ TagRendering extends UIElement implements TagDependantUIElement { | ||||||
|                 const tag = new Tag(freeform.key, formatter(string, this._source.data._country)); |                 const tag = new Tag(freeform.key, formatter(string, this._source.data._country)); | ||||||
| 
 | 
 | ||||||
|                 if (tag.value.length > 255) { |                 if (tag.value.length > 255) { | ||||||
|                     return undefined; // Toolong
 |                     return undefined; // Too long
 | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 if (freeform.extraTags === undefined) { |                 if (freeform.extraTags === undefined) { | ||||||
|  | @ -299,7 +309,13 @@ TagRendering extends UIElement implements TagDependantUIElement { | ||||||
|         const toString = |         const toString = | ||||||
|             (tag) => { |             (tag) => { | ||||||
|                 if (tag instanceof And) { |                 if (tag instanceof And) { | ||||||
|                     return toString(tag.and[0]) |                     for (const subtag of tag.and) { | ||||||
|  |                         if(subtag instanceof Tag && subtag.key === freeform.key){ | ||||||
|  |                             return subtag.value; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                      | ||||||
|  |                     return undefined; | ||||||
|                 } else if (tag instanceof Tag) { |                 } else if (tag instanceof Tag) { | ||||||
|                     return tag.value |                     return tag.value | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|  | @ -216,21 +216,26 @@ export class FilteredLayer { | ||||||
|             pointToLayer: function (feature, latLng) { |             pointToLayer: function (feature, latLng) { | ||||||
|                 const style = self._style(feature.properties); |                 const style = self._style(feature.properties); | ||||||
|                 let marker; |                 let marker; | ||||||
|                 if (style.icon === undefined) { |                if (style.icon === undefined) { | ||||||
|                     marker = L.circle(latLng, { |                    marker = L.circle(latLng, { | ||||||
|                         radius: 25, |                        radius: 25, | ||||||
|                         color: style.color |                        color: style.color | ||||||
|                     }); |                    }); | ||||||
| 
 | 
 | ||||||
|                 } else { |                } else if (style.icon.iconUrl.startsWith("$circle  ")) { | ||||||
|                     if(style.icon.iconSize === undefined){ |                    marker = L.circle(latLng, { | ||||||
|                         style.icon.iconSize = [50,50] |                        radius: 25, | ||||||
|                     } |                        color: style.color | ||||||
|                      |                    }); | ||||||
|                     marker = L.marker(latLng, { |                } else { | ||||||
|                         icon: new L.icon(style.icon), |                    if (style.icon.iconSize === undefined) { | ||||||
|                     }); |                        style.icon.iconSize = [50, 50] | ||||||
|                 } |                    } | ||||||
|  | 
 | ||||||
|  |                    marker = L.marker(latLng, { | ||||||
|  |                        icon: new L.icon(style.icon), | ||||||
|  |                    }); | ||||||
|  |                } | ||||||
|                 let eventSource = State.state.allElements.addOrGetElement(feature); |                 let eventSource = State.state.allElements.addOrGetElement(feature); | ||||||
|                 const uiElement = self._showOnPopup(eventSource, feature); |                 const uiElement = self._showOnPopup(eventSource, feature); | ||||||
|                 const popup = L.popup({}, marker).setContent(uiElement.Render()); |                 const popup = L.popup({}, marker).setContent(uiElement.Render()); | ||||||
|  | @ -253,11 +258,7 @@ export class FilteredLayer { | ||||||
|                         } |                         } | ||||||
|                     } else { |                     } else { | ||||||
|                         self._geolayer.setStyle(function (featureX) { |                         self._geolayer.setStyle(function (featureX) { | ||||||
|                             const style = self._style(featureX.properties); |                             return self._style(featureX.properties); | ||||||
|                             if (featureX === feature) { |  | ||||||
|                                 console.log("Selected element is", featureX.properties.id) |  | ||||||
|                             } |  | ||||||
|                             return style; |  | ||||||
|                         }); |                         }); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|  | @ -4,7 +4,9 @@ export abstract class TagsFilter { | ||||||
|     abstract matches(tags: { k: string, v: string }[]): boolean |     abstract matches(tags: { k: string, v: string }[]): boolean | ||||||
|     abstract asOverpass(): string[] |     abstract asOverpass(): string[] | ||||||
|     abstract substituteValues(tags: any) : TagsFilter; |     abstract substituteValues(tags: any) : TagsFilter; | ||||||
|     abstract isUsableAsAnswer() : boolean; |     abstract isUsableAsAnswer(): boolean; | ||||||
|  | 
 | ||||||
|  |     abstract isEquivalent(other: TagsFilter): boolean; | ||||||
| 
 | 
 | ||||||
|     matchesProperties(properties: Map<string, string>): boolean { |     matchesProperties(properties: Map<string, string>): boolean { | ||||||
|         return this.matches(TagUtils.proprtiesToKV(properties)); |         return this.matches(TagUtils.proprtiesToKV(properties)); | ||||||
|  | @ -58,16 +60,26 @@ export class RegexTag extends TagsFilter { | ||||||
|         return this.invert; |         return this.invert; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     substituteValues(tags: any) : TagsFilter{ |     substituteValues(tags: any): TagsFilter { | ||||||
|         return this; |         return this; | ||||||
|     } |     } | ||||||
|      | 
 | ||||||
|     asHumanString() { |     asHumanString() { | ||||||
|         if (typeof this.key === "string") { |         if (typeof this.key === "string") { | ||||||
|             return `${this.key}${this.invert ? "!" : ""}~${RegexTag.source(this.value)}`; |             return `${this.key}${this.invert ? "!" : ""}~${RegexTag.source(this.value)}`; | ||||||
|         } |         } | ||||||
|         return `~${this.key.source}${this.invert ? "!" : ""}~${RegexTag.source(this.value)}` |         return `~${this.key.source}${this.invert ? "!" : ""}~${RegexTag.source(this.value)}` | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     isEquivalent(other: TagsFilter): boolean { | ||||||
|  |         if (other instanceof RegexTag) { | ||||||
|  |             return other.asHumanString() == this.asHumanString(); | ||||||
|  |         } | ||||||
|  |         if(other instanceof Tag){ | ||||||
|  |             return RegexTag.doesMatch(other.key, this.key) && RegexTag.doesMatch(other.value, this.value); | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -140,6 +152,16 @@ export class Tag extends TagsFilter { | ||||||
|     isUsableAsAnswer(): boolean { |     isUsableAsAnswer(): boolean { | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |      | ||||||
|  |     isEquivalent(other: TagsFilter): boolean { | ||||||
|  |         if(other instanceof Tag){ | ||||||
|  |             return this.key === other.key && this.value === other.value; | ||||||
|  |         } | ||||||
|  |         if(other instanceof RegexTag){ | ||||||
|  |             other.isEquivalent(this); | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -187,6 +209,24 @@ export class Or extends TagsFilter { | ||||||
|     isUsableAsAnswer(): boolean { |     isUsableAsAnswer(): boolean { | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|  |      | ||||||
|  |     isEquivalent(other: TagsFilter): boolean { | ||||||
|  |         if(other instanceof Or){ | ||||||
|  | 
 | ||||||
|  |             for (const selfTag of this.or) { | ||||||
|  |                 let matchFound = false; | ||||||
|  |                 for (let i = 0; i < other.or.length && !matchFound; i++){ | ||||||
|  |                     let otherTag = other.or[i]; | ||||||
|  |                     matchFound = selfTag.isEquivalent(otherTag); | ||||||
|  |                 } | ||||||
|  |                 if(!matchFound){ | ||||||
|  |                     return false; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -256,6 +296,24 @@ export class And extends TagsFilter { | ||||||
|         } |         } | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |      | ||||||
|  |     isEquivalent(other: TagsFilter): boolean { | ||||||
|  |         if(other instanceof And){ | ||||||
|  | 
 | ||||||
|  |             for (const selfTag of this.and) { | ||||||
|  |                 let matchFound = false; | ||||||
|  |                 for (let i = 0; i < other.and.length && !matchFound; i++){ | ||||||
|  |                     let otherTag = other.and[i]; | ||||||
|  |                     matchFound = selfTag.isEquivalent(otherTag); | ||||||
|  |                 } | ||||||
|  |                 if(!matchFound){ | ||||||
|  |                     return false; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								State.ts
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								State.ts
									
										
									
									
									
								
							|  | @ -23,7 +23,7 @@ export class State { | ||||||
|     // The singleton of the global state
 |     // The singleton of the global state
 | ||||||
|     public static state: State; |     public static state: State; | ||||||
|      |      | ||||||
|     public static vNumber = "0.0.7j"; |     public static vNumber = "0.0.7k"; | ||||||
|      |      | ||||||
|     // The user journey states thresholds when a new feature gets unlocked
 |     // The user journey states thresholds when a new feature gets unlocked
 | ||||||
|     public static userJourney = { |     public static userJourney = { | ||||||
|  |  | ||||||
|  | @ -10,6 +10,8 @@ import {UserDetails} from "../../Logic/Osm/OsmConnection"; | ||||||
| import {MultiInput} from "../Input/MultiInput"; | import {MultiInput} from "../Input/MultiInput"; | ||||||
| import TagRenderingPanel from "./TagRenderingPanel"; | import TagRenderingPanel from "./TagRenderingPanel"; | ||||||
| import SingleSetting from "./SingleSetting"; | import SingleSetting from "./SingleSetting"; | ||||||
|  | import {VariableUiElement} from "../Base/VariableUIElement"; | ||||||
|  | import {FromJSON} from "../../Customizations/JSON/FromJSON"; | ||||||
| 
 | 
 | ||||||
| export default class AllLayersPanel extends UIElement { | export default class AllLayersPanel extends UIElement { | ||||||
| 
 | 
 | ||||||
|  | @ -49,9 +51,22 @@ export default class AllLayersPanel extends UIElement { | ||||||
| 
 | 
 | ||||||
|         const layers = this._config.data.layers; |         const layers = this._config.data.layers; | ||||||
|         for (let i = 0; i < layers.length; i++) { |         for (let i = 0; i < layers.length; i++) { | ||||||
| 
 |  | ||||||
|             tabs.push({ |             tabs.push({ | ||||||
|                 header: "<img src='./assets/bug.svg'>", |                 header: new VariableUiElement(this._config.map((config: LayoutConfigJson) => { | ||||||
|  |                     const layer = config.layers[i]; | ||||||
|  |                     if (typeof layer !== "string") { | ||||||
|  |                         try { | ||||||
|  |                             const iconTagRendering = FromJSON.TagRendering(layer.icon, "icon"); | ||||||
|  |                             const icon = iconTagRendering.GetContent({"id": "node/-1"}).txt; | ||||||
|  |                             return `<img src='${icon}'>` | ||||||
|  |                         } catch (e) { | ||||||
|  |                             return "<img src='./assets/bug.svg'>" | ||||||
|  |                             // Nothing to do here
 | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     return "<img src='./assets/help.svg'>" | ||||||
|  | 
 | ||||||
|  |                 })), | ||||||
|                 content: new LayerPanelWithPreview(this._config, this.languages, i, userDetails) |                 content: new LayerPanelWithPreview(this._config, this.languages, i, userDetails) | ||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ export class GenerateEmpty { | ||||||
|             title: {}, |             title: {}, | ||||||
|             description: {}, |             description: {}, | ||||||
|             tagRenderings: [], |             tagRenderings: [], | ||||||
|  |             hideUnderlayingFeaturesMinPercentage: 0, | ||||||
|             icon: { |             icon: { | ||||||
|                 render: "./assets/bug.svg" |                 render: "./assets/bug.svg" | ||||||
|             }, |             }, | ||||||
|  |  | ||||||
|  | @ -96,7 +96,10 @@ export default class LayerPanel extends UIElement { | ||||||
|                         {value: 2, shown: "Show both the ways/areas and the centerpoints"}, |                         {value: 2, shown: "Show both the ways/areas and the centerpoints"}, | ||||||
|                         {value: 1, shown: "Show everything as centerpoint"}]), "wayHandling", "Way handling", |                         {value: 1, shown: "Show everything as centerpoint"}]), "wayHandling", "Way handling", | ||||||
|                     "Describes how ways and areas are represented on the map: areas can be represented as the area itself, or it can be converted into the centerpoint"), |                     "Describes how ways and areas are represented on the map: areas can be represented as the area itself, or it can be converted into the centerpoint"), | ||||||
| 
 |                 setting(TextField.NumberInput("nat", n => n <= 100), "hideUnderlayingFeaturesMinPercentage", "Max allowed overlap percentage", | ||||||
|  |                     "Consider that we want to show 'Nature Reserves' and 'Forests'. Now, ofter, there are pieces of forest mapped _in_ the nature reserve.<br/>" + | ||||||
|  |                     "Now, showing those pieces of forest overlapping with the nature reserve truly clutters the map and is very user-unfriendly.<br/>" + | ||||||
|  |                     "The features are placed layer by layer. If a feature below a feature on this layer overlaps for more then 'x'-percent, the underlying feature is hidden."), | ||||||
|                 setting(new AndOrTagInput(), "overpassTags", "Overpass query", |                 setting(new AndOrTagInput(), "overpassTags", "Overpass query", | ||||||
|                     "The tags of the objects to load from overpass"), |                     "The tags of the objects to load from overpass"), | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -13,7 +13,6 @@ import {UserDetails} from "../../Logic/Osm/OsmConnection"; | ||||||
| 
 | 
 | ||||||
| export default class LayerPanelWithPreview extends UIElement{ | export default class LayerPanelWithPreview extends UIElement{ | ||||||
|     private panel: UIElement; |     private panel: UIElement; | ||||||
|      |  | ||||||
|     constructor(config: UIEventSource<any>, languages: UIEventSource<string[]>, index: number, userDetails: UserDetails) { |     constructor(config: UIEventSource<any>, languages: UIEventSource<string[]>, index: number, userDetails: UserDetails) { | ||||||
|         super(); |         super(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -91,7 +91,7 @@ export default class TagRenderingPanel extends InputElement<TagRenderingConfigJs | ||||||
| 
 | 
 | ||||||
|             "<h3>Mappings</h3>", |             "<h3>Mappings</h3>", | ||||||
|             setting(new MultiInput<{ if: AndOrTagConfigJson, then: (string | any), hideInAnswer?: boolean }>("Add a mapping", |             setting(new MultiInput<{ if: AndOrTagConfigJson, then: (string | any), hideInAnswer?: boolean }>("Add a mapping", | ||||||
|                 () => ({if: undefined, then: undefined}), |                 () => ({if: {and: []}, then: {}}), | ||||||
|                 () => new MappingInput(languages, options?.disableQuestions ?? false), |                 () => new MappingInput(languages, options?.disableQuestions ?? false), | ||||||
|                 undefined, {allowMovement: true}), "mappings", |                 undefined, {allowMovement: true}), "mappings", | ||||||
|                 "If a tag matches, then show the first respective text", "") |                 "If a tag matches, then show the first respective text", "") | ||||||
|  |  | ||||||
|  | @ -7,9 +7,13 @@ export class FixedInputElement<T> extends InputElement<T> { | ||||||
|     private readonly rendering: UIElement; |     private readonly rendering: UIElement; | ||||||
|     private readonly value: UIEventSource<T>; |     private readonly value: UIEventSource<T>; | ||||||
|     public readonly IsSelected : UIEventSource<boolean> = new UIEventSource<boolean>(false); |     public readonly IsSelected : UIEventSource<boolean> = new UIEventSource<boolean>(false); | ||||||
|  |     private readonly _comparator: (t0: T, t1: T) => boolean; | ||||||
| 
 | 
 | ||||||
|     constructor(rendering: UIElement | string, value: T) { |     constructor(rendering: UIElement | string,  | ||||||
|  |                 value: T, | ||||||
|  |                 comparator: ((t0: T, t1: T) => boolean ) = undefined) { | ||||||
|         super(undefined); |         super(undefined); | ||||||
|  |         this._comparator = comparator ?? ((t0, t1) => t0 == t1);  | ||||||
|         this.value = new UIEventSource<T>(value); |         this.value = new UIEventSource<T>(value); | ||||||
|         this.rendering = typeof (rendering) === 'string' ? new FixedUiElement(rendering) : rendering; |         this.rendering = typeof (rendering) === 'string' ? new FixedUiElement(rendering) : rendering; | ||||||
|     } |     } | ||||||
|  | @ -22,7 +26,9 @@ export class FixedInputElement<T> extends InputElement<T> { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     IsValid(t: T): boolean { |     IsValid(t: T): boolean { | ||||||
|         return t == this.value.data; |          | ||||||
|  |         console.log("Comparing ",t, "with", this.value.data); | ||||||
|  |         return this._comparator(t, this.value.data); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     protected InnerUpdate(htmlElement: HTMLElement) { |     protected InnerUpdate(htmlElement: HTMLElement) { | ||||||
|  |  | ||||||
|  | @ -8,14 +8,15 @@ export class RadioButton<T> extends InputElement<T> { | ||||||
|     private readonly _selectedElementIndex: UIEventSource<number> |     private readonly _selectedElementIndex: UIEventSource<number> | ||||||
|         = new UIEventSource<number>(null); |         = new UIEventSource<number>(null); | ||||||
| 
 | 
 | ||||||
|     private value: UIEventSource<T>; |     private readonly value: UIEventSource<T>; | ||||||
|     private readonly _elements: InputElement<T>[] |     private readonly _elements: InputElement<T>[] | ||||||
|     private _selectFirstAsDefault: boolean; |     private readonly _selectFirstAsDefault: boolean; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     constructor(elements: InputElement<T>[], |     constructor(elements: InputElement<T>[], | ||||||
|                 selectFirstAsDefault = true) { |                 selectFirstAsDefault = true) { | ||||||
|         super(undefined); |         super(undefined); | ||||||
|  |         console.log("Created new radiobutton with values ", elements) | ||||||
|         this._elements = Utils.NoNull(elements); |         this._elements = Utils.NoNull(elements); | ||||||
|         this._selectFirstAsDefault = selectFirstAsDefault; |         this._selectFirstAsDefault = selectFirstAsDefault; | ||||||
|         const self = this; |         const self = this; | ||||||
|  |  | ||||||
|  | @ -54,6 +54,9 @@ export class SimpleAddUI extends UIElement { | ||||||
|                     if (typeof (preset.icon) !== "string") { |                     if (typeof (preset.icon) !== "string") { | ||||||
|                         const tags = Utils.MergeTags(TagUtils.KVtoProperties(preset.tags), {id:"node/-1"}); |                         const tags = Utils.MergeTags(TagUtils.KVtoProperties(preset.tags), {id:"node/-1"}); | ||||||
|                         icon = preset.icon.GetContent(tags).txt; |                         icon = preset.icon.GetContent(tags).txt; | ||||||
|  |                         if(icon.startsWith("$")){ | ||||||
|  |                             icon = undefined; | ||||||
|  |                         } | ||||||
|                     } else { |                     } else { | ||||||
|                         icon = preset.icon; |                         icon = preset.icon; | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
|  | @ -13,6 +13,8 @@ export abstract class UIElement extends UIEventSource<string> { | ||||||
|     private _hideIfEmpty = false; |     private _hideIfEmpty = false; | ||||||
| 
 | 
 | ||||||
|     public dumbMode = false; |     public dumbMode = false; | ||||||
|  |      | ||||||
|  |     private lastInnerRender: string; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * In the 'deploy'-step, some code needs to be run by ts-node. |      * In the 'deploy'-step, some code needs to be run by ts-node. | ||||||
|  | @ -37,6 +39,7 @@ export abstract class UIElement extends UIEventSource<string> { | ||||||
|         this.dumbMode = false; |         this.dumbMode = false; | ||||||
|         const self = this; |         const self = this; | ||||||
|         source.addCallback(() => { |         source.addCallback(() => { | ||||||
|  |             self.lastInnerRender = undefined; | ||||||
|             self.Update(); |             self.Update(); | ||||||
|         }) |         }) | ||||||
|         return this; |         return this; | ||||||
|  | @ -92,7 +95,7 @@ export abstract class UIElement extends UIEventSource<string> { | ||||||
| 
 | 
 | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         this.setData(this.InnerRender()); |         this.setData(this.lastInnerRender ?? this.InnerRender()); | ||||||
|         element.innerHTML = this.data; |         element.innerHTML = this.data; | ||||||
| 
 | 
 | ||||||
|         if (this._hideIfEmpty) { |         if (this._hideIfEmpty) { | ||||||
|  | @ -151,14 +154,15 @@ export abstract class UIElement extends UIEventSource<string> { | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|     Render(): string { |     Render(): string { | ||||||
|  |         this.lastInnerRender = this.lastInnerRender ?? this.InnerRender(); | ||||||
|         if (this.dumbMode) { |         if (this.dumbMode) { | ||||||
|             return this.InnerRender(); |             return this.lastInnerRender; | ||||||
|         } |         } | ||||||
|         let style = ""; |         let style = ""; | ||||||
|         if (this.style !== undefined && this.style !== "") { |         if (this.style !== undefined && this.style !== "") { | ||||||
|             style = `style="${this.style}"`; |             style = `style="${this.style}"`; | ||||||
|         } |         } | ||||||
|         return `<span class='uielement ${this.clss.join(" ")}' ${style} id='${this.id}'>${this.InnerRender()}</span>` |         return `<span class='uielement ${this.clss.join(" ")}' ${style} id='${this.id}'>${this.lastInnerRender}</span>` | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     AttachTo(divId: string) { |     AttachTo(divId: string) { | ||||||
|  |  | ||||||
|  | @ -52,7 +52,12 @@ | ||||||
|         "if": { |         "if": { | ||||||
|           "and": [ |           "and": [ | ||||||
|             "service:bicycle:pump:operational_status=broken", |             "service:bicycle:pump:operational_status=broken", | ||||||
|             "service:bicycle:tools=no" |             { | ||||||
|  |               "or": [ | ||||||
|  |                 "service:bicycle:tools=no", | ||||||
|  |                 "service:bicycle:tools=" | ||||||
|  |               ] | ||||||
|  |             } | ||||||
|           ] |           ] | ||||||
|         }, |         }, | ||||||
|         "then": { |         "then": { | ||||||
|  | @ -381,7 +386,12 @@ | ||||||
|         "if": { |         "if": { | ||||||
|           "and": [ |           "and": [ | ||||||
|             "service:bicycle:pump=yes", |             "service:bicycle:pump=yes", | ||||||
|             "service:bicycle:tools=no" |             { | ||||||
|  |               "or": [ | ||||||
|  |                 "service:bicycle:tools=no", | ||||||
|  |                 "service:bicycle:tools=" | ||||||
|  |               ] | ||||||
|  |             } | ||||||
|           ] |           ] | ||||||
|         }, |         }, | ||||||
|         "then": "./assets/layers/bike_repair_station/pump.svg" |         "then": "./assets/layers/bike_repair_station/pump.svg" | ||||||
|  |  | ||||||
							
								
								
									
										252
									
								
								assets/layers/nature_reserve/nature_reserve.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										252
									
								
								assets/layers/nature_reserve/nature_reserve.json
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,252 @@ | ||||||
|  | { | ||||||
|  |   "id": "nature_reserve_simple", | ||||||
|  |   "name": "Layer", | ||||||
|  |   "minzoom": 10, | ||||||
|  |   "overpassTags": { | ||||||
|  |     "or": [ | ||||||
|  |       "leisure=nature_reserve", | ||||||
|  |       "boundary=protected_area" | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   "title": { | ||||||
|  |     "mappings": [ | ||||||
|  |       { | ||||||
|  |         "if": { | ||||||
|  |           "and": [ | ||||||
|  |             "name~*" | ||||||
|  |           ] | ||||||
|  |         }, | ||||||
|  |         "then": { | ||||||
|  |           "nl": "Natuurgebied <i>{name}</i>" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     ], | ||||||
|  |     "render": { | ||||||
|  |       "nl": "Natuurgebied" | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   "description": { | ||||||
|  |     "nl": "Een natuurreservaat is een gebied dat wordt beheerd door Natuurpunt, ANB of een privépersoon zodat deze biodiversiteit bevordert. " | ||||||
|  |   }, | ||||||
|  |   "tagRenderings": [ | ||||||
|  |     { | ||||||
|  |       "question": { | ||||||
|  |         "nl": "Is dit gebied vrij toegankelijk?" | ||||||
|  |       }, | ||||||
|  |       "freeform": { | ||||||
|  |         "key": "access:description", | ||||||
|  |         "addExtraTags": [] | ||||||
|  |       }, | ||||||
|  |       "render": { | ||||||
|  |         "nl": "De toegankelijkheid van dit gebied is {access:description}" | ||||||
|  |       }, | ||||||
|  |       "mappings": [ | ||||||
|  |         { | ||||||
|  |           "if": { | ||||||
|  |             "and": [ | ||||||
|  |               "access=yes", | ||||||
|  |               "fee=" | ||||||
|  |             ] | ||||||
|  |           }, | ||||||
|  |           "then": { | ||||||
|  |             "nl": "Publiek toegankelijk" | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           "if": { | ||||||
|  |             "and": [ | ||||||
|  |               "access=no", | ||||||
|  |               "fee=" | ||||||
|  |             ] | ||||||
|  |           }, | ||||||
|  |           "then": { | ||||||
|  |             "nl": "Niet publiek toegankelijk" | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           "if": { | ||||||
|  |             "and": [ | ||||||
|  |               "access=guided", | ||||||
|  |               "fee=" | ||||||
|  |             ] | ||||||
|  |           }, | ||||||
|  |           "then": { | ||||||
|  |             "nl": "Enkel met gids of op activiteit" | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           "if": { | ||||||
|  |             "and": [ | ||||||
|  |               "access=private", | ||||||
|  |               "fee=" | ||||||
|  |             ] | ||||||
|  |           }, | ||||||
|  |           "then": { | ||||||
|  |             "nl": "Niet toegankelijk privégebied" | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           "if": { | ||||||
|  |             "and": [ | ||||||
|  |               "access=permissive", | ||||||
|  |               "fee=" | ||||||
|  |             ] | ||||||
|  |           }, | ||||||
|  |           "then": { | ||||||
|  |             "nl": "Toegankelijk, maar het is privégebied" | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           "if": { | ||||||
|  |             "and": [ | ||||||
|  |               "access=yes", | ||||||
|  |               "fee=yes" | ||||||
|  |             ] | ||||||
|  |           }, | ||||||
|  |           "then": { | ||||||
|  |             "nl": "Toegankelijk mits betaling" | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       ] | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "render": { | ||||||
|  |         "nl": "Beheer door {operator}" | ||||||
|  |       }, | ||||||
|  |       "question": { | ||||||
|  |         "nl": "Wie beheert dit natuurgebied?" | ||||||
|  |       }, | ||||||
|  |       "freeform": { | ||||||
|  |         "key": "operator" | ||||||
|  |       }, | ||||||
|  |       "mappings": [ | ||||||
|  |         { | ||||||
|  |           "if": { | ||||||
|  |             "and": [ | ||||||
|  |               "operator=Natuurpunt" | ||||||
|  |             ] | ||||||
|  |           }, | ||||||
|  |           "then": { | ||||||
|  |             "nl": "Beheer door Natuurpunt" | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           "if": { | ||||||
|  |             "and": [ | ||||||
|  |               "operator=Agenstchap Natuur en Bos" | ||||||
|  |             ] | ||||||
|  |           }, | ||||||
|  |           "then": { | ||||||
|  |             "nl": "Beheer door het Agentschap Natuur en Bos (ANB)" | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       ] | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "question": { | ||||||
|  |         "nl": "<b>Wat is de <i>officiële</i> naam van dit natuurgebied?</b><br/><span class='subtle'>Sommige gebieden hebben geen naam</span>" | ||||||
|  |       }, | ||||||
|  |       "freeform": { | ||||||
|  |         "key": "name", | ||||||
|  |         "addExtraTags": [ | ||||||
|  |           "noname=" | ||||||
|  |         ] | ||||||
|  |       }, | ||||||
|  |       "render": { | ||||||
|  |         "nl": "Dit gebied heet <i>{name}</i>" | ||||||
|  |       }, | ||||||
|  |       "mappings": [ | ||||||
|  |         { | ||||||
|  |           "if": { | ||||||
|  |             "and": [ | ||||||
|  |               "noname=yes", | ||||||
|  |               "name=" | ||||||
|  |             ] | ||||||
|  |           }, | ||||||
|  |           "then": { | ||||||
|  |             "nl": "Dit gebied heeft geen naam" | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       ], | ||||||
|  |       "condition": { | ||||||
|  |         "and": [ | ||||||
|  |           "name:nl=" | ||||||
|  |         ] | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "render": { | ||||||
|  |         "nl": "De naam van dit gebied is {name:nl}" | ||||||
|  |       }, | ||||||
|  |       "freeform": { | ||||||
|  |         "key": "name:nl" | ||||||
|  |       }, | ||||||
|  |       "question": { | ||||||
|  |         "nl": "Wat is de Nederlandstalige naam van dit gebied?" | ||||||
|  |       }, | ||||||
|  |       "condition": { | ||||||
|  |         "and": [ | ||||||
|  |           "name:nl~*" | ||||||
|  |         ] | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "render": { | ||||||
|  |         "nl": "Meer uitleg:<br/><i>{description:0}</i>" | ||||||
|  |       }, | ||||||
|  |       "question": { | ||||||
|  |         "nl": "Zijn er nog opmerkingen of vermeldenswaardigheden?" | ||||||
|  |       }, | ||||||
|  |       "freeform": { | ||||||
|  |         "key": "description:0" | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   ], | ||||||
|  |   "icon": { | ||||||
|  |     "render": "$circle" | ||||||
|  |   }, | ||||||
|  |   "width": { | ||||||
|  |     "render": "3" | ||||||
|  |   }, | ||||||
|  |   "iconSize": { | ||||||
|  |     "render": "40,40,center" | ||||||
|  |   }, | ||||||
|  |   "color": { | ||||||
|  |     "render": "#c90014", | ||||||
|  |     "mappings": [ | ||||||
|  |       { | ||||||
|  |         "if": { | ||||||
|  |           "and": [ | ||||||
|  |             "name~*", | ||||||
|  |             "operator~*", | ||||||
|  |             "access~*" | ||||||
|  |           ] | ||||||
|  |         }, | ||||||
|  |         "then": "#37c65b" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "if": { | ||||||
|  |           "and": [ | ||||||
|  |             "name~*", | ||||||
|  |             "access~*" | ||||||
|  |           ] | ||||||
|  |         }, | ||||||
|  |         "then": "#c98d00" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   "presets": [ | ||||||
|  |     { | ||||||
|  |       "tags": [ | ||||||
|  |         "leisure=nature_reserve" | ||||||
|  |       ], | ||||||
|  |       "title": { | ||||||
|  |         "nl": "Natuurreservaat" | ||||||
|  |       }, | ||||||
|  |       "description": { | ||||||
|  |         "nl": "Voeg een ontbrekend, erkend natuurreservaat toe, bv. een gebied dat beheerd wordt door het ANB of natuurpunt" | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   ], | ||||||
|  |   "hideUnderlayingFeaturesMinPercentage": 10 | ||||||
|  | } | ||||||
|  | @ -54,14 +54,22 @@ new T([ | ||||||
|                 } |                 } | ||||||
|             ], |             ], | ||||||
|             condition: "x=" |             condition: "x=" | ||||||
|         }); |         }, ""); | ||||||
| 
 | 
 | ||||||
|         equal(true, tr.IsKnown({"noname": "yes"})); |         equal(true, tr.IsKnown({"noname": "yes"})); | ||||||
|         equal(true, tr.IsKnown({"name": "ABC"})); |         equal(true, tr.IsKnown({"name": "ABC"})); | ||||||
|         equal(false, tr.IsKnown({"foo": "bar"})); |         equal(false, tr.IsKnown({"foo": "bar"})); | ||||||
|         equal("Has no name", tr.GetContent({"noname": "yes"})); |         equal("Has no name", tr.GetContent({"noname": "yes"})?.txt); | ||||||
|         equal("Ook een xyz", tr.GetContent({"name": "xyz"})); |         equal("Ook een xyz", tr.GetContent({"name": "xyz"})?.txt); | ||||||
|         equal("Ook een {name}", tr.GetContent({"foo": "bar"})); |         equal(undefined, tr.GetContent({"foo": "bar"})); | ||||||
| 
 | 
 | ||||||
|     })] |     })], | ||||||
|  |     [ | ||||||
|  |         "Select right value test", | ||||||
|  |         () => { | ||||||
|  |      | ||||||
|  |         } | ||||||
|  |     ] | ||||||
|  |      | ||||||
|  |      | ||||||
| ]); | ]); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue