diff --git a/Customizations/JSON/FromJSON.ts b/Customizations/JSON/FromJSON.ts
index cbba4b67f9..b3901272b0 100644
--- a/Customizations/JSON/FromJSON.ts
+++ b/Customizations/JSON/FromJSON.ts
@@ -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_repair_station from "../../assets/layers/bike_repair_station/bike_repair_station.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";
@@ -34,6 +35,7 @@ export class FromJSON {
FromJSON.Layer(bike_parking),
FromJSON.Layer(bike_repair_station),
FromJSON.Layer(birdhides),
+ FromJSON.Layer(nature_reserve),
];
for (const layer of sharedLayersList) {
@@ -102,7 +104,6 @@ export class FromJSON {
public static TagRenderingWithDefault(json: TagRenderingConfigJson | string, propertyName, defaultValue: string): TagDependantUIElementConstructor {
if (json === undefined) {
if(defaultValue !== undefined){
- console.log(`Using default value ${defaultValue} for ${propertyName}`)
return FromJSON.TagRendering(defaultValue, propertyName);
}
throw `Tagrendering ${propertyName} is undefined...`
@@ -207,7 +208,7 @@ export class FromJSON {
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){
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 {
- console.log("Parsing layer", json)
const tr = FromJSON.Translation;
const overpassTags = FromJSON.Tag(json.overpassTags, "overpasstags for layer "+json.id);
const icon = FromJSON.TagRenderingWithDefault(json.icon, "icon", "./assets/bug.svg");
@@ -381,6 +381,7 @@ export class FromJSON {
}
);
+ layer.maxAllowedOverlapPercentage = json.hideUnderlayingFeaturesMinPercentage;
return layer;
}
diff --git a/Customizations/JSON/LayerConfigJson.ts b/Customizations/JSON/LayerConfigJson.ts
index b8f488b63d..1eac673773 100644
--- a/Customizations/JSON/LayerConfigJson.ts
+++ b/Customizations/JSON/LayerConfigJson.ts
@@ -70,6 +70,14 @@ export interface LayerConfigJson {
*/
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
*/
diff --git a/Customizations/LayerDefinition.ts b/Customizations/LayerDefinition.ts
index a17b7de32a..10b5d3ec3d 100644
--- a/Customizations/LayerDefinition.ts
+++ b/Customizations/LayerDefinition.ts
@@ -129,47 +129,5 @@ export class LayerDefinition {
this.style = options.style;
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)
- }*/
+
}
\ No newline at end of file
diff --git a/Customizations/Layers/Bos.ts b/Customizations/Layers/Bos.ts
index 28a418448b..d0027428dc 100644
--- a/Customizations/Layers/Bos.ts
+++ b/Customizations/Layers/Bos.ts
@@ -35,7 +35,7 @@ export class Bos extends LayerDefinition {
this.minzoom = 13;
this.style = this.generateStyleFunction();
- this.title = new NameInline("bos");
+ this.title = new NameInline("Bos");
this.elementsToShow = [
new ImageCarouselWithUploadConstructor(),
new NameQuestion(),
diff --git a/Customizations/Layers/NatureReserves.ts b/Customizations/Layers/NatureReserves.ts
index 6beb53a992..3725f849a2 100644
--- a/Customizations/Layers/NatureReserves.ts
+++ b/Customizations/Layers/NatureReserves.ts
@@ -26,7 +26,7 @@ export class NatureReserves extends LayerDefinition {
}
];
this.minzoom = 13;
- this.title = new NameInline("natuurreservaat");
+ this.title = new NameInline("Natuurreservaat");
this.style = this.generateStyleFunction();
this.elementsToShow = [
new ImageCarouselWithUploadConstructor(),
diff --git a/Customizations/Layers/Park.ts b/Customizations/Layers/Park.ts
index ff1f7a61d8..6fe87f8ed4 100644
--- a/Customizations/Layers/Park.ts
+++ b/Customizations/Layers/Park.ts
@@ -13,7 +13,7 @@ export class Park extends LayerDefinition {
question: "Is dit park publiek toegankelijk?",
mappings: [
{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", "private"), txt: "Niet publiek toegankelijk, want privaat"},
{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.style = this.generateStyleFunction();
- this.title = new NameInline("park");
+ this.title = new NameInline("Park");
this.elementsToShow = [
new ImageCarouselWithUploadConstructor(),
new NameQuestion(),
diff --git a/Customizations/Layers/Widths.ts b/Customizations/Layers/Widths.ts
index 22995e7087..f873ef0c4b 100644
--- a/Customizations/Layers/Widths.ts
+++ b/Customizations/Layers/Widths.ts
@@ -168,12 +168,12 @@ export class Widths extends LayerDefinition {
let dashArray = undefined;
if (props.onewayBike) {
- dashArray = [20, 8]
+ dashArray = [5, 6]
}
return {
icon: null,
color: c,
- weight: 9,
+ weight: 5,
dashArray: dashArray
}
}
diff --git a/Customizations/Layouts/Groen.ts b/Customizations/Layouts/Groen.ts
index 97b92b8874..103f4795e3 100644
--- a/Customizations/Layouts/Groen.ts
+++ b/Customizations/Layouts/Groen.ts
@@ -1,7 +1,7 @@
-import {NatureReserves} from "../Layers/NatureReserves";
import {Park} from "../Layers/Park";
import {Bos} from "../Layers/Bos";
import {Layout} from "../Layout";
+import {NatureReserves} from "../Layers/NatureReserves";
export class Groen extends Layout {
diff --git a/Customizations/OnlyShowIf.ts b/Customizations/OnlyShowIf.ts
index d439f07de5..0fe632dbf0 100644
--- a/Customizations/OnlyShowIf.ts
+++ b/Customizations/OnlyShowIf.ts
@@ -41,7 +41,7 @@ export class OnlyShowIfConstructor implements TagDependantUIElementConstructor{
}
GetContent(tags: any): Translation {
- if(this.IsKnown(tags)){
+ if(!this.IsKnown(tags)){
return undefined;
}
return this._embedded.GetContent(tags);
diff --git a/Customizations/Questions/AccessTag.ts b/Customizations/Questions/AccessTag.ts
index 8190cdc8d7..6375962272 100644
--- a/Customizations/Questions/AccessTag.ts
+++ b/Customizations/Questions/AccessTag.ts
@@ -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", "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", "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"),
new Tag("fee", "yes")]),
diff --git a/Customizations/Questions/NameInline.ts b/Customizations/Questions/NameInline.ts
index 6d5350840f..5a2c280a60 100644
--- a/Customizations/Questions/NameInline.ts
+++ b/Customizations/Questions/NameInline.ts
@@ -1,4 +1,4 @@
-import {Tag} from "../../Logic/Tags";
+import {RegexTag, Tag} from "../../Logic/Tags";
import Translations from "../../UI/i18n/Translations";
import {TagRenderingOptions} from "../TagRenderingOptions";
import Translation from "../../UI/i18n/Translation";
@@ -8,18 +8,10 @@ export class NameInline extends TagRenderingOptions{
constructor(category: string | Translation ) {
super({
- question: "",
-
- freeform: {
- renderTemplate: "{name}",
- template: Translations.t.general.nameInlineQuestion.Subs({category: category}),
- key: "name",
- extraTags: new Tag("noname", "") // Remove 'noname=yes'
- },
-
mappings: [
- {k: new Tag("noname","yes"), txt: Translations.t.general.noNameCategory.Subs({category: category})},
- {k: null, txt: category}
+ {k: new Tag("noname", "yes"), txt: Translations.t.general.noNameCategory.Subs({category: category})},
+ {k: new RegexTag("name", /.+/), txt: "{name}"},
+ {k:new Tag("name",""), txt: category}
]
});
}
diff --git a/Customizations/Questions/NameQuestion.ts b/Customizations/Questions/NameQuestion.ts
index 7b54f8cbc1..a8301a40e4 100644
--- a/Customizations/Questions/NameQuestion.ts
+++ b/Customizations/Questions/NameQuestion.ts
@@ -3,31 +3,29 @@
* One is a big 'name-question', the other is the 'edit name' in the title.
* THis one is the big question
*/
-import {Tag} from "../../Logic/Tags";
+import {And, Tag} from "../../Logic/Tags";
import {TagRenderingOptions} from "../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 officiële naam van dit gebied?
" +
- "Zelf een naam bedenken wordt afgeraden.
" +
- "Een beschrijving van het gebied geven kan in een volgende stap.
" +
- "",
- 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"},
- ]
- }
-
+export class NameQuestion extends TagRenderingOptions {
+
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 officiële naam van dit gebied?
" +
+ "Zelf een naam bedenken wordt afgeraden.
" +
+ "Een beschrijving van het gebied geven kan in een volgende stap.
" +
+ "",
+ freeform: {
+ key: "name",
+ template: "De naam is $$$",
+ renderTemplate: "Dit gebied heet {name}",
+ placeholder: "",
+ extraTags: new Tag("noname", "")
+ },
+ mappings: [
+ {k: new And([new Tag("name", ""), new Tag("noname", "yes")]), txt: "Dit gebied heeft geen naam"},
+ ]
+ });
}
-
+
}
\ No newline at end of file
diff --git a/Customizations/Questions/OsmLink.ts b/Customizations/Questions/OsmLink.ts
index 97d8821088..fdcf5e71a1 100644
--- a/Customizations/Questions/OsmLink.ts
+++ b/Customizations/Questions/OsmLink.ts
@@ -1,5 +1,5 @@
import {Img} from "../../UI/Img";
-import {Tag} from "../../Logic/Tags";
+import {RegexTag, Tag} from "../../Logic/Tags";
import {TagRenderingOptions} from "../TagRenderingOptions";
@@ -18,7 +18,7 @@ export class OsmLink extends TagRenderingOptions {
placeholder: "",
},
mappings: [
- {k: new Tag("id", "node/-1"), txt: "Uploading"}
+ {k: new RegexTag("id", /node\/-.+/), txt: "Uploading"}
]
}
diff --git a/Customizations/TagRendering.ts b/Customizations/TagRendering.ts
index 34c0817383..c1b21ee8d0 100644
--- a/Customizations/TagRendering.ts
+++ b/Customizations/TagRendering.ts
@@ -18,17 +18,16 @@ import Translation from "../UI/i18n/Translation";
import Combine from "../UI/Base/Combine";
-export class
-TagRendering extends UIElement implements TagDependantUIElement {
+export class TagRendering extends UIElement implements TagDependantUIElement {
private readonly _priority: number;
private readonly _question: string | Translation;
private readonly _mapping: { k: TagsFilter, txt: string | UIElement, priority?: number }[];
- private currentTags : UIEventSource ;
-
-
+ private currentTags: UIEventSource;
+
+
private readonly _freeform: {
key: string,
template: string | UIElement,
@@ -110,7 +109,6 @@ TagRendering extends UIElement implements TagDependantUIElement {
for (const choice of options.mappings ?? []) {
-
let choiceSubbed = {
k: choice.k?.substituteValues(this.currentTags.data),
txt: choice.txt,
@@ -225,7 +223,7 @@ TagRendering extends UIElement implements TagDependantUIElement {
}
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) }) {
- return new FixedInputElement(this.ApplyTemplate(mapping.txt),
- mapping.k.substituteValues(this.currentTags.data)
- );
+ private InputElementForMapping(mapping: { k: TagsFilter, txt: (string | Translation) }, substituteValues: boolean) {
+ if (substituteValues) {
+
+ 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 {
+ private InputForFreeForm(freeform : {
+ key: string,
+ template: string | Translation,
+ renderTemplate: string | Translation,
+ placeholder?: string | Translation,
+ extraTags?: TagsFilter,
+ }): InputElement {
if (freeform?.template === undefined) {
return undefined;
}
@@ -283,7 +293,7 @@ TagRendering extends UIElement implements TagDependantUIElement {
const tag = new Tag(freeform.key, formatter(string, this._source.data._country));
if (tag.value.length > 255) {
- return undefined; // Toolong
+ return undefined; // Too long
}
if (freeform.extraTags === undefined) {
@@ -299,7 +309,13 @@ TagRendering extends UIElement implements TagDependantUIElement {
const toString =
(tag) => {
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) {
return tag.value
}
diff --git a/Logic/FilteredLayer.ts b/Logic/FilteredLayer.ts
index ebb1db19b5..8ce39f9f5d 100644
--- a/Logic/FilteredLayer.ts
+++ b/Logic/FilteredLayer.ts
@@ -216,21 +216,26 @@ export class FilteredLayer {
pointToLayer: function (feature, latLng) {
const style = self._style(feature.properties);
let marker;
- if (style.icon === undefined) {
- marker = L.circle(latLng, {
- radius: 25,
- color: style.color
- });
+ if (style.icon === undefined) {
+ marker = L.circle(latLng, {
+ radius: 25,
+ color: style.color
+ });
- } else {
- if(style.icon.iconSize === undefined){
- style.icon.iconSize = [50,50]
- }
-
- marker = L.marker(latLng, {
- icon: new L.icon(style.icon),
- });
- }
+ } else if (style.icon.iconUrl.startsWith("$circle ")) {
+ marker = L.circle(latLng, {
+ radius: 25,
+ color: style.color
+ });
+ } else {
+ 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);
const uiElement = self._showOnPopup(eventSource, feature);
const popup = L.popup({}, marker).setContent(uiElement.Render());
@@ -253,11 +258,7 @@ export class FilteredLayer {
}
} else {
self._geolayer.setStyle(function (featureX) {
- const style = self._style(featureX.properties);
- if (featureX === feature) {
- console.log("Selected element is", featureX.properties.id)
- }
- return style;
+ return self._style(featureX.properties);
});
}
}
diff --git a/Logic/Tags.ts b/Logic/Tags.ts
index df8a280d12..a0afad8a73 100644
--- a/Logic/Tags.ts
+++ b/Logic/Tags.ts
@@ -4,7 +4,9 @@ export abstract class TagsFilter {
abstract matches(tags: { k: string, v: string }[]): boolean
abstract asOverpass(): string[]
abstract substituteValues(tags: any) : TagsFilter;
- abstract isUsableAsAnswer() : boolean;
+ abstract isUsableAsAnswer(): boolean;
+
+ abstract isEquivalent(other: TagsFilter): boolean;
matchesProperties(properties: Map): boolean {
return this.matches(TagUtils.proprtiesToKV(properties));
@@ -58,16 +60,26 @@ export class RegexTag extends TagsFilter {
return this.invert;
}
- substituteValues(tags: any) : TagsFilter{
+ substituteValues(tags: any): TagsFilter {
return this;
}
-
+
asHumanString() {
if (typeof this.key === "string") {
return `${this.key}${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 {
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 {
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;
}
+
+ 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;
+ }
}
diff --git a/State.ts b/State.ts
index 6a9431626f..416928f522 100644
--- a/State.ts
+++ b/State.ts
@@ -23,7 +23,7 @@ export class State {
// The singleton of the global 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
public static userJourney = {
diff --git a/UI/CustomGenerator/AllLayersPanel.ts b/UI/CustomGenerator/AllLayersPanel.ts
index cf33733cb6..2e28b020c5 100644
--- a/UI/CustomGenerator/AllLayersPanel.ts
+++ b/UI/CustomGenerator/AllLayersPanel.ts
@@ -10,6 +10,8 @@ import {UserDetails} from "../../Logic/Osm/OsmConnection";
import {MultiInput} from "../Input/MultiInput";
import TagRenderingPanel from "./TagRenderingPanel";
import SingleSetting from "./SingleSetting";
+import {VariableUiElement} from "../Base/VariableUIElement";
+import {FromJSON} from "../../Customizations/JSON/FromJSON";
export default class AllLayersPanel extends UIElement {
@@ -49,9 +51,22 @@ export default class AllLayersPanel extends UIElement {
const layers = this._config.data.layers;
for (let i = 0; i < layers.length; i++) {
-
tabs.push({
- header: "
",
+ 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 `
`
+ } catch (e) {
+ return "
"
+ // Nothing to do here
+ }
+ }
+ return "
"
+
+ })),
content: new LayerPanelWithPreview(this._config, this.languages, i, userDetails)
});
}
diff --git a/UI/CustomGenerator/GenerateEmpty.ts b/UI/CustomGenerator/GenerateEmpty.ts
index a1102e7b25..84274645f2 100644
--- a/UI/CustomGenerator/GenerateEmpty.ts
+++ b/UI/CustomGenerator/GenerateEmpty.ts
@@ -12,6 +12,7 @@ export class GenerateEmpty {
title: {},
description: {},
tagRenderings: [],
+ hideUnderlayingFeaturesMinPercentage: 0,
icon: {
render: "./assets/bug.svg"
},
diff --git a/UI/CustomGenerator/LayerPanel.ts b/UI/CustomGenerator/LayerPanel.ts
index 97a8edd2e7..97e37964aa 100644
--- a/UI/CustomGenerator/LayerPanel.ts
+++ b/UI/CustomGenerator/LayerPanel.ts
@@ -96,7 +96,10 @@ export default class LayerPanel extends UIElement {
{value: 2, shown: "Show both the ways/areas and the centerpoints"},
{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"),
-
+ 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.
" +
+ "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."),
setting(new AndOrTagInput(), "overpassTags", "Overpass query",
"The tags of the objects to load from overpass"),
diff --git a/UI/CustomGenerator/LayerPanelWithPreview.ts b/UI/CustomGenerator/LayerPanelWithPreview.ts
index 891ad6ca89..16e17f165b 100644
--- a/UI/CustomGenerator/LayerPanelWithPreview.ts
+++ b/UI/CustomGenerator/LayerPanelWithPreview.ts
@@ -13,7 +13,6 @@ import {UserDetails} from "../../Logic/Osm/OsmConnection";
export default class LayerPanelWithPreview extends UIElement{
private panel: UIElement;
-
constructor(config: UIEventSource, languages: UIEventSource, index: number, userDetails: UserDetails) {
super();
diff --git a/UI/CustomGenerator/TagRenderingPanel.ts b/UI/CustomGenerator/TagRenderingPanel.ts
index aa81aa585d..367ca869e6 100644
--- a/UI/CustomGenerator/TagRenderingPanel.ts
+++ b/UI/CustomGenerator/TagRenderingPanel.ts
@@ -91,7 +91,7 @@ export default class TagRenderingPanel extends InputElementMappings",
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),
undefined, {allowMovement: true}), "mappings",
"If a tag matches, then show the first respective text", "")
diff --git a/UI/Input/FixedInputElement.ts b/UI/Input/FixedInputElement.ts
index 6a19a1ada2..0309a5c0ea 100644
--- a/UI/Input/FixedInputElement.ts
+++ b/UI/Input/FixedInputElement.ts
@@ -7,9 +7,13 @@ export class FixedInputElement extends InputElement {
private readonly rendering: UIElement;
private readonly value: UIEventSource;
public readonly IsSelected : UIEventSource = new UIEventSource(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);
+ this._comparator = comparator ?? ((t0, t1) => t0 == t1);
this.value = new UIEventSource(value);
this.rendering = typeof (rendering) === 'string' ? new FixedUiElement(rendering) : rendering;
}
@@ -22,7 +26,9 @@ export class FixedInputElement extends InputElement {
}
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) {
diff --git a/UI/Input/RadioButton.ts b/UI/Input/RadioButton.ts
index 196efed28e..b120eff7d6 100644
--- a/UI/Input/RadioButton.ts
+++ b/UI/Input/RadioButton.ts
@@ -8,14 +8,15 @@ export class RadioButton extends InputElement {
private readonly _selectedElementIndex: UIEventSource
= new UIEventSource(null);
- private value: UIEventSource;
+ private readonly value: UIEventSource;
private readonly _elements: InputElement[]
- private _selectFirstAsDefault: boolean;
+ private readonly _selectFirstAsDefault: boolean;
constructor(elements: InputElement[],
selectFirstAsDefault = true) {
super(undefined);
+ console.log("Created new radiobutton with values ", elements)
this._elements = Utils.NoNull(elements);
this._selectFirstAsDefault = selectFirstAsDefault;
const self = this;
diff --git a/UI/SimpleAddUI.ts b/UI/SimpleAddUI.ts
index a3a9a890c4..70c47b2a91 100644
--- a/UI/SimpleAddUI.ts
+++ b/UI/SimpleAddUI.ts
@@ -54,6 +54,9 @@ export class SimpleAddUI extends UIElement {
if (typeof (preset.icon) !== "string") {
const tags = Utils.MergeTags(TagUtils.KVtoProperties(preset.tags), {id:"node/-1"});
icon = preset.icon.GetContent(tags).txt;
+ if(icon.startsWith("$")){
+ icon = undefined;
+ }
} else {
icon = preset.icon;
}
diff --git a/UI/UIElement.ts b/UI/UIElement.ts
index cae8fb5fb2..f1c0d12f7e 100644
--- a/UI/UIElement.ts
+++ b/UI/UIElement.ts
@@ -13,6 +13,8 @@ export abstract class UIElement extends UIEventSource {
private _hideIfEmpty = false;
public dumbMode = false;
+
+ private lastInnerRender: string;
/**
* In the 'deploy'-step, some code needs to be run by ts-node.
@@ -37,6 +39,7 @@ export abstract class UIElement extends UIEventSource {
this.dumbMode = false;
const self = this;
source.addCallback(() => {
+ self.lastInnerRender = undefined;
self.Update();
})
return this;
@@ -92,7 +95,7 @@ export abstract class UIElement extends UIEventSource {
return;
}
- this.setData(this.InnerRender());
+ this.setData(this.lastInnerRender ?? this.InnerRender());
element.innerHTML = this.data;
if (this._hideIfEmpty) {
@@ -151,14 +154,15 @@ export abstract class UIElement extends UIEventSource {
}
Render(): string {
+ this.lastInnerRender = this.lastInnerRender ?? this.InnerRender();
if (this.dumbMode) {
- return this.InnerRender();
+ return this.lastInnerRender;
}
let style = "";
if (this.style !== undefined && this.style !== "") {
style = `style="${this.style}"`;
}
- return `${this.InnerRender()}`
+ return `${this.lastInnerRender}`
}
AttachTo(divId: string) {
diff --git a/assets/layers/bike_repair_station/bike_repair_station.json b/assets/layers/bike_repair_station/bike_repair_station.json
index 12d7698050..59daccf95c 100644
--- a/assets/layers/bike_repair_station/bike_repair_station.json
+++ b/assets/layers/bike_repair_station/bike_repair_station.json
@@ -52,7 +52,12 @@
"if": {
"and": [
"service:bicycle:pump:operational_status=broken",
- "service:bicycle:tools=no"
+ {
+ "or": [
+ "service:bicycle:tools=no",
+ "service:bicycle:tools="
+ ]
+ }
]
},
"then": {
@@ -381,7 +386,12 @@
"if": {
"and": [
"service:bicycle:pump=yes",
- "service:bicycle:tools=no"
+ {
+ "or": [
+ "service:bicycle:tools=no",
+ "service:bicycle:tools="
+ ]
+ }
]
},
"then": "./assets/layers/bike_repair_station/pump.svg"
diff --git a/assets/layers/nature_reserve/nature_reserve.json b/assets/layers/nature_reserve/nature_reserve.json
new file mode 100644
index 0000000000..d3e48396f5
--- /dev/null
+++ b/assets/layers/nature_reserve/nature_reserve.json
@@ -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 {name}"
+ }
+ }
+ ],
+ "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": "Wat is de officiële naam van dit natuurgebied?
Sommige gebieden hebben geen naam"
+ },
+ "freeform": {
+ "key": "name",
+ "addExtraTags": [
+ "noname="
+ ]
+ },
+ "render": {
+ "nl": "Dit gebied heet {name}"
+ },
+ "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:
{description:0}"
+ },
+ "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
+}
\ No newline at end of file
diff --git a/test/Tag.spec.ts b/test/Tag.spec.ts
index 0a224acc6f..11674a972e 100644
--- a/test/Tag.spec.ts
+++ b/test/Tag.spec.ts
@@ -54,14 +54,22 @@ new T([
}
],
condition: "x="
- });
+ }, "");
equal(true, tr.IsKnown({"noname": "yes"}));
equal(true, tr.IsKnown({"name": "ABC"}));
equal(false, tr.IsKnown({"foo": "bar"}));
- equal("Has no name", tr.GetContent({"noname": "yes"}));
- equal("Ook een xyz", tr.GetContent({"name": "xyz"}));
- equal("Ook een {name}", tr.GetContent({"foo": "bar"}));
+ equal("Has no name", tr.GetContent({"noname": "yes"})?.txt);
+ equal("Ook een xyz", tr.GetContent({"name": "xyz"})?.txt);
+ equal(undefined, tr.GetContent({"foo": "bar"}));
- })]
+ })],
+ [
+ "Select right value test",
+ () => {
+
+ }
+ ]
+
+
]);