From 2bf45faa70b2f788ab8599b6a4864e6436a9adb2 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Tue, 19 Oct 2021 00:57:21 +0200 Subject: [PATCH 01/95] First test version of street lighting theme --- .../themes/street_lighting/license_info.json | 11 + assets/themes/street_lighting/street_lamp.svg | 4 + .../street_lighting/street_lighting.json | 380 ++++++++++++++++++ langs/themes/en.json | 151 +++++++ langs/themes/nl.json | 151 +++++++ 5 files changed, 697 insertions(+) create mode 100644 assets/themes/street_lighting/license_info.json create mode 100644 assets/themes/street_lighting/street_lamp.svg create mode 100644 assets/themes/street_lighting/street_lighting.json diff --git a/assets/themes/street_lighting/license_info.json b/assets/themes/street_lighting/license_info.json new file mode 100644 index 000000000..541b0ed40 --- /dev/null +++ b/assets/themes/street_lighting/license_info.json @@ -0,0 +1,11 @@ +[ + { + "path": "street_lamp.svg", + "authors": [ + "Yohan Boniface" + ], + "sources": [ + "https://raw.githubusercontent.com/hotosm/HDM-CartoCSS/master/icons/poi/street_lamp.svg" + ] + } +] \ No newline at end of file diff --git a/assets/themes/street_lighting/street_lamp.svg b/assets/themes/street_lighting/street_lamp.svg new file mode 100644 index 000000000..72602af3d --- /dev/null +++ b/assets/themes/street_lighting/street_lamp.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/themes/street_lighting/street_lighting.json b/assets/themes/street_lighting/street_lighting.json new file mode 100644 index 000000000..79ff16975 --- /dev/null +++ b/assets/themes/street_lighting/street_lighting.json @@ -0,0 +1,380 @@ +{ + "id": "street_lighting", + "maintainer": "Robin van der Linde", + "version": "20211018", + "language": [ + "en", + "nl" + ], + "title": { + "en": "Street Lighting", + "nl": "Straatverlichting" + }, + "description": { + "en": "A theme showing information about street lamps, like light type, mounting, light colour and much more.", + "nl": "Een thema met details over straatlantaarns zoals verlichtingstype, montage, lichtkleur en veel meer." + }, + "icon": "./assets/themes/street_lighting/street_lamp.svg", + "startZoom": 19, + "startLat": 52.99319, + "startLon": 6.56113, + "layers": [ + { + "id": "streetlamps", + "name": { + "en": "Street Lamps", + "nl": "Straatlantaarns" + }, + "source": { + "osmTags": "highway=street_lamp" + }, + "minZoom": 16, + "title": { + "render": { + "en": "Street Lamp", + "nl": "Straatlantaarn" + } + }, + "icon": "./assets/themes/street_lighting/street_lamp.svg", + "presets": [ + { + "title": { + "en": "street lamp", + "nl": "straatlantaarn" + }, + "tags": [ + "highway=street_lamp" + ], + "preciseInput": true + } + ], + "tagRenderings": [ + { + "id": "ref", + "render": { + "en": "This street lamp has the reference number {ref}", + "nl": "Deze straatlantaarn heeft het nummer {ref}" + }, + "question": { + "en": "What is the reference number of this street lamp?", + "nl": "Wat is het nummer van deze straatlantaarn?" + }, + "freeform": { + "key": "ref" + } + }, + { + "id": "support", + "question": { + "en": "How is this street lamp mounted?", + "nl": "Hoe is deze straatlantaarn gemonteerd?" + }, + "mappings": [ + { + "if": "support=catenary", + "then": { + "en": "This lamp is suspended using cables", + "nl": "Deze lantaarn hangt aan kabels" + } + }, + { + "if": "support=ceiling", + "then": { + "en": "This lamp is mounted on a ceiling", + "nl": "Deze lantaarn hangt aan een plafond" + } + }, + { + "if": "support=ground", + "then": { + "en": "This lamp is mounted in the ground", + "nl": "Deze lantaarn zit in de grond" + } + }, + { + "if": "support=pedestal", + "then": { + "en": "This lamp is mounted on a short pole (mostly < 1.5m)", + "nl": "Deze lantaarn zit op een korte paal (meestal < 1.5m)" + } + }, + { + "if": "support=pole", + "then": { + "en": "This lamp is mounted on a pole", + "nl": "Deze lantaarn zit op een paal" + } + }, + { + "if": "support=wall", + "then": { + "en": "This lamp is mounted directly to the wall", + "nl": "Deze lantaarn hangt direct aan de muur" + } + }, + { + "if": "support=wall_mount", + "then": { + "en": "This lamp is mounted to the wall using a metal bar", + "nl": "Deze lantaarn hangt aan de muur met een metalen balk" + } + } + ] + }, + { + "id": "lamp_mount", + "question": { + "en": "How is this lamp mounted to the pole?", + "nl": "Hoe zit deze lantaarn aan de paal?" + }, + "condition": "support=pole", + "mappings": [ + { + "if": "lamp_mount=straight_mast", + "then": { + "en": "This lamp sits atop of a straight mast", + "nl": "Deze lantaarn zit boven op een rechte paal" + } + }, + { + "if": "lamp_mount=bent_mast", + "then": { + "en": "This lamp sits at the end of a bent mast", + "nl": "Deze lantaarn zit aan het eind van een gebogen paal" + } + } + ] + }, + { + "id": "method", + "question": { + "en": "What kind of lighting does this lamp use?", + "nl": "Wat voor verlichting gebruikt deze lantaarn?" + }, + "mappings": [ + { + "if": "light:method=electric", + "then": { + "en": "This lamp is lit electrically", + "nl": "Deze lantaarn is elektrisch verlicht" + }, + "hideInAnswer": true + }, + { + "if": "light:method=LED", + "then": { + "en": "This lamp uses LEDs", + "nl": "Deze lantaarn gebruikt LEDs" + } + }, + { + "if": "light:method=incandescent", + "then": { + "en": "This lamp uses incandescent lighting", + "nl": "Deze lantaarn gebruikt gloeilampen" + } + }, + { + "if": "light:method=halogen", + "then": { + "en": "This lamp uses halogen lighting", + "nl": "Deze lantaarn gebruikt halogeen verlichting" + } + }, + { + "if": "light:method=discharge", + "then": { + "en": "This lamp uses discharge lamps (unknown type)", + "nl": "Deze lantaarn gebruikt gasontladingslampen (onbekend type)" + } + }, + { + "if": "light:method=mercury", + "then": { + "en": "This lamp uses a mercury-vapour lamp (lightly blueish)", + "nl": "Deze lantaarn gebruikt een kwiklamp (enigszins blauwachtig)" + } + }, + { + "if": "light:method=metal-halide", + "then": { + "en": "This lamp uses metal-halide lamps (bright white)", + "nl": "Deze lantaarn gebruikt metaalhalidelampen" + } + }, + { + "if": "light:method=fluorescent", + "then": { + "en": "This lamp uses fluorescent lighting", + "nl": "Deze lantaarn gebruikt fluorescentieverlichting (TL en spaarlamp)" + } + }, + { + "if": "light:method=sodium", + "then": { + "en": "This lamp uses sodium lamps (unknown type)", + "nl": "Deze lantaarn gebruikt natriumlampen (onbekend type)" + } + }, + { + "if": "light:method=low_pressure_sodium", + "then": { + "en": "This lamp uses low pressure sodium lamps (monochrome orange)", + "nl": "Deze lantaarn gebruikt lagedruknatriumlampen (monochroom oranje)" + } + }, + { + "if": "light:method=high_pressure_sodium", + "then": { + "en": "This lamp uses high pressure sodium lamps (orange with white)", + "nl": "Deze lantaarn gebruikt hogedruknatriumlampen (oranje met wit)" + } + }, + { + "if": "light:method=gas", + "then": { + "en": "This lamp is lit using gas", + "nl": "Deze lantaarn wordt verlicht met gas" + } + } + ] + } + ] + }, + { + "id": "lit_streets", + "name": { + "en": "Lit streets", + "nl": "Verlichte straten" + }, + "source": { + "osmTags": { + "and": [ + "highway!=", + "lit!=no" + ] + } + }, + "minZoom": 16, + "title": { + "render": { + "en": "Lit street", + "nl": "Verlichte straat" + }, + "mappings": [ + { + "if": "name~*", + "then": "{name}" + } + ] + }, + "color": "#ff0", + "tagRenderings": [ + { + "id": "lit", + "question": { + "en": "Is this street lit?", + "nl": "Is deze straat verlicht?" + }, + "mappings": [ + { + "if": "lit=yes", + "then": { + "en": "This street is lit", + "nl": "Deze weg is verlicht" + } + }, + { + "if": "lit=no", + "then": { + "en": "This road is not lit", + "nl": "Deze weg is niet verlicht" + } + }, + { + "if": "lit=sunset-sunrise", + "then": { + "en": "This road is lit at night", + "nl": "Deze weg is 's nachts verlicht" + }, + "hideInAnswer": true + }, + { + "if": "lit=24/7", + "then": { + "en": "This road is lit 24/7", + "nl": "Deze weg is 24/7 verlicht" + } + } + ] + } + ], + "allowSplit": true + }, + { + "id": "all_streets", + "name": { + "en": "All streets", + "nl": "Alle straten" + }, + "source": { + "osmTags": "highway!=" + }, + "minZoom": 19, + "title": { + "render": { + "en": "Street", + "nl": "Straat" + }, + "mappings": [ + { + "if": "name~*", + "then": "{name}" + } + ] + }, + "color": "#a9a9a9", + "tagRenderings": [ + { + "id": "lit", + "question": { + "en": "Is this street lit?", + "nl": "Is deze straat verlicht?" + }, + "mappings": [ + { + "if": "lit=yes", + "then": { + "en": "This street is lit", + "nl": "Deze weg is verlicht" + } + }, + { + "if": "lit=no", + "then": { + "en": "This road is not lit", + "nl": "Deze weg is niet verlicht" + } + }, + { + "if": "lit=sunset-sunrise", + "then": { + "en": "This road is lit at night", + "nl": "Deze weg is 's nachts verlicht" + }, + "hideInAnswer": true + }, + { + "if": "lit=24/7", + "then": { + "en": "This road is lit 24/7", + "nl": "Deze weg is 24/7 verlicht" + } + } + ] + } + ], + "allowSplit": true + } + ] +} \ No newline at end of file diff --git a/langs/themes/en.json b/langs/themes/en.json index 8bf77665b..9874ca47a 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -1227,6 +1227,157 @@ "shortDescription": "A map showing sport pitches", "title": "Sport pitches" }, + "street_lighting": { + "description": "A theme showing information about street lamps, like light type, mounting, light colour and much more.", + "layers": { + "0": { + "name": "Street Lamps", + "presets": { + "0": { + "title": "street lamp" + } + }, + "tagRenderings": { + "lamp_mount": { + "mappings": { + "0": { + "then": "This lamp sits atop of a straight mast" + }, + "1": { + "then": "This lamp sits at the end of a bent mast" + } + }, + "question": "How is this lamp mounted to the pole?" + }, + "method": { + "mappings": { + "0": { + "then": "This lamp is lit electrically" + }, + "1": { + "then": "This lamp uses LEDs" + }, + "2": { + "then": "This lamp uses incandescent lighting" + }, + "3": { + "then": "This lamp uses halogen lighting" + }, + "4": { + "then": "This lamp uses discharge lamps (unknown type)" + }, + "5": { + "then": "This lamp uses a mercury-vapour lamp (lightly blueish)" + }, + "6": { + "then": "This lamp uses metal-halide lamps (bright white)" + }, + "7": { + "then": "This lamp uses fluorescent lighting" + }, + "8": { + "then": "This lamp uses sodium lamps (unknown type)" + }, + "9": { + "then": "This lamp uses low pressure sodium lamps (monochrome orange)" + }, + "10": { + "then": "This lamp uses high pressure sodium lamps (orange with white)" + }, + "11": { + "then": "This lamp is lit using gas" + } + }, + "question": "What kind of lighting does this lamp use?" + }, + "ref": { + "question": "What is the reference number of this street lamp?", + "render": "This street lamp has the reference number {ref}" + }, + "support": { + "mappings": { + "0": { + "then": "This lamp is suspended using cables" + }, + "1": { + "then": "This lamp is mounted on a ceiling" + }, + "2": { + "then": "This lamp is mounted in the ground" + }, + "3": { + "then": "This lamp is mounted on a short pole (mostly < 1.5m)" + }, + "4": { + "then": "This lamp is mounted on a pole" + }, + "5": { + "then": "This lamp is mounted directly to the wall" + }, + "6": { + "then": "This lamp is mounted to the wall using a metal bar" + } + }, + "question": "How is this street lamp mounted?" + } + }, + "title": { + "render": "Street Lamp" + } + }, + "1": { + "name": "Lit streets", + "tagRenderings": { + "lit": { + "mappings": { + "0": { + "then": "This street is lit" + }, + "1": { + "then": "This road is not lit" + }, + "2": { + "then": "This road is lit at night" + }, + "3": { + "then": "This road is lit 24/7" + } + }, + "question": "Is this street lit?" + } + }, + "title": { + "render": "Lit street" + } + }, + "2": { + "name": "All streets", + "tagRenderings": { + "lit": { + "mappings": { + "0": { + "then": "This street is lit" + }, + "1": { + "then": "This road is not lit" + }, + "2": { + "then": "This road is lit at night" + }, + "3": { + "then": "This road is lit 24/7" + } + }, + "question": "Is this street lit?" + } + }, + "title": { + "render": "Street" + } + } + }, + "title": "Street Lighting" + }, "surveillance": { "description": "On this open map, you can find surveillance cameras.", "shortDescription": "Surveillance cameras and other means of surveillance", diff --git a/langs/themes/nl.json b/langs/themes/nl.json index a6322e3d5..7045a2658 100644 --- a/langs/themes/nl.json +++ b/langs/themes/nl.json @@ -897,6 +897,157 @@ "shortDescription": "Deze kaart toont sportvelden", "title": "Sportvelden" }, + "street_lighting": { + "description": "Een thema met details over straatlantaarns zoals verlichtingstype, montage, lichtkleur en veel meer.", + "layers": { + "0": { + "name": "Straatlantaarns", + "presets": { + "0": { + "title": "straatlantaarn" + } + }, + "tagRenderings": { + "lamp_mount": { + "mappings": { + "0": { + "then": "Deze lantaarn zit boven op een rechte paal" + }, + "1": { + "then": "Deze lantaarn zit aan het eind van een gebogen paal" + } + }, + "question": "Hoe zit deze lantaarn aan de paal?" + }, + "method": { + "mappings": { + "0": { + "then": "Deze lantaarn is elektrisch verlicht" + }, + "1": { + "then": "Deze lantaarn gebruikt LEDs" + }, + "2": { + "then": "Deze lantaarn gebruikt gloeilampen" + }, + "3": { + "then": "Deze lantaarn gebruikt halogeen verlichting" + }, + "4": { + "then": "Deze lantaarn gebruikt gasontladingslampen (onbekend type)" + }, + "5": { + "then": "Deze lantaarn gebruikt een kwiklamp (enigszins blauwachtig)" + }, + "6": { + "then": "Deze lantaarn gebruikt metaalhalidelampen" + }, + "7": { + "then": "Deze lantaarn gebruikt fluorescentieverlichting (TL en spaarlamp)" + }, + "8": { + "then": "Deze lantaarn gebruikt natriumlampen (onbekend type)" + }, + "9": { + "then": "Deze lantaarn gebruikt lagedruknatriumlampen (monochroom oranje)" + }, + "10": { + "then": "Deze lantaarn gebruikt hogedruknatriumlampen (oranje met wit)" + }, + "11": { + "then": "Deze lantaarn wordt verlicht met gas" + } + }, + "question": "Wat voor verlichting gebruikt deze lantaarn?" + }, + "ref": { + "question": "Wat is het nummer van deze straatlantaarn?", + "render": "Deze straatlantaarn heeft het nummer {ref}" + }, + "support": { + "mappings": { + "0": { + "then": "Deze lantaarn hangt aan kabels" + }, + "1": { + "then": "Deze lantaarn hangt aan een plafond" + }, + "2": { + "then": "Deze lantaarn zit in de grond" + }, + "3": { + "then": "Deze lantaarn zit op een korte paal (meestal < 1.5m)" + }, + "4": { + "then": "Deze lantaarn zit op een paal" + }, + "5": { + "then": "Deze lantaarn hangt direct aan de muur" + }, + "6": { + "then": "Deze lantaarn hangt aan de muur met een metalen balk" + } + }, + "question": "Hoe is deze straatlantaarn gemonteerd?" + } + }, + "title": { + "render": "Straatlantaarn" + } + }, + "1": { + "name": "Verlichte straten", + "tagRenderings": { + "lit": { + "mappings": { + "0": { + "then": "Deze weg is verlicht" + }, + "1": { + "then": "Deze weg is niet verlicht" + }, + "2": { + "then": "Deze weg is 's nachts verlicht" + }, + "3": { + "then": "Deze weg is 24/7 verlicht" + } + }, + "question": "Is deze straat verlicht?" + } + }, + "title": { + "render": "Verlichte straat" + } + }, + "2": { + "name": "Alle straten", + "tagRenderings": { + "lit": { + "mappings": { + "0": { + "then": "Deze weg is verlicht" + }, + "1": { + "then": "Deze weg is niet verlicht" + }, + "2": { + "then": "Deze weg is 's nachts verlicht" + }, + "3": { + "then": "Deze weg is 24/7 verlicht" + } + }, + "question": "Is deze straat verlicht?" + } + }, + "title": { + "render": "Straat" + } + } + }, + "title": "Straatverlichting" + }, "surveillance": { "description": "Op deze open kaart kan je bewakingscamera's vinden.", "shortDescription": "Bewakingscameras en dergelijke", From dccb1daddb761319d4f343881450f6d36520ff1a Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Tue, 19 Oct 2021 01:28:42 +0200 Subject: [PATCH 02/95] Fix incomplete license --- assets/themes/street_lighting/license_info.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/themes/street_lighting/license_info.json b/assets/themes/street_lighting/license_info.json index 541b0ed40..d157db54d 100644 --- a/assets/themes/street_lighting/license_info.json +++ b/assets/themes/street_lighting/license_info.json @@ -1,11 +1,12 @@ [ { "path": "street_lamp.svg", + "license": "CC0", "authors": [ "Yohan Boniface" ], "sources": [ - "https://raw.githubusercontent.com/hotosm/HDM-CartoCSS/master/icons/poi/street_lamp.svg" + "https://github.com/hotosm/HDM-CartoCSS/blob/master/icons/poi/street_lamp.svg" ] } ] \ No newline at end of file From 584ade8e61f0e35d0f93fbf92109feba1089be45 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 19 Oct 2021 02:31:32 +0200 Subject: [PATCH 03/95] Refactoring: split rendering of centroid to PointRendering --- Models/ThemeConfig/Json/LayerConfigJson.ts | 42 +- .../Json/PointRenderingConfigJson.ts | 62 +++ Models/ThemeConfig/LayerConfig.ts | 396 +++--------------- Models/ThemeConfig/LayoutConfig.ts | 36 +- Models/ThemeConfig/PointRenderingConfig.ts | 244 +++++++++++ Models/ThemeConfig/TagRenderingConfig.ts | 1 - Models/ThemeConfig/WithContextLoader.ts | 117 ++++++ scripts/lint.ts | 51 ++- 8 files changed, 509 insertions(+), 440 deletions(-) create mode 100644 Models/ThemeConfig/Json/PointRenderingConfigJson.ts create mode 100644 Models/ThemeConfig/PointRenderingConfig.ts create mode 100644 Models/ThemeConfig/WithContextLoader.ts diff --git a/Models/ThemeConfig/Json/LayerConfigJson.ts b/Models/ThemeConfig/Json/LayerConfigJson.ts index 69d1b972e..eefd18085 100644 --- a/Models/ThemeConfig/Json/LayerConfigJson.ts +++ b/Models/ThemeConfig/Json/LayerConfigJson.ts @@ -4,6 +4,7 @@ import FilterConfigJson from "./FilterConfigJson"; import {DeleteConfigJson} from "./DeleteConfigJson"; import UnitConfigJson from "./UnitConfigJson"; import MoveConfigJson from "./MoveConfigJson"; +import PointRenderingConfigJson from "./PointRenderingConfigJson"; /** * Configuration for a single layer @@ -119,47 +120,8 @@ export interface LayerConfigJson { */ titleIcons?: (string | TagRenderingConfigJson)[]; - /** - * The icon for an element. - * Note that this also doubles as the icon for this layer (rendered with the overpass-tags) ánd the icon in the presets. - * - * The result of the icon is rendered as follows: - * the resulting string is interpreted as a _list_ of items, separated by ";". The bottommost layer is the first layer. - * As a result, on could use a generic pin, then overlay it with a specific icon. - * To make things even more practical, one can use all SVG's from the folder "assets/svg" and _substitute the color_ in it. - * E.g. to draw a red pin, use "pin:#f00", to have a green circle with your icon on top, use `circle:#0f0;` - * - */ - icon?: string | TagRenderingConfigJson; - /** - * IconsOverlays are a list of extra icons/badges to overlay over the icon. - * The 'badge'-toggle changes their behaviour. - * If badge is set, it will be added as a 25% height icon at the bottom right of the icon, with all the badges in a flex layout. - * If badges is false, it'll be a simple overlay - * - * Note: strings are interpreted as icons, so layering and substituting is supported - */ - iconOverlays?: { if: string | AndOrTagConfigJson, then: string | TagRenderingConfigJson, badge?: boolean }[] - - /** - * A string containing "width,height" or "width,height,anchorpoint" where anchorpoint is any of 'center', 'top', 'bottom', 'left', 'right', 'bottomleft','topright', ... - * Default is '40,40,center' - */ - iconSize?: string | TagRenderingConfigJson; - /** - * The rotation of an icon, useful for e.g. directions. - * Usage: as if it were a css property for 'rotate', thus has to end with 'deg', e.g. `90deg`, `{direction}deg`, `calc(90deg - {camera:direction}deg)`` - */ - rotation?: string | TagRenderingConfigJson; - /** - * A HTML-fragment that is shown below the icon, for example: - *
{name}
- * - * If the icon is undefined, then the label is shown in the center of the feature. - * Note that, if the wayhandling hides the icon then no label is shown as well. - */ - label?: string | TagRenderingConfigJson; + mapRendering: PointRenderingConfigJson[] /** * The color for way-elements and SVG-elements. diff --git a/Models/ThemeConfig/Json/PointRenderingConfigJson.ts b/Models/ThemeConfig/Json/PointRenderingConfigJson.ts new file mode 100644 index 000000000..694d98f26 --- /dev/null +++ b/Models/ThemeConfig/Json/PointRenderingConfigJson.ts @@ -0,0 +1,62 @@ +import {TagRenderingConfigJson} from "./TagRenderingConfigJson"; +import {AndOrTagConfigJson} from "./TagConfigJson"; + +/** + * The PointRenderingConfig gives all details onto how to render a single point of a feature. + * + * This can be used if: + * + * - The feature is a point + * - To render something at the centroid of an area, or at the start, end or projected centroid of a way + */ +export default interface PointRenderingConfigJson { + + /** + * All the locations that this point should be rendered at. + * Using `location: ["point", "centroid"] will always render centerpoint + */ + location: ("point" | "centroid")[] + + /** + * The icon for an element. + * Note that this also doubles as the icon for this layer (rendered with the overpass-tags) ánd the icon in the presets. + * + * The result of the icon is rendered as follows: + * the resulting string is interpreted as a _list_ of items, separated by ";". The bottommost layer is the first layer. + * As a result, on could use a generic pin, then overlay it with a specific icon. + * To make things even more practical, one can use all SVG's from the folder "assets/svg" and _substitute the color_ in it. + * E.g. to draw a red pin, use "pin:#f00", to have a green circle with your icon on top, use `circle:#0f0;` + * + */ + icon?: string | TagRenderingConfigJson; + + /** + * IconsOverlays are a list of extra icons/badges to overlay over the icon. + * The 'badge'-toggle changes their behaviour. + * If badge is set, it will be added as a 25% height icon at the bottom right of the icon, with all the badges in a flex layout. + * If badges is false, it'll be a simple overlay + * + * Note: strings are interpreted as icons, so layering and substituting is supported + */ + iconOverlays?: { if: string | AndOrTagConfigJson, then: string | TagRenderingConfigJson, badge?: boolean }[] + + + /** + * A string containing "width,height" or "width,height,anchorpoint" where anchorpoint is any of 'center', 'top', 'bottom', 'left', 'right', 'bottomleft','topright', ... + * Default is '40,40,center' + */ + iconSize?: string | TagRenderingConfigJson; + /** + * The rotation of an icon, useful for e.g. directions. + * Usage: as if it were a css property for 'rotate', thus has to end with 'deg', e.g. `90deg`, `{direction}deg`, `calc(90deg - {camera:direction}deg)`` + */ + rotation?: string | TagRenderingConfigJson; + /** + * A HTML-fragment that is shown below the icon, for example: + *
{name}
+ * + * If the icon is undefined, then the label is shown in the center of the feature. + * Note that, if the wayhandling hides the icon then no label is shown as well. + */ + label?: string | TagRenderingConfigJson; +} \ No newline at end of file diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts index bf365ee6a..6241a37ff 100644 --- a/Models/ThemeConfig/LayerConfig.ts +++ b/Models/ThemeConfig/LayerConfig.ts @@ -6,22 +6,17 @@ import PresetConfig from "./PresetConfig"; import {LayerConfigJson} from "./Json/LayerConfigJson"; import Translations from "../../UI/i18n/Translations"; import {TagUtils} from "../../Logic/Tags/TagUtils"; -import SharedTagRenderings from "../../Customizations/SharedTagRenderings"; -import {TagRenderingConfigJson} from "./Json/TagRenderingConfigJson"; import {Utils} from "../../Utils"; import {UIEventSource} from "../../Logic/UIEventSource"; import BaseUIElement from "../../UI/BaseUIElement"; -import {FixedUiElement} from "../../UI/Base/FixedUiElement"; -import Combine from "../../UI/Base/Combine"; -import {VariableUiElement} from "../../UI/Base/VariableUIElement"; import FilterConfig from "./FilterConfig"; import {Unit} from "../Unit"; import DeleteConfig from "./DeleteConfig"; -import Svg from "../../Svg"; -import Img from "../../UI/Base/Img"; import MoveConfig from "./MoveConfig"; +import PointRenderingConfig from "./PointRenderingConfig"; +import WithContextLoader from "./WithContextLoader"; -export default class LayerConfig { +export default class LayerConfig extends WithContextLoader{ static WAYHANDLING_DEFAULT = 0; static WAYHANDLING_CENTER_ONLY = 1; static WAYHANDLING_CENTER_AND_WAY = 2; @@ -39,11 +34,9 @@ export default class LayerConfig { maxzoom: number; title?: TagRenderingConfig; titleIcons: TagRenderingConfig[]; - icon: TagRenderingConfig; - iconOverlays: { if: TagsFilter; then: TagRenderingConfig; badge: boolean }[]; - iconSize: TagRenderingConfig; - label: TagRenderingConfig; - rotation: TagRenderingConfig; + + public readonly mapRendering: PointRenderingConfig[] + color: TagRenderingConfig; width: TagRenderingConfig; dashArray: TagRenderingConfig; @@ -63,25 +56,9 @@ export default class LayerConfig { context?: string, official: boolean = true ) { - context = context + "." + json.id; - const self = this; + super(json, context) this.id = json.id; - this.allowSplit = json.allowSplit ?? false; - this.name = Translations.T(json.name, context + ".name"); - this.units = (json.units ?? []).map(((unitJson, i) => Unit.fromJson(unitJson, `${context}.unit[${i}]`))) - - if (json.description !== undefined) { - if (Object.keys(json.description).length === 0) { - json.description = undefined; - } - } - - this.description = Translations.T( - json.description, - context + ".description" - ); - let legacy = undefined; if (json["overpassTags"] !== undefined) { // @ts-ignore @@ -111,7 +88,7 @@ export default class LayerConfig { throw context + "Use 'geoJson' instead of 'geojson' (the J is a capital letter)"; } - this.source = new SourceConfig( + this. source = new SourceConfig( { osmTags: osmTags, geojsonSource: json.source["geoJson"], @@ -119,14 +96,33 @@ export default class LayerConfig { overpassScript: json.source["overpassScript"], isOsmCache: json.source["isOsmCache"], }, - this.id + json.id ); } else { - this.source = new SourceConfig({ + this. source = new SourceConfig({ osmTags: legacy, }); } + + + this.id = json.id; + this.allowSplit = json.allowSplit ?? false; + this.name = Translations.T(json.name, context + ".name"); + this.units = (json.units ?? []).map(((unitJson, i) => Unit.fromJson(unitJson, `${context}.unit[${i}]`))) + + if (json.description !== undefined) { + if (Object.keys(json.description).length === 0) { + json.description = undefined; + } + } + + this.description = Translations.T( + json.description, + context + ".description" + ); + + this.calculatedTags = undefined; if (json.calculatedTags !== undefined) { if (!official) { @@ -202,101 +198,15 @@ export default class LayerConfig { return config; }); - /** Given a key, gets the corresponding property from the json (or the default if not found - * - * The found value is interpreted as a tagrendering and fetched/parsed - * */ - function tr(key: string, deflt) { - const v = json[key]; - if (v === undefined || v === null) { - if (deflt === undefined) { - return undefined; - } - return new TagRenderingConfig( - deflt, - self.source.osmTags, - `${context}.${key}.default value` - ); - } - if (typeof v === "string") { - const shared = SharedTagRenderings.SharedTagRendering.get(v); - if (shared) { - return shared; - } - } - return new TagRenderingConfig( - v, - self.source.osmTags, - `${context}.${key}` - ); + + + this.mapRendering = json.mapRendering.map((r, i) => new PointRenderingConfig(r, context+".mapRendering["+i+"]")) + + if(this.mapRendering.length > 1){ + throw "Invalid maprendering for "+this.id+", currently only one mapRendering is supported!" } - /** - * Converts a list of tagRenderingCOnfigJSON in to TagRenderingConfig - * A string is interpreted as a name to call - */ - function trs( - tagRenderings?: (string | { builtin: string, override: any } | TagRenderingConfigJson)[], - readOnly = false - ) { - if (tagRenderings === undefined) { - return []; - } - - return Utils.NoNull( - tagRenderings.map((renderingJson, i) => { - if (typeof renderingJson === "string") { - renderingJson = {builtin: renderingJson, override: undefined} - } - - if (renderingJson["builtin"] !== undefined) { - const renderingId = renderingJson["builtin"] - if (renderingId === "questions") { - if (readOnly) { - throw `A tagrendering has a question, but asking a question does not make sense here: is it a title icon or a geojson-layer? ${context}. The offending tagrendering is ${JSON.stringify( - renderingJson - )}`; - } - - return new TagRenderingConfig("questions", undefined, context); - } - - if (renderingJson["override"] !== undefined) { - const sharedJson = SharedTagRenderings.SharedTagRenderingJson.get(renderingId) - return new TagRenderingConfig( - Utils.Merge(renderingJson["override"], sharedJson), - self.source.osmTags, - `${context}.tagrendering[${i}]+override` - ); - } - - const shared = SharedTagRenderings.SharedTagRendering.get(renderingId); - - if (shared !== undefined) { - return shared; - } - if (Utils.runningFromConsole) { - return undefined; - } - - const keys = Array.from( - SharedTagRenderings.SharedTagRendering.keys() - ); - throw `Predefined tagRendering ${renderingId} not found in ${context}.\n Try one of ${keys.join( - ", " - )}\n If you intent to output this text literally, use {\"render\": } instead"}`; - } - - return new TagRenderingConfig( - renderingJson, - self.source.osmTags, - `${context}.tagrendering[${i}]` - ); - }) - ); - } - - this.tagRenderings = trs(json.tagRenderings, false); + this.tagRenderings = this.trs(json.tagRenderings, false); const missingIds = json.tagRenderings?.filter(tr => typeof tr !== "string" && tr["builtin"] === undefined && tr["id"] === undefined) ?? []; @@ -329,43 +239,13 @@ export default class LayerConfig { } } - this.titleIcons = trs(titleIcons, true); + this.titleIcons = this.trs(titleIcons, true); - this.title = tr("title", undefined); - this.icon = tr("icon", ""); - this.iconOverlays = (json.iconOverlays ?? []).map((overlay, i) => { - let tr = new TagRenderingConfig( - overlay.then, - self.source.osmTags, - `iconoverlays.${i}` - ); - if ( - typeof overlay.then === "string" && - SharedTagRenderings.SharedIcons.get(overlay.then) !== undefined - ) { - tr = SharedTagRenderings.SharedIcons.get(overlay.then); - } - return { - if: TagUtils.Tag(overlay.if), - then: tr, - badge: overlay.badge ?? false, - }; - }); - - const iconPath = this.icon.GetRenderValue({id: "node/-1"}).txt; - if (iconPath.startsWith(Utils.assets_path)) { - const iconKey = iconPath.substr(Utils.assets_path.length); - if (Svg.All[iconKey] === undefined) { - throw "Builtin SVG asset not found: " + iconPath; - } - } - this.isShown = tr("isShown", "yes"); - this.iconSize = tr("iconSize", "40,40,center"); - this.label = tr("label", ""); - this.color = tr("color", "#0000ff"); - this.width = tr("width", "7"); - this.rotation = tr("rotation", "0"); - this.dashArray = tr("dashArray", ""); + this.title = this.tr("title", undefined); + this.isShown = this.tr("isShown", "yes"); + this.color = this.tr("color", "#0000ff"); + this.width = this.tr("width", "7"); + this.dashArray = this.tr("dashArray", ""); this.deletion = null; if (json.deletion === true) { @@ -398,54 +278,9 @@ export default class LayerConfig { if (this.calculatedTags === undefined) { return []; } - return this.calculatedTags.map((code) => code[1]); } - public AddRoamingRenderings(addAll: { - tagRenderings: TagRenderingConfig[]; - titleIcons: TagRenderingConfig[]; - iconOverlays: { - if: TagsFilter; - then: TagRenderingConfig; - badge: boolean; - }[]; - }): LayerConfig { - let insertionPoint = this.tagRenderings - .map((tr) => tr.IsQuestionBoxElement()) - .indexOf(true); - if (insertionPoint < 0) { - // No 'questions' defined - we just add them all to the end - insertionPoint = this.tagRenderings.length; - } - this.tagRenderings.splice(insertionPoint, 0, ...addAll.tagRenderings); - - this.iconOverlays.push(...addAll.iconOverlays); - for (const icon of addAll.titleIcons) { - this.titleIcons.splice(0, 0, icon); - } - return this; - } - - public GetRoamingRenderings(): { - tagRenderings: TagRenderingConfig[]; - titleIcons: TagRenderingConfig[]; - iconOverlays: { - if: TagsFilter; - then: TagRenderingConfig; - badge: boolean; - }[]; - } { - const tagRenderings = this.tagRenderings.filter((tr) => tr.roaming); - const titleIcons = this.titleIcons.filter((tr) => tr.roaming); - const iconOverlays = this.iconOverlays.filter((io) => io.then.roaming); - - return { - tagRenderings: tagRenderings, - titleIcons: titleIcons, - iconOverlays: iconOverlays, - }; - } public GenerateLeafletStyle( tags: UIEventSource, @@ -463,13 +298,6 @@ export default class LayerConfig { weight: number; dashArray: number[]; } { - function num(str, deflt = 40) { - const n = Number(str); - if (isNaN(n)) { - return deflt; - } - return n; - } function rendernum(tr: TagRenderingConfig, deflt: number) { const str = Number(render(tr, "" + deflt)); @@ -488,7 +316,6 @@ export default class LayerConfig { return Utils.SubstituteKeys(str, tags.data).replace(/{.*}/g, ""); } - const iconSize = render(this.iconSize, "40,40,center").split(","); const dashArray = render(this.dashArray)?.split(" ")?.map(Number); let color = render(this.color, "#00f"); @@ -500,134 +327,10 @@ export default class LayerConfig { const weight = rendernum(this.width, 5); - const iconW = num(iconSize[0]); - let iconH = num(iconSize[1]); - const mode = iconSize[2]?.trim()?.toLowerCase() ?? "center"; - - let anchorW = iconW / 2; - let anchorH = iconH / 2; - if (mode === "left") { - anchorW = 0; - } - if (mode === "right") { - anchorW = iconW; - } - - if (mode === "top") { - anchorH = 0; - } - if (mode === "bottom") { - anchorH = iconH; - } - - const iconUrlStatic = render(this.icon); - const self = this; - - function genHtmlFromString(sourcePart: string, rotation: string): BaseUIElement { - const style = `width:100%;height:100%;transform: rotate( ${rotation} );display:block;position: absolute; top: 0; left: 0`; - let html: BaseUIElement = new FixedUiElement( - `` - ); - const match = sourcePart.match(/([a-zA-Z0-9_]*):([^;]*)/); - if (match !== null && Svg.All[match[1] + ".svg"] !== undefined) { - html = new Img( - (Svg.All[match[1] + ".svg"] as string).replace( - /#000000/g, - match[2] - ), - true - ).SetStyle(style); - } - return html; - } - - - const mappedHtml = tags?.map((tgs) => { - // What do you mean, 'tgs' is never read? - // It is read implicitly in the 'render' method - const iconUrl = render(self.icon); - const rotation = render(self.rotation, "0deg"); - - let htmlParts: BaseUIElement[] = []; - let sourceParts = Utils.NoNull( - iconUrl.split(";").filter((prt) => prt != "") - ); - for (const sourcePart of sourceParts) { - htmlParts.push(genHtmlFromString(sourcePart, rotation)); - } - - let badges = []; - for (const iconOverlay of self.iconOverlays) { - if (!iconOverlay.if.matchesProperties(tgs)) { - continue; - } - if (iconOverlay.badge) { - const badgeParts: BaseUIElement[] = []; - const renderValue = iconOverlay - .then - .GetRenderValue(tgs) - - if (renderValue === undefined) { - continue; - } - - const partDefs = renderValue.txt.split(";") - .filter((prt) => prt != ""); - - for (const badgePartStr of partDefs) { - badgeParts.push(genHtmlFromString(badgePartStr, "0")); - } - - const badgeCompound = new Combine(badgeParts).SetStyle( - "display:flex;position:relative;width:100%;height:100%;" - ); - - badges.push(badgeCompound); - } else { - htmlParts.push( - genHtmlFromString(iconOverlay.then.GetRenderValue(tgs).txt, "0") - ); - } - } - - if (badges.length > 0) { - const badgesComponent = new Combine(badges).SetStyle( - "display:flex;height:50%;width:100%;position:absolute;top:50%;left:50%;" - ); - htmlParts.push(badgesComponent); - } - - if (sourceParts.length == 0) { - iconH = 0; - } - try { - const label = self.label - ?.GetRenderValue(tgs) - ?.Subs(tgs) - ?.SetClass("block text-center") - ?.SetStyle("margin-top: " + (iconH + 2) + "px"); - if (label !== undefined) { - htmlParts.push( - new Combine([label]).SetClass("flex flex-col items-center") - ); - } - } catch (e) { - console.error(e, tgs); - } - return new Combine(htmlParts); - }); - + const icon = this.mapRendering[0].GenerateLeafletStyle(tags, clickable) + return { - icon: { - html: mappedHtml === undefined ? new FixedUiElement(self.icon.render.txt) : new VariableUiElement(mappedHtml), - iconSize: [iconW, iconH], - iconAnchor: [anchorW, anchorH], - popupAnchor: [0, 3 - anchorH], - iconUrl: iconUrlStatic, - className: clickable - ? "leaflet-div-icon" - : "leaflet-div-icon unclickable", - }, + icon, color: color, weight: weight, dashArray: dashArray, @@ -638,18 +341,17 @@ export default class LayerConfig { const parts: Set[] = []; parts.push(...this.tagRenderings?.map((tr) => tr.ExtractImages(false))); parts.push(...this.titleIcons?.map((tr) => tr.ExtractImages(true))); - parts.push(this.icon?.ExtractImages(true)); - parts.push( - ...this.iconOverlays?.map((overlay) => overlay.then.ExtractImages(true)) - ); for (const preset of this.presets) { parts.push(new Set(preset.description?.ExtractImages(false))); } - + for (const pointRenderingConfig of this.mapRendering) { + parts.push(pointRenderingConfig.ExtractImages()) + } const allIcons = new Set(); for (const part of parts) { part?.forEach(allIcons.add, allIcons); } + return allIcons; } diff --git a/Models/ThemeConfig/LayoutConfig.ts b/Models/ThemeConfig/LayoutConfig.ts index 98e051535..56d169fc5 100644 --- a/Models/ThemeConfig/LayoutConfig.ts +++ b/Models/ThemeConfig/LayoutConfig.ts @@ -25,7 +25,6 @@ export default class LayoutConfig { public readonly startLat: number; public readonly startLon: number; public readonly widenFactor: number; - public readonly roamingRenderings: TagRenderingConfig[]; public readonly defaultBackgroundId?: string; public layers: LayerConfig[]; public tileLayerSources: TilesourceConfig[] @@ -97,44 +96,11 @@ export default class LayoutConfig { throw "Widenfactor is very big, use a value between 1 and 5 (current value is "+json.widenFactor+") at "+context } this.widenFactor = json.widenFactor ?? 1.5; - this.roamingRenderings = (json.roamingRenderings ?? []).map((tr, i) => { - if (typeof tr === "string") { - if (SharedTagRenderings.SharedTagRendering.get(tr) !== undefined) { - return SharedTagRenderings.SharedTagRendering.get(tr); - } - } - return new TagRenderingConfig(tr, undefined, `${this.id}.roaming_renderings[${i}]`); - } - ); + this.defaultBackgroundId = json.defaultBackgroundId; this.tileLayerSources = (json.tileLayerSources??[]).map((config, i) => new TilesourceConfig(config, `${this.id}.tileLayerSources[${i}]`)) this.layers = LayoutConfig.ExtractLayers(json, official, context); - // ALl the layers are constructed, let them share tagRenderings now! - const roaming: { r, source: LayerConfig }[] = [] - for (const layer of this.layers) { - roaming.push({r: layer.GetRoamingRenderings(), source: layer}); - } - - for (const layer of this.layers) { - for (const r of roaming) { - if (r.source == layer) { - continue; - } - layer.AddRoamingRenderings(r.r); - } - } - - for (const layer of this.layers) { - layer.AddRoamingRenderings( - { - titleIcons: [], - iconOverlays: [], - tagRenderings: this.roamingRenderings - } - ); - } - this.clustering = { maxZoom: 16, minNeededElements: 25, diff --git a/Models/ThemeConfig/PointRenderingConfig.ts b/Models/ThemeConfig/PointRenderingConfig.ts new file mode 100644 index 000000000..75f382029 --- /dev/null +++ b/Models/ThemeConfig/PointRenderingConfig.ts @@ -0,0 +1,244 @@ +import PointRenderingConfigJson from "./Json/PointRenderingConfigJson"; +import TagRenderingConfig from "./TagRenderingConfig"; +import {TagsFilter} from "../../Logic/Tags/TagsFilter"; +import SharedTagRenderings from "../../Customizations/SharedTagRenderings"; +import {TagUtils} from "../../Logic/Tags/TagUtils"; +import {Utils} from "../../Utils"; +import Svg from "../../Svg"; +import WithContextLoader from "./WithContextLoader"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import BaseUIElement from "../../UI/BaseUIElement"; +import {FixedUiElement} from "../../UI/Base/FixedUiElement"; +import Img from "../../UI/Base/Img"; +import Combine from "../../UI/Base/Combine"; +import {VariableUiElement} from "../../UI/Base/VariableUIElement"; + +export default class PointRenderingConfig extends WithContextLoader { + + public readonly icon: TagRenderingConfig; + public readonly iconOverlays: { if: TagsFilter; then: TagRenderingConfig; badge: boolean }[]; + public readonly iconSize: TagRenderingConfig; + public readonly label: TagRenderingConfig; + public readonly rotation: TagRenderingConfig; + + constructor(json: PointRenderingConfigJson, context: string) { + super(json, context) + this.icon = this.tr("icon", ""); + this.iconOverlays = (json.iconOverlays ?? []).map((overlay, i) => { + let tr = new TagRenderingConfig( + overlay.then, + undefined, + `iconoverlays.${i}` + ); + if ( + typeof overlay.then === "string" && + SharedTagRenderings.SharedIcons.get(overlay.then) !== undefined + ) { + tr = SharedTagRenderings.SharedIcons.get(overlay.then); + } + return { + if: TagUtils.Tag(overlay.if), + then: tr, + badge: overlay.badge ?? false, + }; + }); + + const iconPath = this.icon.GetRenderValue({id: "node/-1"}).txt; + if (iconPath.startsWith(Utils.assets_path)) { + const iconKey = iconPath.substr(Utils.assets_path.length); + if (Svg.All[iconKey] === undefined) { + throw "Builtin SVG asset not found: " + iconPath; + } + } + this.iconSize = this.tr("iconSize", "40,40,center"); + this.label = this.tr("label", ""); + this.rotation = this.tr("rotation", "0"); + } + + + public ExtractImages(): Set { + const parts: Set[] = []; + parts.push(this.icon?.ExtractImages(true)); + parts.push( + ...this.iconOverlays?.map((overlay) => overlay.then.ExtractImages(true)) + ); + + const allIcons = new Set(); + for (const part of parts) { + part?.forEach(allIcons.add, allIcons); + } + return allIcons; + } + + + + public GenerateLeafletStyle( + tags: UIEventSource, + clickable: boolean + ): + { + html: BaseUIElement; + iconSize: [number, number]; + iconAnchor: [number, number]; + popupAnchor: [number, number]; + iconUrl: string; + className: string; + } + { + function num(str, deflt = 40) { + const n = Number(str); + if (isNaN(n)) { + return deflt; + } + return n; + } + + function rendernum(tr: TagRenderingConfig, deflt: number) { + const str = Number(render(tr, "" + deflt)); + const n = Number(str); + if (isNaN(n)) { + return deflt; + } + return n; + } + + function render(tr: TagRenderingConfig, deflt?: string) { + if (tags === undefined) { + return deflt + } + const str = tr?.GetRenderValue(tags.data)?.txt ?? deflt; + return Utils.SubstituteKeys(str, tags.data).replace(/{.*}/g, ""); + } + + const iconSize = render(this.iconSize, "40,40,center").split(","); + + const iconW = num(iconSize[0]); + let iconH = num(iconSize[1]); + const mode = iconSize[2]?.trim()?.toLowerCase() ?? "center"; + + let anchorW = iconW / 2; + let anchorH = iconH / 2; + if (mode === "left") { + anchorW = 0; + } + if (mode === "right") { + anchorW = iconW; + } + + if (mode === "top") { + anchorH = 0; + } + if (mode === "bottom") { + anchorH = iconH; + } + + const iconUrlStatic = render(this.icon); + const self = this; + + function genHtmlFromString(sourcePart: string, rotation: string): BaseUIElement { + const style = `width:100%;height:100%;transform: rotate( ${rotation} );display:block;position: absolute; top: 0; left: 0`; + let html: BaseUIElement = new FixedUiElement( + `` + ); + const match = sourcePart.match(/([a-zA-Z0-9_]*):([^;]*)/); + if (match !== null && Svg.All[match[1] + ".svg"] !== undefined) { + html = new Img( + (Svg.All[match[1] + ".svg"] as string).replace( + /#000000/g, + match[2] + ), + true + ).SetStyle(style); + } + return html; + } + + + const mappedHtml = tags?.map((tgs) => { + // What do you mean, 'tgs' is never read? + // It is read implicitly in the 'render' method + const iconUrl = render(self.icon); + const rotation = render(self.rotation, "0deg"); + + let htmlParts: BaseUIElement[] = []; + let sourceParts = Utils.NoNull( + iconUrl.split(";").filter((prt) => prt != "") + ); + for (const sourcePart of sourceParts) { + htmlParts.push(genHtmlFromString(sourcePart, rotation)); + } + + let badges = []; + for (const iconOverlay of self.iconOverlays) { + if (!iconOverlay.if.matchesProperties(tgs)) { + continue; + } + if (iconOverlay.badge) { + const badgeParts: BaseUIElement[] = []; + const renderValue = iconOverlay + .then + .GetRenderValue(tgs) + + if (renderValue === undefined) { + continue; + } + + const partDefs = renderValue.txt.split(";") + .filter((prt) => prt != ""); + + for (const badgePartStr of partDefs) { + badgeParts.push(genHtmlFromString(badgePartStr, "0")); + } + + const badgeCompound = new Combine(badgeParts).SetStyle( + "display:flex;position:relative;width:100%;height:100%;" + ); + + badges.push(badgeCompound); + } else { + htmlParts.push( + genHtmlFromString(iconOverlay.then.GetRenderValue(tgs).txt, "0") + ); + } + } + + if (badges.length > 0) { + const badgesComponent = new Combine(badges).SetStyle( + "display:flex;height:50%;width:100%;position:absolute;top:50%;left:50%;" + ); + htmlParts.push(badgesComponent); + } + + if (sourceParts.length == 0) { + iconH = 0; + } + try { + const label = self.label + ?.GetRenderValue(tgs) + ?.Subs(tgs) + ?.SetClass("block text-center") + ?.SetStyle("margin-top: " + (iconH + 2) + "px"); + if (label !== undefined) { + htmlParts.push( + new Combine([label]).SetClass("flex flex-col items-center") + ); + } + } catch (e) { + console.error(e, tgs); + } + return new Combine(htmlParts); + }); + + return { + html: mappedHtml === undefined ? new FixedUiElement(self.icon.render.txt) : new VariableUiElement(mappedHtml), + iconSize: [iconW, iconH], + iconAnchor: [anchorW, anchorH], + popupAnchor: [0, 3 - anchorH], + iconUrl: iconUrlStatic, + className: clickable + ? "leaflet-div-icon" + : "leaflet-div-icon unclickable", + }; + } + +} \ No newline at end of file diff --git a/Models/ThemeConfig/TagRenderingConfig.ts b/Models/ThemeConfig/TagRenderingConfig.ts index 4edd0da1d..a498894d1 100644 --- a/Models/ThemeConfig/TagRenderingConfig.ts +++ b/Models/ThemeConfig/TagRenderingConfig.ts @@ -221,7 +221,6 @@ export default class TagRenderingConfig { } } - /** * Returns true if it is known or not shown, false if the question should be asked * @constructor diff --git a/Models/ThemeConfig/WithContextLoader.ts b/Models/ThemeConfig/WithContextLoader.ts new file mode 100644 index 000000000..9a4758a23 --- /dev/null +++ b/Models/ThemeConfig/WithContextLoader.ts @@ -0,0 +1,117 @@ +import TagRenderingConfig from "./TagRenderingConfig"; +import SharedTagRenderings from "../../Customizations/SharedTagRenderings"; +import {TagRenderingConfigJson} from "./Json/TagRenderingConfigJson"; +import {Utils} from "../../Utils"; + +export default class WithContextLoader { + private readonly _json: any; + private readonly _context: string; + + constructor(json: any, context: string) { + this._json = json; + this._context = context; + } + + /** Given a key, gets the corresponding property from the json (or the default if not found + * + * The found value is interpreted as a tagrendering and fetched/parsed + * */ + public tr(key: string, deflt) { + const v = this._json[key]; + if (v === undefined || v === null) { + if (deflt === undefined) { + return undefined; + } + return new TagRenderingConfig( + deflt, + undefined, + `${this._context}.${key}.default value` + ); + } + if (typeof v === "string") { + const shared = SharedTagRenderings.SharedTagRendering.get(v); + if (shared) { + return shared; + } + } + return new TagRenderingConfig( + v, + undefined, + `${this._context}.${key}` + ); + } + + /** + * Converts a list of tagRenderingCOnfigJSON in to TagRenderingConfig + * A string is interpreted as a name to call + */ + public trs( + tagRenderings?: (string | { builtin: string, override: any } | TagRenderingConfigJson)[], + readOnly = false + ) { + if (tagRenderings === undefined) { + return []; + } + + const context = this._context + + const renderings: TagRenderingConfig[] = [] + + for (let i = 0; i < tagRenderings.length; i++) { + let renderingJson= tagRenderings[i] + if (typeof renderingJson === "string") { + renderingJson = {builtin: renderingJson, override: undefined} + } + + if (renderingJson["builtin"] !== undefined) { + const renderingId = renderingJson["builtin"] + if (renderingId === "questions") { + if (readOnly) { + throw `A tagrendering has a question, but asking a question does not make sense here: is it a title icon or a geojson-layer? ${context}. The offending tagrendering is ${JSON.stringify( + renderingJson + )}`; + } + + const tr = new TagRenderingConfig("questions", undefined, context); + renderings.push(tr) + continue; + } + + if (renderingJson["override"] !== undefined) { + const sharedJson = SharedTagRenderings.SharedTagRenderingJson.get(renderingId) + const tr = new TagRenderingConfig( + Utils.Merge(renderingJson["override"], sharedJson), + undefined, + `${context}.tagrendering[${i}]+override` + ); + renderings.push(tr) + continue + } + + const shared = SharedTagRenderings.SharedTagRendering.get(renderingId); + + if (shared !== undefined) { + renderings.push( shared) + continue + } + if (Utils.runningFromConsole) { + continue + } + + const keys = Array.from( SharedTagRenderings.SharedTagRendering.keys() ); + throw `Predefined tagRendering ${renderingId} not found in ${context}.\n Try one of ${keys.join( + ", " + )}\n If you intent to output this text literally, use {\"render\": } instead"}`; + } + + const tr = new TagRenderingConfig( + renderingJson, + undefined, + `${context}.tagrendering[${i}]` + ); + renderings.push(tr) + } + + return renderings; + } +} \ No newline at end of file diff --git a/scripts/lint.ts b/scripts/lint.ts index a4f3902a6..35632ed20 100644 --- a/scripts/lint.ts +++ b/scripts/lint.ts @@ -1,4 +1,3 @@ - /* * This script reads all theme and layer files and reformats them inplace * Use with caution, make a commit beforehand! @@ -6,29 +5,47 @@ import ScriptUtils from "./ScriptUtils"; -import {readFileSync, writeFileSync} from "fs"; -import {tag} from "@turf/turf"; +import {writeFileSync} from "fs"; import {LayerConfigJson} from "../Models/ThemeConfig/Json/LayerConfigJson"; /** * In place fix */ -function fixLayerConfig(config: LayerConfigJson) : void{ - if(config.tagRenderings === undefined){ - return - } - - for (const tagRendering of config.tagRenderings) { - if(tagRendering["#"] !== undefined){ - tagRendering["id"] = tagRendering["#"] - delete tagRendering["#"] - } - if(tagRendering["id"] === undefined){ - if(tagRendering["freeform"]?.key !== undefined ) { - tagRendering["id"] = config.id+"-"+tagRendering["freeform"]["key"] +function fixLayerConfig(config: LayerConfigJson): void { + if (config.tagRenderings !== undefined) { + for (const tagRendering of config.tagRenderings) { + if (tagRendering["#"] !== undefined) { + tagRendering["id"] = tagRendering["#"] + delete tagRendering["#"] + } + if (tagRendering["id"] === undefined) { + if (tagRendering["freeform"]?.key !== undefined) { + tagRendering["id"] = config.id + "-" + tagRendering["freeform"]["key"] + } } } } + + if(config.mapRendering === undefined){ + // This is a legacy format, lets create a pointRendering + let location: ("point"|"centroid")[] = ["point"] + if(config.wayHandling === 2){ + location = ["point", "centroid"] + } + config.mapRendering = [ + { + icon: config["icon"], + iconOverlays: config["iconOverlays"], + label: config["label"], + iconSize: config["iconSize"], + location, + rotation: config["rotation"] + } + ] + + + } + } const layerFiles = ScriptUtils.getLayerFiles(); @@ -40,7 +57,7 @@ for (const layerFile of layerFiles) { const themeFiles = ScriptUtils.getThemeFiles() for (const themeFile of themeFiles) { for (const layerConfig of themeFile.parsed.layers ?? []) { - if(typeof layerConfig === "string" || layerConfig["builtin"]!== undefined){ + if (typeof layerConfig === "string" || layerConfig["builtin"] !== undefined) { continue } // @ts-ignore From 1852eb8e522386a9c3aacf75d687aaa690c3102c Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 19 Oct 2021 03:00:57 +0200 Subject: [PATCH 04/95] Refactoring: moved pointRenderingConfig into seperate part of the configuration, removed roaming rendering capabilities --- Customizations/SharedTagRenderings.ts | 2 +- Models/ThemeConfig/PointRenderingConfig.ts | 10 - Models/ThemeConfig/TagRenderingConfig.ts | 11 +- Models/ThemeConfig/WithContextLoader.ts | 6 +- UI/Input/LocationInput.ts | 69 ++++--- UI/Popup/DeleteWizard.ts | 2 +- UI/Popup/FeatureInfoBox.ts | 4 +- UI/Popup/SplitRoadWizard.ts | 11 +- assets/layers/artwork/artwork.json | 13 +- assets/layers/barrier/barrier.json | 8 + assets/layers/bench/bench.json | 15 +- assets/layers/bench_at_pt/bench_at_pt.json | 15 +- .../bicycle_library/bicycle_library.json | 28 ++- .../bicycle_tube_vending_machine.json | 26 ++- assets/layers/bike_cafe/bike_cafe.json | 16 +- .../layers/bike_cleaning/bike_cleaning.json | 28 ++- assets/layers/bike_parking/bike_parking.json | 14 +- .../bike_repair_station.json | 75 ++++++- assets/layers/bike_shop/bike_shop.json | 38 +++- .../bike_themed_object.json | 16 +- assets/layers/binocular/binocular.json | 15 +- assets/layers/birdhide/birdhide.json | 26 ++- assets/layers/cafe_pub/cafe_pub.json | 33 ++- .../charging_station/charging_station.json | 64 ++++++ .../layers/cluster_style/cluster_style.json | 18 +- assets/layers/crossings/crossings.json | 21 ++ .../cycleways_and_roads.json | 15 +- .../layers/defibrillator/defibrillator.json | 23 ++- assets/layers/direction/direction.json | 24 ++- .../layers/drinking_water/drinking_water.json | 25 ++- assets/layers/etymology/etymology.json | 26 ++- assets/layers/food/food.json | 54 ++++- assets/layers/ghost_bike/ghost_bike.json | 11 +- .../layers/grass_in_parks/grass_in_parks.json | 10 + .../layers/home_location/home_location.json | 15 +- .../information_board/information_board.json | 15 +- assets/layers/map/map.json | 44 +++- .../layers/nature_reserve/nature_reserve.json | 14 ++ .../observation_tower/observation_tower.json | 15 +- assets/layers/parking/parking.json | 15 +- assets/layers/picnic_table/picnic_table.json | 15 +- assets/layers/play_forest/play_forest.json | 16 +- assets/layers/playground/playground.json | 46 ++++- .../public_bookcase/public_bookcase.json | 19 ++ assets/layers/shops/shops.json | 23 ++- assets/layers/slow_roads/slow_roads.json | 10 +- assets/layers/sport_pitch/sport_pitch.json | 73 +++++++ .../surveillance_camera.json | 54 ++++- assets/layers/toilet/toilet.json | 27 ++- assets/layers/trail/trail.json | 25 ++- assets/layers/tree_node/tree_node.json | 33 ++- assets/layers/viewpoint/viewpoint.json | 9 + .../layers/village_green/village_green.json | 10 + .../visitor_information_centre.json | 15 +- assets/layers/waste_basket/waste_basket.json | 29 ++- assets/layers/watermill/watermill.json | 15 +- assets/themes/aed/aed_brugge.json | 17 ++ assets/themes/buurtnatuur/buurtnatuur.json | 39 ++++ assets/themes/campersite/campersite.json | 39 +++- assets/themes/climbing/climbing.json | 84 +++++++- .../themes/cycle_highways/cycle_highways.json | 7 + assets/themes/cyclestreets/cyclestreets.json | 24 +++ .../themes/facadegardens/facadegardens.json | 73 ++++++- assets/themes/fruit_trees/fruit_trees.json | 26 +++ assets/themes/grb.json | 21 +- assets/themes/hackerspaces/hackerspaces.json | 29 ++- assets/themes/hailhydrant/hailhydrant.json | 61 +++++- assets/themes/natuurpunt/natuurpunt.json | 188 +++++++++++------- .../openwindpowermap/openwindpowermap.json | 17 ++ assets/themes/postboxes/postboxes.json | 37 +++- assets/themes/speelplekken/speelplekken.json | 19 +- assets/themes/uk_addresses/uk_addresses.json | 63 +++++- css/index-tailwind-output.css | 4 + test/Tag.spec.ts | 4 +- 74 files changed, 1868 insertions(+), 193 deletions(-) diff --git a/Customizations/SharedTagRenderings.ts b/Customizations/SharedTagRenderings.ts index 035a61dcc..0f47ef9a9 100644 --- a/Customizations/SharedTagRenderings.ts +++ b/Customizations/SharedTagRenderings.ts @@ -15,7 +15,7 @@ export default class SharedTagRenderings { const d = new Map() for (const key of Array.from(configJsons.keys())) { try { - d.set(key, new TagRenderingConfig(configJsons.get(key), undefined, `SharedTagRenderings.${key}`)) + d.set(key, new TagRenderingConfig(configJsons.get(key), `SharedTagRenderings.${key}`)) } catch (e) { if (!Utils.runningFromConsole) { console.error("BUG: could not parse", key, " from questions.json or icons.json - this error happened during the build step of the SharedTagRenderings", e) diff --git a/Models/ThemeConfig/PointRenderingConfig.ts b/Models/ThemeConfig/PointRenderingConfig.ts index 75f382029..51cfc18d6 100644 --- a/Models/ThemeConfig/PointRenderingConfig.ts +++ b/Models/ThemeConfig/PointRenderingConfig.ts @@ -27,7 +27,6 @@ export default class PointRenderingConfig extends WithContextLoader { this.iconOverlays = (json.iconOverlays ?? []).map((overlay, i) => { let tr = new TagRenderingConfig( overlay.then, - undefined, `iconoverlays.${i}` ); if ( @@ -93,15 +92,6 @@ export default class PointRenderingConfig extends WithContextLoader { return n; } - function rendernum(tr: TagRenderingConfig, deflt: number) { - const str = Number(render(tr, "" + deflt)); - const n = Number(str); - if (isNaN(n)) { - return deflt; - } - return n; - } - function render(tr: TagRenderingConfig, deflt?: string) { if (tags === undefined) { return deflt diff --git a/Models/ThemeConfig/TagRenderingConfig.ts b/Models/ThemeConfig/TagRenderingConfig.ts index a498894d1..42c8e60ea 100644 --- a/Models/ThemeConfig/TagRenderingConfig.ts +++ b/Models/ThemeConfig/TagRenderingConfig.ts @@ -37,9 +37,8 @@ export default class TagRenderingConfig { readonly then: Translation readonly hideInAnswer: boolean | TagsFilter }[] - readonly roaming: boolean; - constructor(json: string | TagRenderingConfigJson, conditionIfRoaming: TagsFilter, context?: string) { + constructor(json: string | TagRenderingConfigJson, context?: string) { if (json === "questions") { // Very special value @@ -61,13 +60,7 @@ export default class TagRenderingConfig { this.id = json.id ?? ""; this.render = Translations.T(json.render, context + ".render"); this.question = Translations.T(json.question, context + ".question"); - this.roaming = json.roaming ?? false; - const condition = TagUtils.Tag(json.condition ?? {"and": []}, `${context}.condition`); - if (this.roaming && conditionIfRoaming !== undefined) { - this.condition = new And([condition, conditionIfRoaming]); - } else { - this.condition = condition; - } + this.condition = TagUtils.Tag(json.condition ?? {"and": []}, `${context}.condition`); if (json.freeform) { if(json.freeform.addExtraTags !== undefined && json.freeform.addExtraTags.map === undefined){ diff --git a/Models/ThemeConfig/WithContextLoader.ts b/Models/ThemeConfig/WithContextLoader.ts index 9a4758a23..cfdc5439b 100644 --- a/Models/ThemeConfig/WithContextLoader.ts +++ b/Models/ThemeConfig/WithContextLoader.ts @@ -24,7 +24,6 @@ export default class WithContextLoader { } return new TagRenderingConfig( deflt, - undefined, `${this._context}.${key}.default value` ); } @@ -36,7 +35,6 @@ export default class WithContextLoader { } return new TagRenderingConfig( v, - undefined, `${this._context}.${key}` ); } @@ -72,7 +70,7 @@ export default class WithContextLoader { )}`; } - const tr = new TagRenderingConfig("questions", undefined, context); + const tr = new TagRenderingConfig("questions", context); renderings.push(tr) continue; } @@ -81,7 +79,6 @@ export default class WithContextLoader { const sharedJson = SharedTagRenderings.SharedTagRenderingJson.get(renderingId) const tr = new TagRenderingConfig( Utils.Merge(renderingJson["override"], sharedJson), - undefined, `${context}.tagrendering[${i}]+override` ); renderings.push(tr) @@ -106,7 +103,6 @@ export default class WithContextLoader { const tr = new TagRenderingConfig( renderingJson, - undefined, `${context}.tagrendering[${i}]` ); renderings.push(tr) diff --git a/UI/Input/LocationInput.ts b/UI/Input/LocationInput.ts index 8e5474c33..3ff7fe5da 100644 --- a/UI/Input/LocationInput.ts +++ b/UI/Input/LocationInput.ts @@ -6,7 +6,6 @@ import BaseLayer from "../../Models/BaseLayer"; import Combine from "../Base/Combine"; import Svg from "../../Svg"; import State from "../../State"; -import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"; import {GeoOperations} from "../../Logic/GeoOperations"; import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer"; import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"; @@ -16,7 +15,6 @@ import {FixedUiElement} from "../Base/FixedUiElement"; import ShowDataLayer from "../ShowDataLayer/ShowDataLayer"; import BaseUIElement from "../BaseUIElement"; import Toggle from "./Toggle"; -import {start} from "repl"; export default class LocationInput extends InputElement implements MinimapObj { @@ -25,12 +23,17 @@ export default class LocationInput extends InputElement implements MinimapO id: "matchpoint", source: { osmTags: {and: []} }, - icon: "./assets/svg/crosshair-empty.svg" + mapRendering: [{ + location: ["point"], + icon: "./assets/svg/crosshair-empty.svg" + }] }, "matchpoint icon", true ) - + IsSelected: UIEventSource = new UIEventSource(false); public readonly snappedOnto: UIEventSource = new UIEventSource(undefined) + public readonly _matching_layer: LayerConfig; + public readonly leafletMap: UIEventSource private _centerLocation: UIEventSource; private readonly mapBackground: UIEventSource; /** @@ -43,10 +46,7 @@ export default class LocationInput extends InputElement implements MinimapO private readonly _maxSnapDistance: number private readonly _snappedPointTags: any; private readonly _bounds: UIEventSource; - public readonly _matching_layer: LayerConfig; private readonly map: BaseUIElement & MinimapObj; - public readonly leafletMap: UIEventSource - private readonly clickLocation: UIEventSource; private readonly _minZoom: number; @@ -83,7 +83,7 @@ export default class LocationInput extends InputElement implements MinimapO } this._matching_layer = matchingLayer; } else { - this._matching_layer = LocationInput.matchLayer + this._matching_layer = LocationInput.matchLayer } this._snappedPoint = options.centerLocation.map(loc => { @@ -158,25 +158,29 @@ export default class LocationInput extends InputElement implements MinimapO IsValid(t: Loc): boolean { return t !== undefined; } - + + installBounds(factor: number | BBox, showRange?: boolean): void { + this.map.installBounds(factor, showRange) + } + protected InnerConstructElement(): HTMLElement { try { const self = this; const hasMoved = new UIEventSource(false) - const startLocation = { ...this._centerLocation.data } - this._centerLocation. addCallbackD(newLocation => { + const startLocation = {...this._centerLocation.data} + this._centerLocation.addCallbackD(newLocation => { const f = 100000 console.log(newLocation.lon, startLocation.lon) - const diff = (Math.abs(newLocation.lon * f - startLocation.lon* f ) + Math.abs(newLocation.lat* f - startLocation.lat* f )) - if(diff < 1){ + const diff = (Math.abs(newLocation.lon * f - startLocation.lon * f) + Math.abs(newLocation.lat * f - startLocation.lat * f)) + if (diff < 1) { return; } hasMoved.setData(true) return true; }) this.clickLocation.addCallbackAndRunD(location => this._centerLocation.setData(location)) - if (this._snapTo !== undefined) { - + if (this._snapTo !== undefined) { + // Show the lines to snap to new ShowDataMultiLayer({ features: new StaticFeatureSource(this._snapTo, true), @@ -184,7 +188,7 @@ export default class LocationInput extends InputElement implements MinimapO zoomToFeatures: false, leafletMap: this.map.leafletMap, layers: State.state.filteredLayers, - allElements: State.state.allElements + allElements: State.state.allElements } ) // Show the central point @@ -194,16 +198,16 @@ export default class LocationInput extends InputElement implements MinimapO } return [{feature: loc}]; }) - new ShowDataLayer({ - features: new StaticFeatureSource(matchPoint, true), - enablePopups: false, - zoomToFeatures: false, - leafletMap: this.map.leafletMap, - layerToShow: this._matching_layer, - allElements: State.state.allElements, - selectedElement: State.state.selectedElement - }) - + new ShowDataLayer({ + features: new StaticFeatureSource(matchPoint, true), + enablePopups: false, + zoomToFeatures: false, + leafletMap: this.map.leafletMap, + layerToShow: this._matching_layer, + allElements: State.state.allElements, + selectedElement: State.state.selectedElement + }) + } this.mapBackground.map(layer => { const leaflet = this.map.leafletMap.data @@ -216,19 +220,19 @@ export default class LocationInput extends InputElement implements MinimapO leaflet.setZoom(layer.max_zoom - 1) }, [this.map.leafletMap]) - + const animatedHand = Svg.hand_ui() .SetStyle("width: 2rem; height: unset;") .SetClass("hand-drag-animation block pointer-events-none") - + return new Combine([ new Combine([ Svg.move_arrows_ui() .SetClass("block relative pointer-events-none") .SetStyle("left: -2.5rem; top: -2.5rem; width: 5rem; height: 5rem") - ]).SetClass("block w-0 h-0 z-10 relative") + ]).SetClass("block w-0 h-0 z-10 relative") .SetStyle("background: rgba(255, 128, 128, 0.21); left: 50%; top: 50%; opacity: 0.5"), - + new Toggle(undefined, animatedHand, hasMoved) .SetClass("block w-0 h-0 z-10 relative") @@ -244,9 +248,4 @@ export default class LocationInput extends InputElement implements MinimapO } } - - installBounds(factor: number | BBox, showRange?: boolean): void { - this.map.installBounds(factor, showRange) - } - } \ No newline at end of file diff --git a/UI/Popup/DeleteWizard.ts b/UI/Popup/DeleteWizard.ts index 478737a9e..b0981f78b 100644 --- a/UI/Popup/DeleteWizard.ts +++ b/UI/Popup/DeleteWizard.ts @@ -264,7 +264,7 @@ export default class DeleteWizard extends Toggle { ] - }, undefined, "Delete wizard" + }, "Delete wizard" ) } diff --git a/UI/Popup/FeatureInfoBox.ts b/UI/Popup/FeatureInfoBox.ts index 155df8bdc..c31eff395 100644 --- a/UI/Popup/FeatureInfoBox.ts +++ b/UI/Popup/FeatureInfoBox.ts @@ -37,7 +37,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen { private static GenerateTitleBar(tags: UIEventSource, layerConfig: LayerConfig): BaseUIElement { - const title = new TagRenderingAnswer(tags, layerConfig.title ?? new TagRenderingConfig("POI", undefined)) + const title = new TagRenderingAnswer(tags, layerConfig.title ?? new TagRenderingConfig("POI")) .SetClass("break-words font-bold sm:p-0.5 md:p-1 sm:p-1.5 md:p-2"); const titleIcons = new Combine( layerConfig.titleIcons.map(icon => new TagRenderingAnswer(tags, icon, @@ -132,7 +132,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen { new VariableUiElement( State.state.featureSwitchIsDebugging.map(isDebugging => { if (isDebugging) { - const config: TagRenderingConfig = new TagRenderingConfig({render: "{all_tags()}"}, new Tag("id", ""), ""); + const config: TagRenderingConfig = new TagRenderingConfig({render: "{all_tags()}"}, ""); return new TagRenderingAnswer(tags, config, "all_tags") } }) diff --git a/UI/Popup/SplitRoadWizard.ts b/UI/Popup/SplitRoadWizard.ts index 9c321d8b9..2e2f38c85 100644 --- a/UI/Popup/SplitRoadWizard.ts +++ b/UI/Popup/SplitRoadWizard.ts @@ -21,8 +21,13 @@ export default class SplitRoadWizard extends Toggle { private static splitLayerStyling = new LayerConfig({ id: "splitpositions", source: {osmTags: "_cutposition=yes"}, - icon: {render: "circle:white;./assets/svg/scissors.svg"}, - iconSize: {render: "30,30,center"}, + mapRendering: [ + { + location: ["point"], + icon: {render: "circle:white;./assets/svg/scissors.svg"}, + iconSize: {render: "30,30,center"} + } + ], }, "(BUILTIN) SplitRoadWizard.ts", true) public dialogIsOpened: UIEventSource @@ -61,7 +66,7 @@ export default class SplitRoadWizard extends Toggle { miniMap.installBounds(BBox.get(roadElement).pad(0.25), false) // Define how a cut is displayed on the map - + // Datalayer displaying the road and the cut points (if any) new ShowDataLayer({ features: new StaticFeatureSource(splitPoints, true), diff --git a/assets/layers/artwork/artwork.json b/assets/layers/artwork/artwork.json index b89f83df7..82e27e9c7 100644 --- a/assets/layers/artwork/artwork.json +++ b/assets/layers/artwork/artwork.json @@ -394,5 +394,16 @@ "allowMove": { "enableRelocation": false, "enableImproveAccuraccy": true - } + }, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/artwork/artwork.svg" + }, + "location": [ + "point", + "centroid" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/barrier/barrier.json b/assets/layers/barrier/barrier.json index afa8bcfaf..8461abce3 100644 --- a/assets/layers/barrier/barrier.json +++ b/assets/layers/barrier/barrier.json @@ -323,5 +323,13 @@ }, "id": "Overlap (cyclebarrier)" } + ], + "mapRendering": [ + { + "icon": "./assets/layers/barrier/barrier.svg", + "location": [ + "point" + ] + } ] } \ No newline at end of file diff --git a/assets/layers/bench/bench.json b/assets/layers/bench/bench.json index ca08d1575..11587d5aa 100644 --- a/assets/layers/bench/bench.json +++ b/assets/layers/bench/bench.json @@ -644,5 +644,18 @@ "allowMove": { "enableRelocation": false, "enableImproveAccuraccy": true - } + }, + "mapRendering": [ + { + "icon": { + "render": "circle:#FE6F32;./assets/layers/bench/bench.svg" + }, + "iconSize": { + "render": "35,35,center" + }, + "location": [ + "point" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/bench_at_pt/bench_at_pt.json b/assets/layers/bench_at_pt/bench_at_pt.json index 29c759a63..c43ec58e6 100644 --- a/assets/layers/bench_at_pt/bench_at_pt.json +++ b/assets/layers/bench_at_pt/bench_at_pt.json @@ -148,5 +148,18 @@ }, "color": { "render": "#00f" - } + }, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/benches/bench_public_transport.svg" + }, + "iconSize": { + "render": "35,35,center" + }, + "location": [ + "point" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/bicycle_library/bicycle_library.json b/assets/layers/bicycle_library/bicycle_library.json index 735a45990..3d5dcc455 100644 --- a/assets/layers/bicycle_library/bicycle_library.json +++ b/assets/layers/bicycle_library/bicycle_library.json @@ -287,5 +287,31 @@ "color": { "render": "#c00" }, - "wayHandling": 2 + "wayHandling": 2, + "mapRendering": [ + { + "icon": { + "render": "pin:#22ff55;./assets/layers/bicycle_library/bicycle_library.svg" + }, + "iconOverlays": [ + { + "if": "opening_hours~*", + "then": "isOpen", + "badge": true + }, + { + "if": "service:bicycle:pump=yes", + "then": "circle:#e2783d;./assets/layers/bike_repair_station/pump.svg", + "badge": true + } + ], + "iconSize": { + "render": "50,50,bottom" + }, + "location": [ + "point", + "centroid" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/bicycle_tube_vending_machine/bicycle_tube_vending_machine.json b/assets/layers/bicycle_tube_vending_machine/bicycle_tube_vending_machine.json index 6f069aacf..5b44f6882 100644 --- a/assets/layers/bicycle_tube_vending_machine/bicycle_tube_vending_machine.json +++ b/assets/layers/bicycle_tube_vending_machine/bicycle_tube_vending_machine.json @@ -276,5 +276,29 @@ "allowMove": { "enableRelocation": false, "enableImproveAccuraccy": true - } + }, + "mapRendering": [ + { + "icon": { + "render": "pin:#ffffff;./assets/layers/bicycle_tube_vending_machine/pinIcon.svg" + }, + "iconOverlays": [ + { + "if": { + "or": [ + "operational_status=broken", + "operational_status=closed" + ] + }, + "then": "close:#c33", + "badge": true + } + ], + "iconSize": "50,50,bottom", + "location": [ + "point", + "centroid" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/bike_cafe/bike_cafe.json b/assets/layers/bike_cafe/bike_cafe.json index c3b328beb..53caf147b 100644 --- a/assets/layers/bike_cafe/bike_cafe.json +++ b/assets/layers/bike_cafe/bike_cafe.json @@ -365,5 +365,19 @@ ] } ], - "wayHandling": 2 + "wayHandling": 2, + "mapRendering": [ + { + "icon": { + "render": "./assets/layers/bike_cafe/bike_cafe.svg" + }, + "iconSize": { + "render": "50,50,bottom" + }, + "location": [ + "point", + "centroid" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/bike_cleaning/bike_cleaning.json b/assets/layers/bike_cleaning/bike_cleaning.json index 9d56e19a5..0d6217933 100644 --- a/assets/layers/bike_cleaning/bike_cleaning.json +++ b/assets/layers/bike_cleaning/bike_cleaning.json @@ -162,5 +162,31 @@ "allowMove": { "enableRelocation": false, "enableImproveAccuraccy": true - } + }, + "mapRendering": [ + { + "icon": { + "render": "./assets/layers/bike_cleaning/bike_cleaning.svg" + }, + "iconOverlays": [ + { + "if": { + "and": [ + "service:bicycle:cleaning~*", + "amenity!=bike_wash" + ] + }, + "then": { + "render": "./assets/layers/bike_cleaning/bike_cleaning_icon.svg", + "roaming": true + }, + "badge": true + } + ], + "iconSize": "50,50,bottom", + "location": [ + "point" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/bike_parking/bike_parking.json b/assets/layers/bike_parking/bike_parking.json index 380f97c37..94880bfe7 100644 --- a/assets/layers/bike_parking/bike_parking.json +++ b/assets/layers/bike_parking/bike_parking.json @@ -551,5 +551,17 @@ "allowMove": { "enableRelocation": false, "enableImproveAccuraccy": true - } + }, + "mapRendering": [ + { + "icon": { + "render": "./assets/layers/bike_parking/parking.svg" + }, + "iconSize": "40,40,bottom", + "location": [ + "point", + "centroid" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/bike_repair_station/bike_repair_station.json b/assets/layers/bike_repair_station/bike_repair_station.json index 8b78c689d..0a44bf6ce 100644 --- a/assets/layers/bike_repair_station/bike_repair_station.json +++ b/assets/layers/bike_repair_station/bike_repair_station.json @@ -768,5 +768,78 @@ "allowMove": { "enableRelocation": false, "enableImproveAccuraccy": true - } + }, + "mapRendering": [ + { + "icon": { + "render": { + "en": "./assets/layers/bike_repair_station/repair_station.svg", + "ru": "./assets/layers/bike_repair_station/repair_station.svg", + "it": "./assets/layers/bike_repair_station/repair_station.svg", + "fi": "./assets/layers/bike_repair_station/repair_station.svg", + "fr": "./assets/layers/bike_repair_station/repair_station.svg", + "pt_BR": "./assets/layers/bike_repair_station/repair_station.svg", + "de": "./assets/layers/bike_repair_station/repair_station.svg", + "pt": "./assets/layers/bike_repair_station/repair_station.svg" + }, + "mappings": [ + { + "if": { + "and": [ + "service:bicycle:pump=no", + "service:bicycle:pump:operational_status=broken" + ] + }, + "then": "./assets/layers/bike_repair_station/repair_station.svg" + }, + { + "if": { + "and": [ + "service:bicycle:pump=yes", + "service:bicycle:tools=yes" + ] + }, + "then": "./assets/layers/bike_repair_station/repair_station_pump.svg" + }, + { + "if": { + "and": [ + "service:bicycle:pump:operational_status=broken", + "service:bicycle:tools=no" + ] + }, + "then": "./assets/layers/bike_repair_station/broken_pump_2.svg" + }, + { + "if": { + "and": [ + "service:bicycle:pump=yes", + { + "or": [ + "service:bicycle:tools=no", + "service:bicycle:tools=" + ] + } + ] + }, + "then": "./assets/layers/bike_repair_station/pump.svg" + } + ] + }, + "iconOverlays": [ + { + "if": "operator=De Fietsambassade Gent", + "then": "./assets/themes/cyclofix/fietsambassade_gent_logo_small.svg", + "badge": true + } + ], + "iconSize": { + "render": "50,50,bottom" + }, + "location": [ + "point", + "centroid" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/bike_shop/bike_shop.json b/assets/layers/bike_shop/bike_shop.json index 061575037..0654df919 100644 --- a/assets/layers/bike_shop/bike_shop.json +++ b/assets/layers/bike_shop/bike_shop.json @@ -711,5 +711,41 @@ "color": { "render": "#c00" }, - "wayHandling": 2 + "wayHandling": 2, + "mapRendering": [ + { + "icon": { + "render": "./assets/layers/bike_shop/repair_shop.svg", + "mappings": [ + { + "if": "operator=De Fietsambassade Gent", + "then": "./assets/themes/cyclofix/fietsambassade_gent_logo_small.svg" + }, + { + "if": "service:bicycle:retail=yes", + "then": "./assets/layers/bike_shop/shop.svg" + } + ] + }, + "iconOverlays": [ + { + "if": "opening_hours~*", + "then": "isOpen", + "badge": true + }, + { + "if": "service:bicycle:pump=yes", + "then": "circle:#e2783d;./assets/layers/bike_repair_station/pump.svg", + "badge": true + } + ], + "iconSize": { + "render": "50,50,bottom" + }, + "location": [ + "point", + "centroid" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/bike_themed_object/bike_themed_object.json b/assets/layers/bike_themed_object/bike_themed_object.json index 527376742..a6653c8b7 100644 --- a/assets/layers/bike_themed_object/bike_themed_object.json +++ b/assets/layers/bike_themed_object/bike_themed_object.json @@ -69,5 +69,19 @@ "render": "#AB76D5" }, "presets": [], - "wayHandling": 2 + "wayHandling": 2, + "mapRendering": [ + { + "icon": { + "render": "./assets/layers/bike_themed_object/other_services.svg" + }, + "iconSize": { + "render": "50,50,bottom" + }, + "location": [ + "point", + "centroid" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/binocular/binocular.json b/assets/layers/binocular/binocular.json index 5b84365e2..4e44ad7d9 100644 --- a/assets/layers/binocular/binocular.json +++ b/assets/layers/binocular/binocular.json @@ -127,5 +127,18 @@ "allowMove": { "enableRelocation": false, "enableImproveAccuraccy": true - } + }, + "mapRendering": [ + { + "icon": { + "render": "circle:white;./assets/layers/binocular/telescope.svg" + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/birdhide/birdhide.json b/assets/layers/birdhide/birdhide.json index c8015e5f4..c09c7e958 100644 --- a/assets/layers/birdhide/birdhide.json +++ b/assets/layers/birdhide/birdhide.json @@ -309,5 +309,29 @@ "allowMove": { "enableRelocation": false, "enableImproveAccuraccy": true - } + }, + "mapRendering": [ + { + "icon": { + "render": { + "nl": "./assets/layers/birdhide/birdhide.svg" + }, + "mappings": [ + { + "if": { + "or": [ + "building=yes", + "shelter=yes", + "amenity=shelter" + ] + }, + "then": "./assets/layers/birdhide/birdshelter.svg" + } + ] + }, + "location": [ + "point" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/cafe_pub/cafe_pub.json b/assets/layers/cafe_pub/cafe_pub.json index cd5f6de2e..6fcbf9d14 100644 --- a/assets/layers/cafe_pub/cafe_pub.json +++ b/assets/layers/cafe_pub/cafe_pub.json @@ -203,5 +203,36 @@ ] } }, - "allowMove": true + "allowMove": true, + "mapRendering": [ + { + "icon": { + "render": "circle:white;./assets/layers/cafe_pub/pub.svg", + "mappings": [ + { + "if": "amenity=cafe", + "then": "circle:white;./assets/layers/cafe_pub/cafe.svg" + } + ] + }, + "iconOverlays": [ + { + "if": "opening_hours~*", + "then": "isOpen", + "badge": true + } + ], + "label": { + "mappings": [ + { + "if": "name~*", + "then": "
{name}
" + } + ] + }, + "location": [ + "point" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index 3e421b24d..67ebde7a0 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -3729,5 +3729,69 @@ ], "eraseInvalidValues": true } + ], + "mapRendering": [ + { + "icon": { + "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", + "mappings": [ + { + "if": "bicycle=yes", + "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" + }, + { + "if": { + "or": [ + "car=yes", + "motorcar=yes" + ] + }, + "then": "pin:#fff;./assets/themes/charging_stations/car.svg" + } + ] + }, + "iconOverlays": [ + { + "if": { + "or": [ + "disused:amenity=charging_station", + "operational_status=broken" + ] + }, + "then": "cross_bottom_right:#c22;" + }, + { + "if": { + "or": [ + "proposed:amenity=charging_station", + "planned:amenity=charging_station" + ] + }, + "then": "./assets/layers/charging_station/under_construction.svg", + "badge": true + }, + { + "if": { + "and": [ + "bicycle=yes", + { + "or": [ + "motorcar=yes", + "car=yes" + ] + } + ] + }, + "then": "circle:#fff;./assets/themes/charging_stations/car.svg", + "badge": true + } + ], + "iconSize": { + "render": "50,50,bottom" + }, + "location": [ + "point" + ] + } ] } \ No newline at end of file diff --git a/assets/layers/cluster_style/cluster_style.json b/assets/layers/cluster_style/cluster_style.json index 7e93c506a..16bd123c7 100644 --- a/assets/layers/cluster_style/cluster_style.json +++ b/assets/layers/cluster_style/cluster_style.json @@ -36,5 +36,21 @@ "then": "
{kilocount}K
" } ] - } + }, + "mapRendering": [ + { + "label": { + "render": "
{showCount}
", + "mappings": [ + { + "if": "showCount>1000", + "then": "
{kilocount}K
" + } + ] + }, + "location": [ + "point" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/crossings/crossings.json b/assets/layers/crossings/crossings.json index 3b71f3f1e..a4e005b6e 100644 --- a/assets/layers/crossings/crossings.json +++ b/assets/layers/crossings/crossings.json @@ -331,5 +331,26 @@ } ] } + ], + "mapRendering": [ + { + "icon": { + "render": "./assets/layers/crossings/pedestrian_crossing.svg", + "mappings": [ + { + "if": { + "or": [ + "highway=traffic_signals", + "crossing=traffic_signals" + ] + }, + "then": "./assets/layers/crossings/traffic_lights.svg" + } + ] + }, + "location": [ + "point" + ] + } ] } \ No newline at end of file diff --git a/assets/layers/cycleways_and_roads/cycleways_and_roads.json b/assets/layers/cycleways_and_roads/cycleways_and_roads.json index 8b10ac7b3..c6eb50400 100644 --- a/assets/layers/cycleways_and_roads/cycleways_and_roads.json +++ b/assets/layers/cycleways_and_roads/cycleways_and_roads.json @@ -1176,5 +1176,18 @@ } ] }, - "allowSplit": true + "allowSplit": true, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/cycle_infra/bicycleway.svg" + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/defibrillator/defibrillator.json b/assets/layers/defibrillator/defibrillator.json index 8a7fff1ec..a994361e4 100644 --- a/assets/layers/defibrillator/defibrillator.json +++ b/assets/layers/defibrillator/defibrillator.json @@ -553,5 +553,26 @@ "allowMove": { "enableRelocation": false, "enableImproveAccuraccy": true - } + }, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/aed/aed.svg", + "mappings": [ + { + "if": "_recently_surveyed=true", + "then": { + "en": "./assets/layers/defibrillator/aed_checked.svg", + "ru": "./assets/layers/defibrillator/aed_checked.svg", + "it": "./assets/layers/defibrillator/aed_checked.svg", + "fr": "./assets/layers/defibrillator/aed_checked.svg" + } + } + ] + }, + "location": [ + "point" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/direction/direction.json b/assets/layers/direction/direction.json index 4d3fd684d..cb5b8b7da 100644 --- a/assets/layers/direction/direction.json +++ b/assets/layers/direction/direction.json @@ -43,5 +43,27 @@ "color": "--catch-detail-color", "stroke": "0", "presets": [], - "wayHandling": 2 + "wayHandling": 2, + "mapRendering": [ + { + "icon": { + "render": "direction_gradient:var(--catch-detail-color)", + "#": "For some weird reason, showing the icon in the layer control panel breaks the svg-gradient (because the svg gradient has a global color or smthng) - so we use a different icon without gradient", + "mappings": [ + { + "if": "id=node/-1", + "then": "direction:var(--catch-detail-color)" + } + ] + }, + "iconSize": "200,200,center", + "location": [ + "point", + "centroid" + ], + "rotation": { + "render": "{_direction:numerical}deg" + } + } + ] } \ No newline at end of file diff --git a/assets/layers/drinking_water/drinking_water.json b/assets/layers/drinking_water/drinking_water.json index c6040e3af..76130921f 100644 --- a/assets/layers/drinking_water/drinking_water.json +++ b/assets/layers/drinking_water/drinking_water.json @@ -179,5 +179,28 @@ "allowMove": { "enableRelocation": false, "enableImproveAccuraccy": true - } + }, + "mapRendering": [ + { + "icon": { + "render": "pin:#6BC4F7;./assets/layers/drinking_water/drips.svg" + }, + "iconOverlays": [ + { + "if": { + "or": [ + "operational_status=broken", + "operational_status=closed" + ] + }, + "then": "close:#c33", + "badge": true + } + ], + "iconSize": "40,40,bottom", + "location": [ + "point" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/etymology/etymology.json b/assets/layers/etymology/etymology.json index 690612341..48cd87fe4 100644 --- a/assets/layers/etymology/etymology.json +++ b/assets/layers/etymology/etymology.json @@ -167,5 +167,29 @@ "then": "#fcca05aa" } ] - } + }, + "mapRendering": [ + { + "icon": { + "render": "pin:#05d7fcaa;./assets/layers/etymology/logo.svg", + "mappings": [ + { + "if": { + "and": [ + "name:etymology=", + "name:etymology:wikidata=" + ] + }, + "then": "pin:#fcca05aa;./assets/layers/etymology/logo.svg" + } + ] + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/food/food.json b/assets/layers/food/food.json index c2c966aac..3386e1b79 100644 --- a/assets/layers/food/food.json +++ b/assets/layers/food/food.json @@ -635,5 +635,57 @@ ] } }, - "allowMove": true + "allowMove": true, + "mapRendering": [ + { + "icon": { + "render": "circle:white;./assets/layers/food/restaurant.svg", + "mappings": [ + { + "if": { + "and": [ + "amenity=fast_food", + "cuisine=friture" + ] + }, + "then": "circle:white;./assets/layers/food/fries.svg" + }, + { + "if": "amenity=fast_food", + "then": "circle:white;./assets/layers/food/fastfood.svg" + } + ] + }, + "iconOverlays": [ + { + "if": "opening_hours~*", + "then": "isOpen", + "badge": true + }, + { + "if": { + "or": [ + "diet:vegetarian=yes", + "diet:vegan=yes" + ] + }, + "then": { + "render": "circle:white;./assets/themes/fritures/Vegetarian-mark.svg" + }, + "badge": true + } + ], + "label": { + "mappings": [ + { + "if": "name~*", + "then": "
{name}
" + } + ] + }, + "location": [ + "point" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/ghost_bike/ghost_bike.json b/assets/layers/ghost_bike/ghost_bike.json index de82c8bae..1ec2c9dab 100644 --- a/assets/layers/ghost_bike/ghost_bike.json +++ b/assets/layers/ghost_bike/ghost_bike.json @@ -202,5 +202,14 @@ "allowMove": { "enableRelocation": false, "enableImproveAccuraccy": true - } + }, + "mapRendering": [ + { + "icon": "./assets/layers/ghost_bike/ghost_bike.svg", + "iconSize": "40,40,bottom", + "location": [ + "point" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/grass_in_parks/grass_in_parks.json b/assets/layers/grass_in_parks/grass_in_parks.json index a95d6e0eb..321cebcb3 100644 --- a/assets/layers/grass_in_parks/grass_in_parks.json +++ b/assets/layers/grass_in_parks/grass_in_parks.json @@ -51,5 +51,15 @@ "id": "grass-in-parks-reviews", "render": "{reviews(name, landuse=grass )}" } + ], + "mapRendering": [ + { + "icon": "./assets/themes/playgrounds/playground.svg", + "iconSize": "40,40,center", + "location": [ + "point", + "centroid" + ] + } ] } \ No newline at end of file diff --git a/assets/layers/home_location/home_location.json b/assets/layers/home_location/home_location.json index 0bf07c75a..1cb233440 100644 --- a/assets/layers/home_location/home_location.json +++ b/assets/layers/home_location/home_location.json @@ -13,5 +13,18 @@ }, "color": { "render": "#00f" - } + }, + "mapRendering": [ + { + "icon": { + "render": "circle:white;./assets/svg/home.svg" + }, + "iconSize": { + "render": "20,20,center" + }, + "location": [ + "point" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/information_board/information_board.json b/assets/layers/information_board/information_board.json index 1f6b08c2d..2f93526a8 100644 --- a/assets/layers/information_board/information_board.json +++ b/assets/layers/information_board/information_board.json @@ -68,5 +68,18 @@ "allowMove": { "enableRelocation": false, "enableImproveAccuraccy": true - } + }, + "mapRendering": [ + { + "icon": { + "render": "./assets/layers/information_board/board.svg" + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/map/map.json b/assets/layers/map/map.json index b0031e573..ca7a5c389 100644 --- a/assets/layers/map/map.json +++ b/assets/layers/map/map.json @@ -240,5 +240,47 @@ "allowMove": { "enableRelocation": false, "enableImproveAccuraccy": true - } + }, + "mapRendering": [ + { + "icon": { + "render": "./assets/layers/map/map.svg", + "mappings": [ + { + "if": { + "and": [ + "map_source=OpenStreetMap", + "map_source:attribution=sticker" + ] + }, + "then": "./assets/layers/map/map-stickered.svg" + }, + { + "if": { + "and": [ + "map_source=OpenStreetMap", + "map_source:attribution=yes" + ] + }, + "then": "./assets/layers/map/osm-logo-white-bg.svg" + }, + { + "if": { + "and": [ + "map_source=OpenStreetMap" + ] + }, + "then": "./assets/layers/map/osm-logo-buggy-attr.svg" + } + ] + }, + "iconSize": { + "render": "50,50,center" + }, + "location": [ + "point", + "centroid" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/nature_reserve/nature_reserve.json b/assets/layers/nature_reserve/nature_reserve.json index 786f5e87b..28b27071c 100644 --- a/assets/layers/nature_reserve/nature_reserve.json +++ b/assets/layers/nature_reserve/nature_reserve.json @@ -461,5 +461,19 @@ } ] } + ], + "mapRendering": [ + { + "icon": { + "render": "./assets/layers/nature_reserve/nature_reserve.svg" + }, + "iconSize": { + "render": "50,50,center" + }, + "location": [ + "point", + "centroid" + ] + } ] } \ No newline at end of file diff --git a/assets/layers/observation_tower/observation_tower.json b/assets/layers/observation_tower/observation_tower.json index 20f92ad8b..d40c9b81b 100644 --- a/assets/layers/observation_tower/observation_tower.json +++ b/assets/layers/observation_tower/observation_tower.json @@ -189,5 +189,18 @@ "allowMove": { "enableRelocation": false, "enableImproveAccuraccy": true - } + }, + "mapRendering": [ + { + "icon": { + "render": "circle:white;./assets/layers/observation_tower/Tower_observation.svg" + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/parking/parking.json b/assets/layers/parking/parking.json index b428d5f99..b2eccdd9e 100644 --- a/assets/layers/parking/parking.json +++ b/assets/layers/parking/parking.json @@ -94,5 +94,18 @@ "allowMove": { "enableRelocation": false, "enableImproveAccuraccy": true - } + }, + "mapRendering": [ + { + "icon": { + "render": "./assets/layers/parking/parking.svg" + }, + "iconSize": { + "render": "36,36,center" + }, + "location": [ + "point" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/picnic_table/picnic_table.json b/assets/layers/picnic_table/picnic_table.json index e4cdc4ac2..cf06e950b 100644 --- a/assets/layers/picnic_table/picnic_table.json +++ b/assets/layers/picnic_table/picnic_table.json @@ -114,5 +114,18 @@ "allowMove": { "enableRelocation": false, "enableImproveAccuraccy": true - } + }, + "mapRendering": [ + { + "icon": { + "render": "circle:#e6cf39;./assets/layers/picnic_table/picnic_table.svg" + }, + "iconSize": { + "render": "35,35,center" + }, + "location": [ + "point" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/play_forest/play_forest.json b/assets/layers/play_forest/play_forest.json index 0b1f2485e..257f9249c 100644 --- a/assets/layers/play_forest/play_forest.json +++ b/assets/layers/play_forest/play_forest.json @@ -116,5 +116,19 @@ "description": "Een zone in het bos, duidelijk gemarkeerd als speelzone met de overeenkomstige borden.
" } ], - "wayHandling": 2 + "wayHandling": 2, + "mapRendering": [ + { + "icon": { + "render": "./assets/layers/play_forest/icon.svg" + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point", + "centroid" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/playground/playground.json b/assets/layers/playground/playground.json index 1baaab7cc..eb489ed71 100644 --- a/assets/layers/playground/playground.json +++ b/assets/layers/playground/playground.json @@ -535,5 +535,49 @@ "leisure=" ] } - } + }, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/playgrounds/playground.svg" + }, + "iconOverlays": [ + { + "if": { + "and": [ + "opening_hours!=24/7", + "opening_hours~*" + ] + }, + "then": "isOpen", + "badge": true + } + ], + "iconSize": { + "render": "40,40,center", + "mappings": [ + { + "if": "id~node/.*", + "then": "40,40,center" + }, + { + "if": "_size_classification=small", + "then": "25,25,center" + }, + { + "if": "_size_classification=medium", + "then": "40,40,center" + }, + { + "if": "_size_classification=large", + "then": "60,60,center" + } + ] + }, + "location": [ + "point", + "centroid" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/public_bookcase/public_bookcase.json b/assets/layers/public_bookcase/public_bookcase.json index 64b6230c1..3f9abe815 100644 --- a/assets/layers/public_bookcase/public_bookcase.json +++ b/assets/layers/public_bookcase/public_bookcase.json @@ -485,5 +485,24 @@ } ] } + ], + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/bookcases/bookcase.svg;" + }, + "label": { + "mappings": [ + { + "if": "name~*", + "then": "
{name}
" + } + ] + }, + "location": [ + "point", + "centroid" + ] + } ] } \ No newline at end of file diff --git a/assets/layers/shops/shops.json b/assets/layers/shops/shops.json index 1e5679d48..15ced87ad 100644 --- a/assets/layers/shops/shops.json +++ b/assets/layers/shops/shops.json @@ -322,5 +322,26 @@ ] } }, - "allowMove": true + "allowMove": true, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/shops/shop.svg" + }, + "iconOverlays": [ + { + "if": "opening_hours~*", + "then": "isOpen", + "badge": true + } + ], + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point", + "centroid" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/slow_roads/slow_roads.json b/assets/layers/slow_roads/slow_roads.json index 50e7cd61d..f1624c853 100644 --- a/assets/layers/slow_roads/slow_roads.json +++ b/assets/layers/slow_roads/slow_roads.json @@ -252,5 +252,13 @@ "color": { "render": "#eaba2a" }, - "presets": [] + "presets": [], + "mapRendering": [ + { + "icon": "./assets/layers/slow_roads/slow_road.svg", + "location": [ + "point" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/sport_pitch/sport_pitch.json b/assets/layers/sport_pitch/sport_pitch.json index 90f1dca73..cc39de256 100644 --- a/assets/layers/sport_pitch/sport_pitch.json +++ b/assets/layers/sport_pitch/sport_pitch.json @@ -490,5 +490,78 @@ "fixme=Toegevoegd met MapComplete, geometry nog uit te tekenen" ] } + ], + "mapRendering": [ + { + "icon": { + "render": "circle:white;./assets/layers/sport_pitch/sport_pitch.svg", + "mappings": [ + { + "if": { + "or": [ + "sport=baseball", + "sport=basketball", + "sport=beachvolleyball", + "sport=boules", + "sport=skateboard", + "sport=soccer", + "sport=table_tennis", + "sport=tennis", + "sport=volleyball" + ] + }, + "then": "circle:white;./assets/layers/sport_pitch/{sport}.svg" + } + ] + }, + "iconOverlays": [ + { + "if": { + "and": [ + "opening_hours!=24/7", + "opening_hours~*" + ] + }, + "then": "isOpen", + "badge": true + }, + { + "if": { + "or": [ + "access=customers", + "access=private", + "access=no" + ] + }, + "then": "circle:white;./assets/layers/sport_pitch/lock.svg", + "badge": true + } + ], + "iconSize": { + "render": "25,25,center", + "mappings": [ + { + "if": { + "or": [ + "_size_classification=medium", + "id~node/.*" + ] + }, + "then": "40,40,center" + }, + { + "if": "_size_classification=small", + "then": "25,25,center" + }, + { + "if": "_size_classification=large", + "then": "50,50,center" + } + ] + }, + "location": [ + "point" + ] + } ] } \ No newline at end of file diff --git a/assets/layers/surveillance_camera/surveillance_camera.json b/assets/layers/surveillance_camera/surveillance_camera.json index 7472ffd80..e1efe7c79 100644 --- a/assets/layers/surveillance_camera/surveillance_camera.json +++ b/assets/layers/surveillance_camera/surveillance_camera.json @@ -480,5 +480,57 @@ "title": "Surveillance camera" } ], - "wayHandling": 2 + "wayHandling": 2, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/surveillance/logo.svg", + "mappings": [ + { + "if": "camera:type=dome", + "then": "./assets/themes/surveillance/dome.svg" + }, + { + "if": "_direction:leftright=right", + "then": "./assets/themes/surveillance/cam_right.svg" + }, + { + "if": "_direction:leftright=left", + "then": "./assets/themes/surveillance/cam_left.svg" + } + ] + }, + "iconSize": { + "mappings": [ + { + "if": "camera:type=dome", + "then": "50,50,center" + }, + { + "if": "_direction:leftright~*", + "then": "100,35,center" + } + ], + "render": "50,50,center" + }, + "location": [ + "point", + "centroid" + ], + "rotation": { + "#": "Note: {camera:direction} is substituted by a number, giving the string 'calc(123deg + 90deg)' ; it is this string that is used as css property, which interprets the calc", + "render": "calc({_direction:numerical}deg + 90deg)", + "mappings": [ + { + "if": "camera:type=dome", + "then": "0" + }, + { + "if": "_direction:leftright=right", + "then": "calc({_direction:numerical}deg - 90deg)" + } + ] + } + } + ] } \ No newline at end of file diff --git a/assets/layers/toilet/toilet.json b/assets/layers/toilet/toilet.json index 7b0366ca9..e30407251 100644 --- a/assets/layers/toilet/toilet.json +++ b/assets/layers/toilet/toilet.json @@ -511,5 +511,30 @@ "allowMove": { "enableRelocation": false, "enableImproveAccuraccy": true - } + }, + "mapRendering": [ + { + "icon": { + "render": "./assets/layers/toilet/toilets.svg", + "mappings": [ + { + "if": "wheelchair=yes", + "then": "circle:white;./assets/layers/toilet/wheelchair.svg" + }, + { + "if": { + "or": [ + "toilets:position=urinals", + "toilets:position=urinal" + ] + }, + "then": "./assets/layers/toilet/urinal.svg" + } + ] + }, + "location": [ + "point" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/trail/trail.json b/assets/layers/trail/trail.json index fa33cc095..e7579f24b 100644 --- a/assets/layers/trail/trail.json +++ b/assets/layers/trail/trail.json @@ -206,5 +206,28 @@ }, "dashArray": { "render": "5 5" - } + }, + "mapRendering": [ + { + "icon": { + "render": "./assets/layers/trail/trail.svg", + "mappings": [ + { + "if": "wheelchair=yes", + "then": "./assets/layers/trail/wheelchair.svg" + }, + { + "if": "pushchair=yes", + "then": "./assets/layers/trail/pushchair.svg" + } + ] + }, + "iconSize": { + "render": "35,35,center" + }, + "location": [ + "point" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/tree_node/tree_node.json b/assets/layers/tree_node/tree_node.json index 6d99b8382..428789429 100644 --- a/assets/layers/tree_node/tree_node.json +++ b/assets/layers/tree_node/tree_node.json @@ -582,5 +582,36 @@ }, "deletion": { "minNeededChangesets": 5 - } + }, + "mapRendering": [ + { + "icon": { + "render": "circle:#ffffff;./assets/themes/trees/unknown.svg", + "mappings": [ + { + "if": { + "and": [ + "leaf_type=broadleaved" + ] + }, + "then": "circle:#ffffff;./assets/themes/trees/broadleaved.svg" + }, + { + "if": { + "and": [ + "leaf_type=needleleaved" + ] + }, + "then": "circle:#ffffff;./assets/themes/trees/needleleaved.svg" + } + ] + }, + "iconSize": { + "render": "40,40,bottom" + }, + "location": [ + "point" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/viewpoint/viewpoint.json b/assets/layers/viewpoint/viewpoint.json index 6f34c3159..08c05640f 100644 --- a/assets/layers/viewpoint/viewpoint.json +++ b/assets/layers/viewpoint/viewpoint.json @@ -70,5 +70,14 @@ }, "id": "viewpoint-description" } + ], + "mapRendering": [ + { + "icon": "./assets/layers/viewpoint/viewpoint.svg", + "iconSize": "20,20,center", + "location": [ + "point" + ] + } ] } \ No newline at end of file diff --git a/assets/layers/village_green/village_green.json b/assets/layers/village_green/village_green.json index 9aa60fca4..447bfe0e9 100644 --- a/assets/layers/village_green/village_green.json +++ b/assets/layers/village_green/village_green.json @@ -35,5 +35,15 @@ "id": "village_green-reviews", "render": "{reviews(name, landuse=village_green )}" } + ], + "mapRendering": [ + { + "icon": "./assets/themes/playgrounds/playground.svg", + "iconSize": "40,40,center", + "location": [ + "point", + "centroid" + ] + } ] } \ No newline at end of file diff --git a/assets/layers/visitor_information_centre/visitor_information_centre.json b/assets/layers/visitor_information_centre/visitor_information_centre.json index 322fb4eab..2ad99d47c 100644 --- a/assets/layers/visitor_information_centre/visitor_information_centre.json +++ b/assets/layers/visitor_information_centre/visitor_information_centre.json @@ -66,5 +66,18 @@ "render": "#E64C00" }, "presets": [], - "wayHandling": 1 + "wayHandling": 1, + "mapRendering": [ + { + "icon": { + "render": "./assets/layers/visitor_information_centre/information.svg" + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/waste_basket/waste_basket.json b/assets/layers/waste_basket/waste_basket.json index 98296a942..9172dd452 100644 --- a/assets/layers/waste_basket/waste_basket.json +++ b/assets/layers/waste_basket/waste_basket.json @@ -177,5 +177,32 @@ "allowMove": { "enableRelocation": false, "enableImproveAccuraccy": true - } + }, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/waste_basket/waste_basket.svg" + }, + "iconSize": { + "render": "40,40,center", + "mappings": [ + { + "if": { + "and": [ + "amenity=waste_basket" + ] + }, + "then": { + "en": "Waste Basket", + "nl": "Vuilnisbak", + "ru": "Контейнер для мусора" + } + } + ] + }, + "location": [ + "point" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/watermill/watermill.json b/assets/layers/watermill/watermill.json index 1804424a7..1764ccc4f 100644 --- a/assets/layers/watermill/watermill.json +++ b/assets/layers/watermill/watermill.json @@ -171,5 +171,18 @@ }, "color": { "render": "#FFC0CB" - } + }, + "mapRendering": [ + { + "icon": { + "render": "./assets/layers/watermill/watermill.svg" + }, + "iconSize": { + "render": "50,50,center" + }, + "location": [ + "point" + ] + } + ] } \ No newline at end of file diff --git a/assets/themes/aed/aed_brugge.json b/assets/themes/aed/aed_brugge.json index aad99cd44..8e74aafb0 100644 --- a/assets/themes/aed/aed_brugge.json +++ b/assets/themes/aed/aed_brugge.json @@ -42,6 +42,23 @@ "iconSize": "20,20,center", "tagRenderings": [ "all_tags" + ], + "mapRendering": [ + { + "icon": { + "render": "circle:red", + "mappings": [ + { + "if": "_has_closeby_feature=yes", + "then": "circle:#008000aa" + } + ] + }, + "iconSize": "20,20,center", + "location": [ + "point" + ] + } ] } ], diff --git a/assets/themes/buurtnatuur/buurtnatuur.json b/assets/themes/buurtnatuur/buurtnatuur.json index 9c4f2a189..2b75a568d 100644 --- a/assets/themes/buurtnatuur/buurtnatuur.json +++ b/assets/themes/buurtnatuur/buurtnatuur.json @@ -124,6 +124,19 @@ "nl": "Voeg een ontbrekend, erkend natuurreservaat toe, bv. een gebied dat beheerd wordt door het ANB of natuurpunt" } } + ], + "mapRendering": [ + { + "icon": { + "render": "circle:#ffffff;./assets/themes/buurtnatuur/nature_reserve.svg" + }, + "iconSize": { + "render": "50,50,center" + }, + "location": [ + "point" + ] + } ] }, { @@ -222,6 +235,19 @@ "nl": "Voeg een ontbrekend park toe" } } + ], + "mapRendering": [ + { + "icon": { + "render": "circle:#ffffff;./assets/themes/buurtnatuur/park.svg" + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } ] }, { @@ -339,6 +365,19 @@ "nl": "Voeg een ontbrekend bos toe aan de kaart" } } + ], + "mapRendering": [ + { + "icon": { + "render": "circle:#ffffff;./assets/themes/buurtnatuur/forest.svg" + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } ] }, "viewpoint" diff --git a/assets/themes/campersite/campersite.json b/assets/themes/campersite/campersite.json index a255aed85..3742c22d6 100644 --- a/assets/themes/campersite/campersite.json +++ b/assets/themes/campersite/campersite.json @@ -676,7 +676,31 @@ } } ], - "wayHandling": 2 + "wayHandling": 2, + "mapRendering": [ + { + "icon": { + "render": "circle:white;./assets/themes/campersite/caravan.svg", + "mappings": [ + { + "if": { + "and": [ + "fee=no" + ] + }, + "then": "circle:white;./assets/themes/campersite/caravan_green.svg" + } + ] + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point", + "centroid" + ] + } + ] }, { "id": "dumpstations", @@ -1075,6 +1099,19 @@ "de": "Fügen Sie eine neue sanitäre Entsorgungsstation hinzu. Hier können Camper Abwasser oder chemischen Toilettenabfälle entsorgen. Oft gibt es auch Trinkwasser und Strom." } } + ], + "mapRendering": [ + { + "icon": { + "render": "circle:white;./assets/themes/campersite/sanitary_dump_station.svg" + }, + "iconSize": { + "render": "32,32,center" + }, + "location": [ + "point" + ] + } ] } ], diff --git a/assets/themes/climbing/climbing.json b/assets/themes/climbing/climbing.json index b5720d4ae..161dd5d8d 100644 --- a/assets/themes/climbing/climbing.json +++ b/assets/themes/climbing/climbing.json @@ -215,7 +215,27 @@ } } ], - "wayHandling": 1 + "wayHandling": 1, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/climbing/club.svg" + }, + "iconOverlays": [ + { + "if": "opening_hours~*", + "then": "isOpen", + "badge": true + } + ], + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } + ] }, { "id": "climbing_gym", @@ -313,7 +333,27 @@ "iconSize": { "render": "40,40,center" }, - "wayHandling": 1 + "wayHandling": 1, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/climbing/climbing_gym.svg" + }, + "iconOverlays": [ + { + "if": "opening_hours~*", + "then": "isOpen", + "badge": true + } + ], + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } + ] }, { "id": "climbing_route", @@ -532,7 +572,21 @@ ] } ], - "wayHandling": 2 + "wayHandling": 2, + "mapRendering": [ + { + "icon": { + "render": "circle:white;./assets/themes/climbing/climbing_route.svg" + }, + "iconSize": { + "render": "28,28,center" + }, + "location": [ + "point", + "centroid" + ] + } + ] }, { "id": "climbing", @@ -801,6 +855,20 @@ "_difficulty_hist=JSON.parse(feat.properties._contained_climbing_routes_properties ?? '[]').map(p => p['climbing:grade:french'])", "_length_hist=JSON.parse(feat.properties._contained_climbing_routes_properties ?? '[]').map(p => p['climbing:length'])", "_contained_climbing_routes_count=JSON.parse(feat.properties._contained_climbing_routes_properties ?? '[]').length" + ], + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/climbing/climbing_no_rope.svg" + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point", + "centroid" + ] + } ] }, { @@ -930,7 +998,15 @@ "color": { "render": "#ddff55AA" }, - "wayHandling": 0 + "wayHandling": 0, + "mapRendering": [ + { + "icon": "./assets/themes/climbing/climbing_unknown.svg", + "location": [ + "point" + ] + } + ] } ], "roamingRenderings": [ diff --git a/assets/themes/cycle_highways/cycle_highways.json b/assets/themes/cycle_highways/cycle_highways.json index 35f49b110..01653d649 100644 --- a/assets/themes/cycle_highways/cycle_highways.json +++ b/assets/themes/cycle_highways/cycle_highways.json @@ -233,6 +233,13 @@ } ] } + ], + "mapRendering": [ + { + "location": [ + "point" + ] + } ] } ], diff --git a/assets/themes/cyclestreets/cyclestreets.json b/assets/themes/cyclestreets/cyclestreets.json index 339267198..cb53867f6 100644 --- a/assets/themes/cyclestreets/cyclestreets.json +++ b/assets/themes/cyclestreets/cyclestreets.json @@ -183,6 +183,14 @@ "width": "10", "tagRenderings": [ "images" + ], + "mapRendering": [ + { + "icon": "./assets/themes/cyclestreets/F111.svg", + "location": [ + "point" + ] + } ] }, { @@ -235,6 +243,14 @@ "width": "5", "tagRenderings": [ "images" + ], + "mapRendering": [ + { + "icon": "./assets/themes/cyclestreets/F113.svg", + "location": [ + "point" + ] + } ] }, { @@ -300,6 +316,14 @@ }, "tagRenderings": [ "images" + ], + "mapRendering": [ + { + "icon": "./assets/svg/pencil.svg", + "location": [ + "point" + ] + } ] } ], diff --git a/assets/themes/facadegardens/facadegardens.json b/assets/themes/facadegardens/facadegardens.json index 526dd699c..3d584898f 100644 --- a/assets/themes/facadegardens/facadegardens.json +++ b/assets/themes/facadegardens/facadegardens.json @@ -454,7 +454,78 @@ } } ], - "wayHandling": 1 + "wayHandling": 1, + "mapRendering": [ + { + "icon": { + "render": "circle:white;./assets/themes/facadegardens/geveltuin.svg", + "mappings": [ + { + "if": { + "and": [ + "direct_sunlight=yes" + ] + }, + "then": "circle:white;./assets/themes/facadegardens/zon.svg" + }, + { + "if": { + "and": [ + "direct_sunlight=partial" + ] + }, + "then": "circle:white;./assets/themes/facadegardens/halfzon.svg" + }, + { + "if": { + "and": [ + "direct_sunlight=no" + ] + }, + "then": "circle:white;./assets/themes/facadegardens/schaduw.svg" + } + ] + }, + "iconOverlays": [ + { + "if": "plant~.*vine.*", + "then": "circle:white;./assets/themes/facadegardens/klimplant.svg", + "badge": true + }, + { + "if": "plant~.*groundcover.*", + "then": "circle:white;./assets/themes/facadegardens/bodembedekker.svg", + "badge": true + }, + { + "if": "edible=true", + "then": "circle:white;./assets/themes/facadegardens/eetbaar.svg", + "badge": true + }, + { + "if": "rain_barel=yes", + "then": "circle:white;./assets/themes/facadegardens/gevelton.svg", + "badge": true + }, + { + "if": "plant~.*shrub.*", + "then": "circle:white;./assets/themes/facadegardens/struik.svg", + "badge": true + }, + { + "if": "plant~.*flower.*", + "then": "circle:white;./assets/themes/facadegardens/bloei.svg", + "badge": true + } + ], + "iconSize": { + "render": "50,50,center" + }, + "location": [ + "point" + ] + } + ] } ], "roamingRenderings": [] diff --git a/assets/themes/fruit_trees/fruit_trees.json b/assets/themes/fruit_trees/fruit_trees.json index 68ce79228..3af096cf9 100644 --- a/assets/themes/fruit_trees/fruit_trees.json +++ b/assets/themes/fruit_trees/fruit_trees.json @@ -68,6 +68,19 @@ "nl": "Voeg een boomgaard toe (als punt - omtrek nog te tekenen)" } } + ], + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/buurtnatuur/forest.svg" + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } ] }, { @@ -170,6 +183,19 @@ "nl": "Voeg hier een boom toe" } } + ], + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/fruit_trees/fruit_tree.svg" + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } ] } ], diff --git a/assets/themes/grb.json b/assets/themes/grb.json index a0f32cdc6..3839c319e 100644 --- a/assets/themes/grb.json +++ b/assets/themes/grb.json @@ -197,7 +197,26 @@ "render": "#00f" }, "wayHandling": 2, - "presets": [] + "presets": [], + "mapRendering": [ + { + "label": { + "mappings": [ + { + "if": "addr:housenumber~*", + "then": "
{addr:housenumber}
" + } + ] + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point", + "centroid" + ] + } + ] } ], "hideFromOverview": true, diff --git a/assets/themes/hackerspaces/hackerspaces.json b/assets/themes/hackerspaces/hackerspaces.json index b2fc74c3a..8744e94c9 100644 --- a/assets/themes/hackerspaces/hackerspaces.json +++ b/assets/themes/hackerspaces/hackerspaces.json @@ -233,7 +233,34 @@ "leisure=hackerspace" ] } - } + }, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/hackerspaces/glider.svg", + "mappings": [ + { + "if": { + "and": [ + "hackerspace=makerspace" + ] + }, + "then": { + "en": "./assets/themes/hackerspaces/led.png", + "de": "./assets/themes/hackerspaces/led.png" + } + } + ] + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point", + "centroid" + ] + } + ] } ] } \ No newline at end of file diff --git a/assets/themes/hailhydrant/hailhydrant.json b/assets/themes/hailhydrant/hailhydrant.json index 3a94dbb1b..1481a652f 100644 --- a/assets/themes/hailhydrant/hailhydrant.json +++ b/assets/themes/hailhydrant/hailhydrant.json @@ -316,7 +316,21 @@ } } ], - "wayHandling": 2 + "wayHandling": 2, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/hailhydrant/hydrant.svg" + }, + "iconSize": { + "render": "20,20,center" + }, + "location": [ + "point", + "centroid" + ] + } + ] }, { "id": "extinguisher", @@ -433,7 +447,20 @@ } } ], - "wayHandling": 1 + "wayHandling": 1, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/hailhydrant/Twemoji12_1f9ef.svg" + }, + "iconSize": { + "render": "20,20,center" + }, + "location": [ + "point" + ] + } + ] }, { "id": "fire_stations", @@ -657,6 +684,20 @@ "fr": "Une caserne de pompiers est un lieu où les pompiers et leur équipements sont situés en dehors des missions." } } + ], + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/hailhydrant/Twemoji12_1f692.svg" + }, + "iconSize": { + "render": "35,35,center" + }, + "location": [ + "point", + "centroid" + ] + } ] }, { @@ -858,7 +899,21 @@ } } ], - "wayHandling": 2 + "wayHandling": 2, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/hailhydrant/Twemoji_1f691.svg" + }, + "iconSize": { + "render": "35,35,center" + }, + "location": [ + "point", + "centroid" + ] + } + ] } ], "defaultBackgroundId": "HDM_HOT" diff --git a/assets/themes/natuurpunt/natuurpunt.json b/assets/themes/natuurpunt/natuurpunt.json index ecbe8afe9..fc9bcd4cd 100644 --- a/assets/themes/natuurpunt/natuurpunt.json +++ b/assets/themes/natuurpunt/natuurpunt.json @@ -54,9 +54,13 @@ }, "minzoom": 13, "minzoomVisible": 0, - "icon": { - "render": "circle:#FE6F32;./assets/themes/natuurpunt/nature_reserve.svg" - } + "mapRendering": [ + { + "icon": { + "render": "circle:#FE6F32;./assets/themes/natuurpunt/nature_reserve.svg" + } + } + ] } }, { @@ -74,9 +78,13 @@ "isOsmCache": "duplicate" }, "minzoom": 1, - "icon": { - "render": "circle:#FE6F32;./assets/themes/natuurpunt/nature_reserve.svg" - }, + "mapRendering": [ + { + "icon": { + "render": "circle:#FE6F32;./assets/themes/natuurpunt/nature_reserve.svg" + } + } + ], "presets": [] } }, @@ -93,9 +101,13 @@ "isOsmCache": true }, "minzoom": 1, - "icon": { - "render": "circle:#FE6F32;./assets/themes/natuurpunt/information.svg" - } + "mapRendering": [ + { + "icon": { + "render": "circle:#FE6F32;./assets/themes/natuurpunt/information.svg" + } + } + ] } }, { @@ -112,19 +124,23 @@ "isOsmCache": true }, "minzoom": 10, - "icon": { - "render": "circle:#FE6F32;./assets/themes/natuurpunt/trail.svg", - "mappings": [ - { - "if": "wheelchair=yes", - "then": "circle:#FE6F32;./assets/themes/natuurpunt/walk_wheelchair.svg" - }, - { - "if": "pushchair=yes", - "then": "circle:#FE6F32;./assets/themes/natuurpunt/pushchair.svg" + "mapRendering": [ + { + "icon": { + "render": "circle:#FE6F32;./assets/themes/natuurpunt/trail.svg", + "mappings": [ + { + "if": "wheelchair=yes", + "then": "circle:#FE6F32;./assets/themes/natuurpunt/walk_wheelchair.svg" + }, + { + "if": "pushchair=yes", + "then": "circle:#FE6F32;./assets/themes/natuurpunt/pushchair.svg" + } + ] } - ] - } + } + ] } }, { @@ -136,19 +152,23 @@ "geoJsonZoomLevel": 12, "isOsmCache": true }, - "icon": { - "render": "circle:#FE6F32;./assets/themes/natuurpunt/toilets.svg", - "mappings": [ - { - "if": "wheelchair=yes", - "then": "circle:#FE6F32;./assets/themes/natuurpunt/wheelchair.svg" - }, - { - "if": "toilets:position=urinals", - "then": "circle:#FE6F32;./assets/themes/natuurpunt/urinal.svg" + "mapRendering": [ + { + "icon": { + "render": "circle:#FE6F32;./assets/themes/natuurpunt/toilets.svg", + "mappings": [ + { + "if": "wheelchair=yes", + "then": "circle:#FE6F32;./assets/themes/natuurpunt/wheelchair.svg" + }, + { + "if": "toilets:position=urinals", + "then": "circle:#FE6F32;./assets/themes/natuurpunt/urinal.svg" + } + ] } - ] - } + } + ] } }, { @@ -160,10 +180,14 @@ "geoJsonZoomLevel": 12, "isOsmCache": true }, - "icon": { - "render": "circle:#FE6F32;./assets/themes/natuurpunt/birdhide.svg", - "mappings": null - } + "mapRendering": [ + { + "icon": { + "render": "circle:#FE6F32;./assets/themes/natuurpunt/birdhide.svg", + "mappings": null + } + } + ] } }, { @@ -175,9 +199,13 @@ "geoJsonZoomLevel": 12, "isOsmCache": true }, - "icon": { - "render": "circle:#FE6F32;./assets/themes/natuurpunt/picnic_table.svg" - } + "mapRendering": [ + { + "icon": { + "render": "circle:#FE6F32;./assets/themes/natuurpunt/picnic_table.svg" + } + } + ] } }, { @@ -189,34 +217,42 @@ "geoJsonZoomLevel": 12, "isOsmCache": true }, - "icon": { - "render": "circle:#FE6F32;./assets/themes/natuurpunt/drips.svg" - } + "mapRendering": [ + { + "icon": { + "render": "circle:#FE6F32;./assets/themes/natuurpunt/drips.svg" + } + } + ] } }, { "builtin": "parking", "override": { "minzoom": "16", - "icon": { - "render": "circle:#FE6F32;./assets/themes/natuurpunt/parking.svg", - "mappings": [ - { - "if": "amenity=bicycle_parking", - "then": "circle:#FE6F32;./assets/themes/natuurpunt/parkingbike.svg" - } - ] - }, - "iconOverlays": [ + "mapRendering": [ { - "if": "amenity=motorcycle_parking", - "then": "circle:#335D9F;./assets/themes/natuurpunt/parkingmotor.svg", - "badge": true - }, - { - "if": "capacity:disabled=yes", - "then": "circle:#335D9F;./assets/themes/natuurpunt/parkingwheels.svg", - "badge": true + "icon": { + "render": "circle:#FE6F32;./assets/themes/natuurpunt/parking.svg", + "mappings": [ + { + "if": "amenity=bicycle_parking", + "then": "circle:#FE6F32;./assets/themes/natuurpunt/parkingbike.svg" + } + ] + }, + "iconOverlays": [ + { + "if": "amenity=motorcycle_parking", + "then": "circle:#335D9F;./assets/themes/natuurpunt/parkingmotor.svg", + "badge": true + }, + { + "if": "capacity:disabled=yes", + "then": "circle:#335D9F;./assets/themes/natuurpunt/parkingwheels.svg", + "badge": true + } + ] } ] } @@ -230,9 +266,13 @@ "geoJsonZoomLevel": 12, "isOsmCache": true }, - "icon": { - "render": "circle:#FE6F32;./assets/themes/natuurpunt/information_board.svg" - } + "mapRendering": [ + { + "icon": { + "render": "circle:#FE6F32;./assets/themes/natuurpunt/information_board.svg" + } + } + ] } }, { @@ -244,9 +284,13 @@ "geoJsonZoomLevel": 12, "isOsmCache": true }, - "icon": { - "render": "circle:#FE6F32;./assets/themes/natuurpunt/bench.svg" - } + "mapRendering": [ + { + "icon": { + "render": "circle:#FE6F32;./assets/themes/natuurpunt/bench.svg" + } + } + ] } }, { @@ -258,9 +302,13 @@ "geoJsonZoomLevel": 12, "isOsmCache": true }, - "icon": { - "render": "circle:#FE6F32;./assets/themes/natuurpunt/watermill.svg" - } + "mapRendering": [ + { + "icon": { + "render": "circle:#FE6F32;./assets/themes/natuurpunt/watermill.svg" + } + } + ] } } ], diff --git a/assets/themes/openwindpowermap/openwindpowermap.json b/assets/themes/openwindpowermap/openwindpowermap.json index a437a06bd..10a7859fb 100644 --- a/assets/themes/openwindpowermap/openwindpowermap.json +++ b/assets/themes/openwindpowermap/openwindpowermap.json @@ -227,6 +227,23 @@ } ] } + ], + "mapRendering": [ + { + "icon": "./assets/themes/openwindpowermap/wind_turbine.svg", + "label": { + "mappings": [ + { + "if": "generator:output:electricity~^[0-9]+.*[W]$", + "then": "
{generator:output:electricity}
" + } + ] + }, + "iconSize": "40, 40, bottom", + "location": [ + "point" + ] + } ] } ], diff --git a/assets/themes/postboxes/postboxes.json b/assets/themes/postboxes/postboxes.json index d90c5a1c3..6a881b29a 100644 --- a/assets/themes/postboxes/postboxes.json +++ b/assets/themes/postboxes/postboxes.json @@ -79,7 +79,21 @@ "razed:amenity=post_box" ] } - } + }, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/postboxes/postbox.svg" + }, + "iconSize": { + "render": "40,40,bottom" + }, + "location": [ + "point", + "centroid" + ] + } + ] }, { "id": "postoffices", @@ -168,6 +182,27 @@ } ] } + ], + "mapRendering": [ + { + "icon": { + "render": "square:white;./assets/themes/postboxes/post_office.svg" + }, + "iconOverlays": [ + { + "if": "opening_hours~*", + "then": "isOpen", + "badge": true + } + ], + "iconSize": { + "render": "40,40,bottom" + }, + "location": [ + "point", + "centroid" + ] + } ] } ] diff --git a/assets/themes/speelplekken/speelplekken.json b/assets/themes/speelplekken/speelplekken.json index d25d24cf9..702087d8c 100644 --- a/assets/themes/speelplekken/speelplekken.json +++ b/assets/themes/speelplekken/speelplekken.json @@ -36,7 +36,14 @@ "color": "#444444", "width": { "render": "1" - } + }, + "mapRendering": [ + { + "location": [ + "point" + ] + } + ] }, { "builtin": "play_forest", @@ -252,7 +259,15 @@ }, "width": { "render": "9" - } + }, + "mapRendering": [ + { + "icon": "./assets/themes/speelplekken/walking_route.svg", + "location": [ + "point" + ] + } + ] } ], "clustering": { diff --git a/assets/themes/uk_addresses/uk_addresses.json b/assets/themes/uk_addresses/uk_addresses.json index 4a9ab30ab..f334634b3 100644 --- a/assets/themes/uk_addresses/uk_addresses.json +++ b/assets/themes/uk_addresses/uk_addresses.json @@ -108,6 +108,29 @@ } ] } + ], + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/uk_addresses/housenumber_unknown.svg", + "mappings": [ + { + "if": "_embedding_object:id~*", + "then": "./assets/themes/uk_addresses/housenumber_unknown_small.svg" + }, + { + "if": "_imported=yes", + "then": "./assets/themes/uk_addresses/housenumber_unknown_small.svg" + } + ] + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } ] }, { @@ -254,7 +277,36 @@ "then": "#ff0" } ] - } + }, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/uk_addresses/housenumber_ok.svg", + "mappings": [ + { + "if": { + "or": [ + { + "and": [ + "addr:housenumber=", + "nohousenumber!=yes" + ] + }, + "addr:street=" + ] + }, + "then": "./assets/themes/uk_addresses/housenumber_unknown.svg" + } + ] + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } + ] }, { "id": "named_streets", @@ -272,7 +324,14 @@ }, "width": { "render": "0" - } + }, + "mapRendering": [ + { + "location": [ + "point" + ] + } + ] } ], "roamingRenderings": [] diff --git a/css/index-tailwind-output.css b/css/index-tailwind-output.css index 63c10a8d0..7ba52d19b 100644 --- a/css/index-tailwind-output.css +++ b/css/index-tailwind-output.css @@ -924,6 +924,10 @@ video { margin-right: 0.75rem; } +.mb-0 { + margin-bottom: 0px; +} + .box-border { box-sizing: border-box; } diff --git a/test/Tag.spec.ts b/test/Tag.spec.ts index 833a7afc5..790d641b5 100644 --- a/test/Tag.spec.ts +++ b/test/Tag.spec.ts @@ -166,7 +166,7 @@ export default class TagSpec extends T { } ], condition: "x=" - }, undefined, "Tests"); + }, "Tests"); equal(undefined, tr.GetRenderValue({"foo": "bar"})); equal("Has no name", tr.GetRenderValue({"noname": "yes"})?.txt); @@ -483,7 +483,7 @@ export default class TagSpec extends T { ] }; - const tagRendering = new TagRenderingConfig(config, null, "test"); + const tagRendering = new TagRenderingConfig(config, "test"); equal(true, tagRendering.IsKnown({bottle: "yes"})) equal(false, tagRendering.IsKnown({})) }], From 3fbff4126159e352d4dae0f52577180c7addacb5 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Tue, 19 Oct 2021 12:11:56 +0200 Subject: [PATCH 05/95] Filter out roads without lit tag in lit_streets --- assets/themes/street_lighting/street_lighting.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/themes/street_lighting/street_lighting.json b/assets/themes/street_lighting/street_lighting.json index 79ff16975..389336899 100644 --- a/assets/themes/street_lighting/street_lighting.json +++ b/assets/themes/street_lighting/street_lighting.json @@ -251,7 +251,8 @@ "osmTags": { "and": [ "highway!=", - "lit!=no" + "lit!=no", + "lit!=" ] } }, From c6ae4e721e543e9131d797027caabd970e670c19 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Tue, 19 Oct 2021 12:14:31 +0200 Subject: [PATCH 06/95] More consistent wording --- .../street_lighting/street_lighting.json | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/assets/themes/street_lighting/street_lighting.json b/assets/themes/street_lighting/street_lighting.json index 389336899..8e2f66c84 100644 --- a/assets/themes/street_lighting/street_lighting.json +++ b/assets/themes/street_lighting/street_lighting.json @@ -282,29 +282,29 @@ "if": "lit=yes", "then": { "en": "This street is lit", - "nl": "Deze weg is verlicht" + "nl": "Deze straat is verlicht" } }, { "if": "lit=no", "then": { - "en": "This road is not lit", - "nl": "Deze weg is niet verlicht" + "en": "This street is not lit", + "nl": "Deze straat is niet verlicht" } }, { "if": "lit=sunset-sunrise", "then": { - "en": "This road is lit at night", - "nl": "Deze weg is 's nachts verlicht" + "en": "This street is lit at night", + "nl": "Deze straat is 's nachts verlicht" }, "hideInAnswer": true }, { "if": "lit=24/7", "then": { - "en": "This road is lit 24/7", - "nl": "Deze weg is 24/7 verlicht" + "en": "This street is lit 24/7", + "nl": "Deze straat is 24/7 verlicht" } } ] @@ -347,29 +347,29 @@ "if": "lit=yes", "then": { "en": "This street is lit", - "nl": "Deze weg is verlicht" + "nl": "Deze straat is verlicht" } }, { "if": "lit=no", "then": { - "en": "This road is not lit", - "nl": "Deze weg is niet verlicht" + "en": "This street is not lit", + "nl": "Deze straat is niet verlicht" } }, { "if": "lit=sunset-sunrise", "then": { - "en": "This road is lit at night", - "nl": "Deze weg is 's nachts verlicht" + "en": "This street is lit at night", + "nl": "Deze straat is 's nachts verlicht" }, "hideInAnswer": true }, { "if": "lit=24/7", "then": { - "en": "This road is lit 24/7", - "nl": "Deze weg is 24/7 verlicht" + "en": "This street is lit 24/7", + "nl": "Deze straat is 24/7 verlicht" } } ] From 4a7cce762e805723225501ba573dd3d69ad4b71e Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 19 Oct 2021 12:58:53 +0200 Subject: [PATCH 07/95] More cleanup --- Models/ThemeConfig/Json/LayoutConfigJson.ts | 12 --- .../Json/TagRenderingConfigJson.ts | 5 -- assets/layers/bike_shop/bike_shop.json | 5 ++ assets/themes/buurtnatuur/buurtnatuur.json | 39 +++++++++ assets/themes/campersite/campersite.json | 39 ++++++++- assets/themes/climbing/climbing.json | 84 ++++++++++++++++++- assets/themes/cyclestreets/cyclestreets.json | 24 ++++++ .../themes/facadegardens/facadegardens.json | 73 +++++++++++++++- assets/themes/fruit_trees/fruit_trees.json | 26 ++++++ assets/themes/grb.json | 21 ++++- assets/themes/natuurpunt/natuurpunt.json | 2 +- assets/themes/uk_addresses/uk_addresses.json | 63 +++++++++++++- 12 files changed, 366 insertions(+), 27 deletions(-) diff --git a/Models/ThemeConfig/Json/LayoutConfigJson.ts b/Models/ThemeConfig/Json/LayoutConfigJson.ts index be7c3ccf5..cf1346b32 100644 --- a/Models/ThemeConfig/Json/LayoutConfigJson.ts +++ b/Models/ThemeConfig/Json/LayoutConfigJson.ts @@ -118,18 +118,6 @@ export interface LayoutConfigJson { * Default: overpassMaxZoom + 1 */ osmApiTileSize?: number - - /** - * A tagrendering depicts how to show some tags or how to show a question for it. - * - * These tagrenderings are applied to _all_ the loaded layers and are a way to reuse tagrenderings. - * Note that if multiple themes are loaded (e.g. via the personal theme) - * that these roamingRenderings are applied to the layers of the OTHER themes too! - * - * In order to prevent them to do too much damage, all the overpass-tags of the layers are taken and combined as OR. - * These tag renderings will only show up if the object matches this filter. - */ - roamingRenderings?: (TagRenderingConfigJson | string)[], /** * An override applied on all layers of the theme. diff --git a/Models/ThemeConfig/Json/TagRenderingConfigJson.ts b/Models/ThemeConfig/Json/TagRenderingConfigJson.ts index 532690af7..f834ec19b 100644 --- a/Models/ThemeConfig/Json/TagRenderingConfigJson.ts +++ b/Models/ThemeConfig/Json/TagRenderingConfigJson.ts @@ -170,9 +170,4 @@ export interface TagRenderingConfigJson { }[] - /** - * If set to true, this tagRendering will escape the current layer and attach itself to all the other layers too. - * However, it will _only_ be shown if it matches the overpass-tags of the layer it was originally defined in. - */ - roaming?: boolean } \ No newline at end of file diff --git a/assets/layers/bike_shop/bike_shop.json b/assets/layers/bike_shop/bike_shop.json index 1cdc7d5e6..7992c7c7f 100644 --- a/assets/layers/bike_shop/bike_shop.json +++ b/assets/layers/bike_shop/bike_shop.json @@ -744,6 +744,11 @@ ] }, "iconOverlays": [ + { + "if": "opening_hours~*", + "then": "isOpen", + "badge": true + }, { "if": "opening_hours~*", "then": "isOpen", diff --git a/assets/themes/buurtnatuur/buurtnatuur.json b/assets/themes/buurtnatuur/buurtnatuur.json index 03d04cffe..12962edee 100644 --- a/assets/themes/buurtnatuur/buurtnatuur.json +++ b/assets/themes/buurtnatuur/buurtnatuur.json @@ -124,6 +124,19 @@ "nl": "Voeg een ontbrekend, erkend natuurreservaat toe, bv. een gebied dat beheerd wordt door het ANB of natuurpunt" } } + ], + "mapRendering": [ + { + "icon": { + "render": "circle:#ffffff;./assets/themes/buurtnatuur/nature_reserve.svg" + }, + "iconSize": { + "render": "50,50,center" + }, + "location": [ + "point" + ] + } ] }, { @@ -222,6 +235,19 @@ "nl": "Voeg een ontbrekend park toe" } } + ], + "mapRendering": [ + { + "icon": { + "render": "circle:#ffffff;./assets/themes/buurtnatuur/park.svg" + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } ] }, { @@ -339,6 +365,19 @@ "nl": "Voeg een ontbrekend bos toe aan de kaart" } } + ], + "mapRendering": [ + { + "icon": { + "render": "circle:#ffffff;./assets/themes/buurtnatuur/forest.svg" + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } ] }, "viewpoint" diff --git a/assets/themes/campersite/campersite.json b/assets/themes/campersite/campersite.json index 670e45299..f7d6586c6 100644 --- a/assets/themes/campersite/campersite.json +++ b/assets/themes/campersite/campersite.json @@ -676,7 +676,31 @@ } } ], - "wayHandling": 2 + "wayHandling": 2, + "mapRendering": [ + { + "icon": { + "render": "circle:white;./assets/themes/campersite/caravan.svg", + "mappings": [ + { + "if": { + "and": [ + "fee=no" + ] + }, + "then": "circle:white;./assets/themes/campersite/caravan_green.svg" + } + ] + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point", + "centroid" + ] + } + ] }, { "id": "dumpstations", @@ -1075,6 +1099,19 @@ "de": "Fügen Sie eine neue sanitäre Entsorgungsstation hinzu. Hier können Camper Abwasser oder chemischen Toilettenabfälle entsorgen. Oft gibt es auch Trinkwasser und Strom." } } + ], + "mapRendering": [ + { + "icon": { + "render": "circle:white;./assets/themes/campersite/sanitary_dump_station.svg" + }, + "iconSize": { + "render": "32,32,center" + }, + "location": [ + "point" + ] + } ] } ], diff --git a/assets/themes/climbing/climbing.json b/assets/themes/climbing/climbing.json index bfd84bba8..5174911cc 100644 --- a/assets/themes/climbing/climbing.json +++ b/assets/themes/climbing/climbing.json @@ -215,7 +215,27 @@ } } ], - "wayHandling": 1 + "wayHandling": 1, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/climbing/club.svg" + }, + "iconOverlays": [ + { + "if": "opening_hours~*", + "then": "isOpen", + "badge": true + } + ], + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } + ] }, { "id": "climbing_gym", @@ -313,7 +333,27 @@ "iconSize": { "render": "40,40,center" }, - "wayHandling": 1 + "wayHandling": 1, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/climbing/climbing_gym.svg" + }, + "iconOverlays": [ + { + "if": "opening_hours~*", + "then": "isOpen", + "badge": true + } + ], + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } + ] }, { "id": "climbing_route", @@ -532,7 +572,21 @@ ] } ], - "wayHandling": 2 + "wayHandling": 2, + "mapRendering": [ + { + "icon": { + "render": "circle:white;./assets/themes/climbing/climbing_route.svg" + }, + "iconSize": { + "render": "28,28,center" + }, + "location": [ + "point", + "centroid" + ] + } + ] }, { "id": "climbing", @@ -801,6 +855,20 @@ "_difficulty_hist=JSON.parse(feat.properties._contained_climbing_routes_properties ?? '[]').map(p => p['climbing:grade:french'])", "_length_hist=JSON.parse(feat.properties._contained_climbing_routes_properties ?? '[]').map(p => p['climbing:length'])", "_contained_climbing_routes_count=JSON.parse(feat.properties._contained_climbing_routes_properties ?? '[]').length" + ], + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/climbing/climbing_no_rope.svg" + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point", + "centroid" + ] + } ] }, { @@ -930,7 +998,15 @@ "color": { "render": "#ddff55AA" }, - "wayHandling": 0 + "wayHandling": 0, + "mapRendering": [ + { + "icon": "./assets/themes/climbing/climbing_unknown.svg", + "location": [ + "point" + ] + } + ] } ], "overrideAll": { diff --git a/assets/themes/cyclestreets/cyclestreets.json b/assets/themes/cyclestreets/cyclestreets.json index 9f332e157..d5b76a7cb 100644 --- a/assets/themes/cyclestreets/cyclestreets.json +++ b/assets/themes/cyclestreets/cyclestreets.json @@ -84,6 +84,14 @@ "width": "10", "tagRenderings": [ "images" + ], + "mapRendering": [ + { + "icon": "./assets/themes/cyclestreets/F111.svg", + "location": [ + "point" + ] + } ] }, { @@ -136,6 +144,14 @@ "width": "5", "tagRenderings": [ "images" + ], + "mapRendering": [ + { + "icon": "./assets/themes/cyclestreets/F113.svg", + "location": [ + "point" + ] + } ] }, { @@ -201,6 +217,14 @@ }, "tagRenderings": [ "images" + ], + "mapRendering": [ + { + "icon": "./assets/svg/pencil.svg", + "location": [ + "point" + ] + } ] } ], diff --git a/assets/themes/facadegardens/facadegardens.json b/assets/themes/facadegardens/facadegardens.json index d9fc9ed62..295c3e03e 100644 --- a/assets/themes/facadegardens/facadegardens.json +++ b/assets/themes/facadegardens/facadegardens.json @@ -454,7 +454,78 @@ } } ], - "wayHandling": 1 + "wayHandling": 1, + "mapRendering": [ + { + "icon": { + "render": "circle:white;./assets/themes/facadegardens/geveltuin.svg", + "mappings": [ + { + "if": { + "and": [ + "direct_sunlight=yes" + ] + }, + "then": "circle:white;./assets/themes/facadegardens/zon.svg" + }, + { + "if": { + "and": [ + "direct_sunlight=partial" + ] + }, + "then": "circle:white;./assets/themes/facadegardens/halfzon.svg" + }, + { + "if": { + "and": [ + "direct_sunlight=no" + ] + }, + "then": "circle:white;./assets/themes/facadegardens/schaduw.svg" + } + ] + }, + "iconOverlays": [ + { + "if": "plant~.*vine.*", + "then": "circle:white;./assets/themes/facadegardens/klimplant.svg", + "badge": true + }, + { + "if": "plant~.*groundcover.*", + "then": "circle:white;./assets/themes/facadegardens/bodembedekker.svg", + "badge": true + }, + { + "if": "edible=true", + "then": "circle:white;./assets/themes/facadegardens/eetbaar.svg", + "badge": true + }, + { + "if": "rain_barel=yes", + "then": "circle:white;./assets/themes/facadegardens/gevelton.svg", + "badge": true + }, + { + "if": "plant~.*shrub.*", + "then": "circle:white;./assets/themes/facadegardens/struik.svg", + "badge": true + }, + { + "if": "plant~.*flower.*", + "then": "circle:white;./assets/themes/facadegardens/bloei.svg", + "badge": true + } + ], + "iconSize": { + "render": "50,50,center" + }, + "location": [ + "point" + ] + } + ] } ] } \ No newline at end of file diff --git a/assets/themes/fruit_trees/fruit_trees.json b/assets/themes/fruit_trees/fruit_trees.json index 9f9e84fab..88b145e6b 100644 --- a/assets/themes/fruit_trees/fruit_trees.json +++ b/assets/themes/fruit_trees/fruit_trees.json @@ -68,6 +68,19 @@ "nl": "Voeg een boomgaard toe (als punt - omtrek nog te tekenen)" } } + ], + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/buurtnatuur/forest.svg" + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } ] }, { @@ -170,6 +183,19 @@ "nl": "Voeg hier een boom toe" } } + ], + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/fruit_trees/fruit_tree.svg" + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } ] } ] diff --git a/assets/themes/grb.json b/assets/themes/grb.json index d55577e5a..d279a6409 100644 --- a/assets/themes/grb.json +++ b/assets/themes/grb.json @@ -197,7 +197,26 @@ "render": "#00f" }, "wayHandling": 2, - "presets": [] + "presets": [], + "mapRendering": [ + { + "label": { + "mappings": [ + { + "if": "addr:housenumber~*", + "then": "
{addr:housenumber}
" + } + ] + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point", + "centroid" + ] + } + ] } ], "hideFromOverview": true, diff --git a/assets/themes/natuurpunt/natuurpunt.json b/assets/themes/natuurpunt/natuurpunt.json index fc9bcd4cd..ceaa8a684 100644 --- a/assets/themes/natuurpunt/natuurpunt.json +++ b/assets/themes/natuurpunt/natuurpunt.json @@ -32,8 +32,8 @@ "enablePdfDownload": true, "enableDownload": true, "hideFromOverview": true, - "#": "Disable clustering for this theme", "clustering": { + "#": "Disable clustering for this theme", "maxZoom": 0 }, "layers": [ diff --git a/assets/themes/uk_addresses/uk_addresses.json b/assets/themes/uk_addresses/uk_addresses.json index 09e93cbe6..ee782ca10 100644 --- a/assets/themes/uk_addresses/uk_addresses.json +++ b/assets/themes/uk_addresses/uk_addresses.json @@ -108,6 +108,29 @@ } ] } + ], + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/uk_addresses/housenumber_unknown.svg", + "mappings": [ + { + "if": "_embedding_object:id~*", + "then": "./assets/themes/uk_addresses/housenumber_unknown_small.svg" + }, + { + "if": "_imported=yes", + "then": "./assets/themes/uk_addresses/housenumber_unknown_small.svg" + } + ] + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } ] }, { @@ -254,7 +277,36 @@ "then": "#ff0" } ] - } + }, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/uk_addresses/housenumber_ok.svg", + "mappings": [ + { + "if": { + "or": [ + { + "and": [ + "addr:housenumber=", + "nohousenumber!=yes" + ] + }, + "addr:street=" + ] + }, + "then": "./assets/themes/uk_addresses/housenumber_unknown.svg" + } + ] + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } + ] }, { "id": "named_streets", @@ -272,7 +324,14 @@ }, "width": { "render": "0" - } + }, + "mapRendering": [ + { + "location": [ + "point" + ] + } + ] } ] } \ No newline at end of file From 33ee2e8747440d6b63c98d156ee9a1970d0780ff Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Tue, 19 Oct 2021 13:12:20 +0200 Subject: [PATCH 08/95] Add more detailed questions to street lighting theme --- .../street_lighting/street_lighting.json | 126 +++++++++++++++++- langs/themes/en.json | 56 +++++++- langs/themes/nl.json | 64 +++++++-- 3 files changed, 232 insertions(+), 14 deletions(-) diff --git a/assets/themes/street_lighting/street_lighting.json b/assets/themes/street_lighting/street_lighting.json index 8e2f66c84..0b0ca2389 100644 --- a/assets/themes/street_lighting/street_lighting.json +++ b/assets/themes/street_lighting/street_lighting.json @@ -238,6 +238,128 @@ } } ] + }, + { + "id": "colour", + "question": { + "en": "What colour light does this lamp emit?", + "nl": "Wat voor kleur licht geeft deze lantaarn?" + }, + "render": { + "en": "This lamp emits {light:colour} light", + "nl": "Deze lantaarn geeft {light:colour} licht" + }, + "freeform": { + "key": "light:colour" + }, + "mappings": [ + { + "if": "light:colour=white", + "then": { + "en": "This lamp emits white light", + "nl": "Deze lantaarn geeft wit licht" + } + }, + { + "if": "light:colour=green", + "then": { + "en": "This lamp emits green light", + "nl": "Deze lantaarn geeft groen licht" + } + }, + { + "if": "light:colour=orange", + "then": { + "en": "This lamp emits orange light", + "nl": "Deze lantaarn geeft oranje licht" + } + } + ] + }, + { + "id": "count", + "render": { + "en": "This lamp has {light:count} fixtures", + "nl": "Deze lantaarn heeft {light:count} lampen" + }, + "question": { + "en": "How many fixtures does this light have?", + "nl": "Hoeveel lampen heeft deze lantaarn?" + }, + "condition": "support=pole", + "freeform": { + "key": "light:count", + "type": "pnat" + }, + "mappings": [ + { + "if": "light:count=1", + "then": { + "en": "This lamp has 1 fixture", + "nl": "Deze lantaarn heeft 1 lamp" + } + }, + { + "if": "light:count=2", + "then": { + "en": "This lamp has 2 fixtures", + "nl": "Deze lantaarn heeft 2 lampen" + } + } + ] + }, + { + "id": "lit", + "question": { + "en": "When is this lamp lit?", + "nl": "Wanneer is deze lantaarn verlicht?" + }, + "mappings": [ + { + "if": "light:lit=dusk-dawn", + "then": { + "en": "This lamp is lit at night", + "nl": "Deze lantaarn is 's nachts verlicht" + } + }, + { + "if": "light:lit=24/7", + "then": { + "en": "This lamp is lit 24/7", + "nl": "Deze lantaarn is 24/7 verlicht" + } + }, + { + "if": "light:lit=motion", + "then": { + "en": "This lamp is lit based on motion", + "nl": "Deze lantaarn is verlicht op basis van beweging" + } + }, + { + "if": "light:lit=demand", + "then": { + "en": "This lamp is lit based on demand (e.g. with a pushbutton)", + "nl": "Deze lantaarn is verlicht op verzoek (bijv. met een drukknop)" + } + } + ] + }, + { + "id": "direction", + "render": { + "en": "This lamp points towards {light:direction}", + "nl": "Deze lantaarn is gericht naar {light:direction}" + }, + "question": { + "en": "Where does this lamp point to?", + "nl": "Waar is deze lamp heengericht?" + }, + "condition": "light:count=1", + "freeform": { + "key": "light:direction", + "type": "direction" + } } ] }, @@ -288,7 +410,7 @@ { "if": "lit=no", "then": { - "en": "This street is not lit", + "en": "This road is not lit", "nl": "Deze straat is niet verlicht" } }, @@ -353,7 +475,7 @@ { "if": "lit=no", "then": { - "en": "This street is not lit", + "en": "This road is not lit", "nl": "Deze straat is niet verlicht" } }, diff --git a/langs/themes/en.json b/langs/themes/en.json index 9874ca47a..2bb027ecb 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -1238,6 +1238,37 @@ } }, "tagRenderings": { + "colour": { + "mappings": { + "0": { + "then": "This lamp emits white light" + }, + "1": { + "then": "This lamp emits green light" + }, + "2": { + "then": "This lamp emits orange light" + } + }, + "question": "What colour light does this lamp emit?", + "render": "This lamp emits {light:colour} light" + }, + "count": { + "mappings": { + "0": { + "then": "This lamp has 1 fixture" + }, + "1": { + "then": "This lamp has 2 fixtures" + } + }, + "question": "How many fixtures does this light have?", + "render": "This lamp has {light:count} fixtures" + }, + "direction": { + "question": "Where does this lamp point to?", + "render": "This lamp points towards {light:direction}" + }, "lamp_mount": { "mappings": { "0": { @@ -1249,6 +1280,23 @@ }, "question": "How is this lamp mounted to the pole?" }, + "lit": { + "mappings": { + "0": { + "then": "This lamp is lit at night" + }, + "1": { + "then": "This lamp is lit 24/7" + }, + "2": { + "then": "This lamp is lit based on motion" + }, + "3": { + "then": "This lamp is lit based on demand (e.g. with a pushbutton)" + } + }, + "question": "When is this lamp lit?" + }, "method": { "mappings": { "0": { @@ -1337,10 +1385,10 @@ "then": "This road is not lit" }, "2": { - "then": "This road is lit at night" + "then": "This street is lit at night" }, "3": { - "then": "This road is lit 24/7" + "then": "This street is lit 24/7" } }, "question": "Is this street lit?" @@ -1362,10 +1410,10 @@ "then": "This road is not lit" }, "2": { - "then": "This road is lit at night" + "then": "This street is lit at night" }, "3": { - "then": "This road is lit 24/7" + "then": "This street is lit 24/7" } }, "question": "Is this street lit?" diff --git a/langs/themes/nl.json b/langs/themes/nl.json index 7045a2658..edd490c72 100644 --- a/langs/themes/nl.json +++ b/langs/themes/nl.json @@ -908,6 +908,37 @@ } }, "tagRenderings": { + "colour": { + "mappings": { + "0": { + "then": "Deze lantaarn geeft wit licht" + }, + "1": { + "then": "Deze lantaarn geeft groen licht" + }, + "2": { + "then": "Deze lantaarn geeft oranje licht" + } + }, + "question": "Wat voor kleur licht geeft deze lantaarn?", + "render": "Deze lantaarn geeft {light:colour} licht" + }, + "count": { + "mappings": { + "0": { + "then": "Deze lantaarn heeft 1 lamp" + }, + "1": { + "then": "Deze lantaarn heeft 2 lampen" + } + }, + "question": "Hoeveel lampen heeft deze lantaarn?", + "render": "Deze lantaarn heeft {light:count} lampen" + }, + "direction": { + "question": "Waar is deze lamp heengericht?", + "render": "Deze lantaarn is gericht naar {light:direction}" + }, "lamp_mount": { "mappings": { "0": { @@ -919,6 +950,23 @@ }, "question": "Hoe zit deze lantaarn aan de paal?" }, + "lit": { + "mappings": { + "0": { + "then": "Deze lantaarn is 's nachts verlicht" + }, + "1": { + "then": "Deze lantaarn is 24/7 verlicht" + }, + "2": { + "then": "Deze lantaarn is verlicht op basis van beweging" + }, + "3": { + "then": "Deze lantaarn is verlicht op verzoek (bijv. met een drukknop)" + } + }, + "question": "Wanneer is deze lantaarn verlicht?" + }, "method": { "mappings": { "0": { @@ -1001,16 +1049,16 @@ "lit": { "mappings": { "0": { - "then": "Deze weg is verlicht" + "then": "Deze straat is verlicht" }, "1": { - "then": "Deze weg is niet verlicht" + "then": "Deze straat is niet verlicht" }, "2": { - "then": "Deze weg is 's nachts verlicht" + "then": "Deze straat is 's nachts verlicht" }, "3": { - "then": "Deze weg is 24/7 verlicht" + "then": "Deze straat is 24/7 verlicht" } }, "question": "Is deze straat verlicht?" @@ -1026,16 +1074,16 @@ "lit": { "mappings": { "0": { - "then": "Deze weg is verlicht" + "then": "Deze straat is verlicht" }, "1": { - "then": "Deze weg is niet verlicht" + "then": "Deze straat is niet verlicht" }, "2": { - "then": "Deze weg is 's nachts verlicht" + "then": "Deze straat is 's nachts verlicht" }, "3": { - "then": "Deze weg is 24/7 verlicht" + "then": "Deze straat is 24/7 verlicht" } }, "question": "Is deze straat verlicht?" From b9f32749b0ca93045b5f19a58299e5a5a0d15e05 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Tue, 19 Oct 2021 14:49:44 +0200 Subject: [PATCH 09/95] Added color icon --- .../street_lighting/street_lighting.json | 30 +++++++++++++++++-- langs/themes/en.json | 5 ++++ langs/themes/nl.json | 5 ++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/assets/themes/street_lighting/street_lighting.json b/assets/themes/street_lighting/street_lighting.json index 0b0ca2389..318c7c781 100644 --- a/assets/themes/street_lighting/street_lighting.json +++ b/assets/themes/street_lighting/street_lighting.json @@ -1,7 +1,7 @@ { "id": "street_lighting", "maintainer": "Robin van der Linde", - "version": "20211018", + "version": "20211019", "language": [ "en", "nl" @@ -33,9 +33,35 @@ "render": { "en": "Street Lamp", "nl": "Straatlantaarn" - } + }, + "mappings": [ + { + "if": "ref~*", + "then": { + "en": "Street Lamp {ref}", + "nl": "Straatlantaarn {ref}" + } + } + ] }, "icon": "./assets/themes/street_lighting/street_lamp.svg", + "iconOverlays": [ + { + "if": "light:colour=white", + "then": "circle:white", + "badge": true + }, + { + "if": "light:colour=orange", + "then": "circle:#FFB000", + "badge": true + }, + { + "if": "light:colour=green", + "then": "circle:#55FF55", + "badge": true + } + ], "presets": [ { "title": { diff --git a/langs/themes/en.json b/langs/themes/en.json index 2bb027ecb..c48072aa6 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -1370,6 +1370,11 @@ } }, "title": { + "mappings": { + "0": { + "then": "Street Lamp {ref}" + } + }, "render": "Street Lamp" } }, diff --git a/langs/themes/nl.json b/langs/themes/nl.json index edd490c72..e4f9fae6f 100644 --- a/langs/themes/nl.json +++ b/langs/themes/nl.json @@ -1040,6 +1040,11 @@ } }, "title": { + "mappings": { + "0": { + "then": "Straatlantaarn {ref}" + } + }, "render": "Straatlantaarn" } }, From 445734470707c188b3b4a128efc8f7c5e043641d Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 20 Oct 2021 01:44:20 +0200 Subject: [PATCH 10/95] Add colour pick to light:colour question --- assets/themes/street_lighting/street_lighting.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/themes/street_lighting/street_lighting.json b/assets/themes/street_lighting/street_lighting.json index 318c7c781..aafa12838 100644 --- a/assets/themes/street_lighting/street_lighting.json +++ b/assets/themes/street_lighting/street_lighting.json @@ -276,7 +276,8 @@ "nl": "Deze lantaarn geeft {light:colour} licht" }, "freeform": { - "key": "light:colour" + "key": "light:colour", + "type": "color" }, "mappings": [ { From a041fbf050b71179c18aaa49fd7caed485412db1 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 20 Oct 2021 02:01:27 +0200 Subject: [PATCH 11/95] Theme format refactoring: move line properties into a seperate lineRendering configuration --- Models/ThemeConfig/Json/LayerConfigJson.ts | 19 +-- .../Json/LineRenderingConfigJson.ts | 30 +++++ Models/ThemeConfig/LayerConfig.ts | 56 +++----- Models/ThemeConfig/LineRenderingConfig.ts | 66 +++++++++ assets/layers/artwork/artwork.json | 8 ++ assets/layers/barrier/barrier.json | 3 + assets/layers/bench_at_pt/bench_at_pt.json | 8 ++ .../bicycle_library/bicycle_library.json | 8 ++ .../bicycle_tube_vending_machine.json | 3 + assets/layers/bike_cafe/bike_cafe.json | 8 ++ assets/layers/bike_parking/bike_parking.json | 4 + .../bike_repair_station.json | 8 ++ assets/layers/bike_shop/bike_shop.json | 22 ++- .../bike_themed_object.json | 8 ++ assets/layers/binocular/binocular.json | 8 ++ .../layers/cluster_style/cluster_style.json | 22 +++ assets/layers/crossings/crossings.json | 3 + .../cycleways_and_roads.json | 69 ++++++++++ .../layers/defibrillator/defibrillator.json | 3 + assets/layers/direction/direction.json | 3 + assets/layers/etymology/etymology.json | 19 +++ .../layers/grass_in_parks/grass_in_parks.json | 4 + .../layers/home_location/home_location.json | 5 + .../information_board/information_board.json | 5 + assets/layers/map/map.json | 8 ++ .../layers/nature_reserve/nature_reserve.json | 8 ++ assets/layers/play_forest/play_forest.json | 8 ++ assets/layers/playground/playground.json | 8 ++ .../public_bookcase/public_bookcase.json | 8 ++ assets/layers/shops/shops.json | 8 ++ assets/layers/slow_roads/slow_roads.json | 34 +++++ .../surveillance_camera.json | 8 ++ assets/layers/trail/trail.json | 17 +++ assets/layers/viewpoint/viewpoint.json | 4 + .../layers/village_green/village_green.json | 4 + assets/layers/waste_basket/waste_basket.json | 8 ++ assets/themes/aed/aed_brugge.json | 18 +++ assets/themes/buurtnatuur/buurtnatuur.json | 127 ++++++++++++++++++ assets/themes/campersite/campersite.json | 16 +++ assets/themes/climbing/climbing.json | 24 ++++ .../themes/cycle_highways/cycle_highways.json | 35 +++++ assets/themes/cyclestreets/cyclestreets.json | 48 +++++++ assets/themes/fruit_trees/fruit_trees.json | 16 +++ assets/themes/grb.json | 8 ++ assets/themes/hackerspaces/hackerspaces.json | 8 ++ assets/themes/hailhydrant/hailhydrant.json | 24 ++++ assets/themes/postboxes/postboxes.json | 16 +++ assets/themes/speelplekken/speelplekken.json | 24 ++++ assets/themes/uk_addresses/uk_addresses.json | 32 +++++ scripts/lint.ts | 28 +++- 50 files changed, 876 insertions(+), 63 deletions(-) create mode 100644 Models/ThemeConfig/Json/LineRenderingConfigJson.ts create mode 100644 Models/ThemeConfig/LineRenderingConfig.ts diff --git a/Models/ThemeConfig/Json/LayerConfigJson.ts b/Models/ThemeConfig/Json/LayerConfigJson.ts index eefd18085..6c6bc107a 100644 --- a/Models/ThemeConfig/Json/LayerConfigJson.ts +++ b/Models/ThemeConfig/Json/LayerConfigJson.ts @@ -5,6 +5,7 @@ import {DeleteConfigJson} from "./DeleteConfigJson"; import UnitConfigJson from "./UnitConfigJson"; import MoveConfigJson from "./MoveConfigJson"; import PointRenderingConfigJson from "./PointRenderingConfigJson"; +import LineRenderingConfigJson from "./LineRenderingConfigJson"; /** * Configuration for a single layer @@ -121,24 +122,8 @@ export interface LayerConfigJson { titleIcons?: (string | TagRenderingConfigJson)[]; - mapRendering: PointRenderingConfigJson[] + mapRendering: (PointRenderingConfigJson | LineRenderingConfigJson)[] - /** - * The color for way-elements and SVG-elements. - * If the value starts with "--", the style of the body element will be queried for the corresponding variable instead - */ - color?: string | TagRenderingConfigJson; - /** - * The stroke-width for way-elements - */ - width?: string | TagRenderingConfigJson; - - /** - * A dasharray, e.g. "5 6" - * The dasharray defines 'pixels of line, pixels of gap, pixels of line, pixels of gap', - * Default value: "" (empty string == full line) - */ - dashArray?: string | TagRenderingConfigJson /** * Wayhandling: should a way/area be displayed as: diff --git a/Models/ThemeConfig/Json/LineRenderingConfigJson.ts b/Models/ThemeConfig/Json/LineRenderingConfigJson.ts new file mode 100644 index 000000000..5330373c9 --- /dev/null +++ b/Models/ThemeConfig/Json/LineRenderingConfigJson.ts @@ -0,0 +1,30 @@ +import {TagRenderingConfigJson} from "./TagRenderingConfigJson"; +import {AndOrTagConfigJson} from "./TagConfigJson"; + +/** + * The LineRenderingConfig gives all details onto how to render a single line of a feature. + * + * This can be used if: + * + * - The feature is a line + * - The feature is an area + */ +export default interface LineRenderingConfigJson { + + /** + * The color for way-elements and SVG-elements. + * If the value starts with "--", the style of the body element will be queried for the corresponding variable instead + */ + color?: string | TagRenderingConfigJson; + /** + * The stroke-width for way-elements + */ + width?: string | TagRenderingConfigJson; + + /** + * A dasharray, e.g. "5 6" + * The dasharray defines 'pixels of line, pixels of gap, pixels of line, pixels of gap', + * Default value: "" (empty string == full line) + */ + dashArray?: string | TagRenderingConfigJson +} \ No newline at end of file diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts index 6241a37ff..ff3e9baca 100644 --- a/Models/ThemeConfig/LayerConfig.ts +++ b/Models/ThemeConfig/LayerConfig.ts @@ -15,6 +15,9 @@ import DeleteConfig from "./DeleteConfig"; import MoveConfig from "./MoveConfig"; import PointRenderingConfig from "./PointRenderingConfig"; import WithContextLoader from "./WithContextLoader"; +import LineRenderingConfig from "./LineRenderingConfig"; +import PointRenderingConfigJson from "./Json/PointRenderingConfigJson"; +import LineRenderingConfigJson from "./Json/LineRenderingConfigJson"; export default class LayerConfig extends WithContextLoader{ static WAYHANDLING_DEFAULT = 0; @@ -36,10 +39,8 @@ export default class LayerConfig extends WithContextLoader{ titleIcons: TagRenderingConfig[]; public readonly mapRendering: PointRenderingConfig[] + public readonly lineRendering: LineRenderingConfig[] - color: TagRenderingConfig; - width: TagRenderingConfig; - dashArray: TagRenderingConfig; wayHandling: number; public readonly units: Unit[]; public readonly deletion: DeleteConfig | null; @@ -200,8 +201,15 @@ export default class LayerConfig extends WithContextLoader{ - this.mapRendering = json.mapRendering.map((r, i) => new PointRenderingConfig(r, context+".mapRendering["+i+"]")) - + this.mapRendering = json.mapRendering + .filter(r => r["icon"] !== undefined || r["label"] !== undefined) + .map((r, i) => new PointRenderingConfig(r, context+".mapRendering["+i+"]")) + + this.lineRendering = json.mapRendering + .filter(r => r["icon"] === undefined && r["label"] === undefined) + .map((r, i) => new LineRenderingConfig(r, context+".mapRendering["+i+"]")) + + if(this.mapRendering.length > 1){ throw "Invalid maprendering for "+this.id+", currently only one mapRendering is supported!" } @@ -243,10 +251,7 @@ export default class LayerConfig extends WithContextLoader{ this.title = this.tr("title", undefined); this.isShown = this.tr("isShown", "yes"); - this.color = this.tr("color", "#0000ff"); - this.width = this.tr("width", "7"); - this.dashArray = this.tr("dashArray", ""); - + this.deletion = null; if (json.deletion === true) { json.deletion = {}; @@ -299,41 +304,12 @@ export default class LayerConfig extends WithContextLoader{ dashArray: number[]; } { - function rendernum(tr: TagRenderingConfig, deflt: number) { - const str = Number(render(tr, "" + deflt)); - const n = Number(str); - if (isNaN(n)) { - return deflt; - } - return n; - } - - function render(tr: TagRenderingConfig, deflt?: string) { - if (tags === undefined) { - return deflt - } - const str = tr?.GetRenderValue(tags.data)?.txt ?? deflt; - return Utils.SubstituteKeys(str, tags.data).replace(/{.*}/g, ""); - } - - const dashArray = render(this.dashArray)?.split(" ")?.map(Number); - let color = render(this.color, "#00f"); - - if (color.startsWith("--")) { - color = getComputedStyle(document.body).getPropertyValue( - "--catch-detail-color" - ); - } - - const weight = rendernum(this.width, 5); const icon = this.mapRendering[0].GenerateLeafletStyle(tags, clickable) - + const lineStyle = this.lineRendering[0].GenerateLeafletStyle(tags) return { icon, - color: color, - weight: weight, - dashArray: dashArray, + ...lineStyle }; } diff --git a/Models/ThemeConfig/LineRenderingConfig.ts b/Models/ThemeConfig/LineRenderingConfig.ts new file mode 100644 index 000000000..f1656c725 --- /dev/null +++ b/Models/ThemeConfig/LineRenderingConfig.ts @@ -0,0 +1,66 @@ +import PointRenderingConfigJson from "./Json/PointRenderingConfigJson"; +import WithContextLoader from "./WithContextLoader"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import TagRenderingConfig from "./TagRenderingConfig"; +import {Utils} from "../../Utils"; +import LineRenderingConfigJson from "./Json/LineRenderingConfigJson"; + +export default class LineRenderingConfig extends WithContextLoader { + + + public readonly color: TagRenderingConfig; + public readonly width: TagRenderingConfig; + public readonly dashArray: TagRenderingConfig; + + constructor(json: LineRenderingConfigJson, context: string) { + super(json, context) + this.color = this.tr("color", "#0000ff"); + this.width = this.tr("width", "7"); + this.dashArray = this.tr("dashArray", ""); + + } + + + public GenerateLeafletStyle( + tags: UIEventSource + ): + { + color: string, + weight: number, + dashArray: number[] + } + { + function rendernum(tr: TagRenderingConfig, deflt: number) { + const str = Number(render(tr, "" + deflt)); + const n = Number(str); + if (isNaN(n)) { + return deflt; + } + return n; + } + function render(tr: TagRenderingConfig, deflt?: string) { + if (tags === undefined) { + return deflt + } + const str = tr?.GetRenderValue(tags.data)?.txt ?? deflt; + return Utils.SubstituteKeys(str, tags.data).replace(/{.*}/g, ""); + } + + const dashArray = render(this.dashArray)?.split(" ")?.map(Number); + let color = render(this.color, "#00f"); + + if (color.startsWith("--")) { + color = getComputedStyle(document.body).getPropertyValue( + "--catch-detail-color" + ); + } + + const weight = rendernum(this.width, 5); + return { + color, + weight, + dashArray + } + } + +} \ No newline at end of file diff --git a/assets/layers/artwork/artwork.json b/assets/layers/artwork/artwork.json index 82e27e9c7..e535bab72 100644 --- a/assets/layers/artwork/artwork.json +++ b/assets/layers/artwork/artwork.json @@ -404,6 +404,14 @@ "point", "centroid" ] + }, + { + "color": { + "render": "#0000ff" + }, + "width": { + "render": "10" + } } ] } \ No newline at end of file diff --git a/assets/layers/barrier/barrier.json b/assets/layers/barrier/barrier.json index 8461abce3..7df7101d4 100644 --- a/assets/layers/barrier/barrier.json +++ b/assets/layers/barrier/barrier.json @@ -330,6 +330,9 @@ "location": [ "point" ] + }, + { + "width": "5" } ] } \ No newline at end of file diff --git a/assets/layers/bench_at_pt/bench_at_pt.json b/assets/layers/bench_at_pt/bench_at_pt.json index c43ec58e6..bc15ac3ba 100644 --- a/assets/layers/bench_at_pt/bench_at_pt.json +++ b/assets/layers/bench_at_pt/bench_at_pt.json @@ -160,6 +160,14 @@ "location": [ "point" ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "8" + } } ] } \ No newline at end of file diff --git a/assets/layers/bicycle_library/bicycle_library.json b/assets/layers/bicycle_library/bicycle_library.json index 3d5dcc455..327f22c57 100644 --- a/assets/layers/bicycle_library/bicycle_library.json +++ b/assets/layers/bicycle_library/bicycle_library.json @@ -312,6 +312,14 @@ "point", "centroid" ] + }, + { + "color": { + "render": "#c00" + }, + "width": { + "render": "1" + } } ] } \ No newline at end of file diff --git a/assets/layers/bicycle_tube_vending_machine/bicycle_tube_vending_machine.json b/assets/layers/bicycle_tube_vending_machine/bicycle_tube_vending_machine.json index 5b44f6882..55918d203 100644 --- a/assets/layers/bicycle_tube_vending_machine/bicycle_tube_vending_machine.json +++ b/assets/layers/bicycle_tube_vending_machine/bicycle_tube_vending_machine.json @@ -299,6 +299,9 @@ "point", "centroid" ] + }, + { + "color": "#6bc4f7" } ] } \ No newline at end of file diff --git a/assets/layers/bike_cafe/bike_cafe.json b/assets/layers/bike_cafe/bike_cafe.json index 53caf147b..8c453ebad 100644 --- a/assets/layers/bike_cafe/bike_cafe.json +++ b/assets/layers/bike_cafe/bike_cafe.json @@ -378,6 +378,14 @@ "point", "centroid" ] + }, + { + "color": { + "render": "#694E2D" + }, + "width": { + "render": "2" + } } ] } \ No newline at end of file diff --git a/assets/layers/bike_parking/bike_parking.json b/assets/layers/bike_parking/bike_parking.json index 94880bfe7..574a06910 100644 --- a/assets/layers/bike_parking/bike_parking.json +++ b/assets/layers/bike_parking/bike_parking.json @@ -562,6 +562,10 @@ "point", "centroid" ] + }, + { + "color": "#00f", + "width": "1" } ] } \ No newline at end of file diff --git a/assets/layers/bike_repair_station/bike_repair_station.json b/assets/layers/bike_repair_station/bike_repair_station.json index 0a44bf6ce..abf579d52 100644 --- a/assets/layers/bike_repair_station/bike_repair_station.json +++ b/assets/layers/bike_repair_station/bike_repair_station.json @@ -840,6 +840,14 @@ "point", "centroid" ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "1" + } } ] } \ No newline at end of file diff --git a/assets/layers/bike_shop/bike_shop.json b/assets/layers/bike_shop/bike_shop.json index ef57895f1..9f52b53ef 100644 --- a/assets/layers/bike_shop/bike_shop.json +++ b/assets/layers/bike_shop/bike_shop.json @@ -775,13 +775,19 @@ "badge": true }, { - "if": "opening_hours~*", - "then": "isOpen", + "if": "service:bicycle:pump=yes", + "then": "circle:#e2783d;./assets/layers/bike_repair_station/pump.svg", "badge": true }, { - "if": "service:bicycle:pump=yes", - "then": "circle:#e2783d;./assets/layers/bike_repair_station/pump.svg", + "if": { + "and": [ + "service:bicycle:cleaning~*" + ] + }, + "then": { + "render": "./assets/layers/bike_cleaning/bike_cleaning_icon.svg" + }, "badge": true } ], @@ -792,6 +798,14 @@ "point", "centroid" ] + }, + { + "color": { + "render": "#c00" + }, + "width": { + "render": "1" + } } ] } \ No newline at end of file diff --git a/assets/layers/bike_themed_object/bike_themed_object.json b/assets/layers/bike_themed_object/bike_themed_object.json index a6653c8b7..0a7e4322b 100644 --- a/assets/layers/bike_themed_object/bike_themed_object.json +++ b/assets/layers/bike_themed_object/bike_themed_object.json @@ -82,6 +82,14 @@ "point", "centroid" ] + }, + { + "color": { + "render": "#AB76D5" + }, + "width": { + "render": "2" + } } ] } \ No newline at end of file diff --git a/assets/layers/binocular/binocular.json b/assets/layers/binocular/binocular.json index 4e44ad7d9..6bbb7ca6a 100644 --- a/assets/layers/binocular/binocular.json +++ b/assets/layers/binocular/binocular.json @@ -139,6 +139,14 @@ "location": [ "point" ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "8" + } } ] } \ No newline at end of file diff --git a/assets/layers/cluster_style/cluster_style.json b/assets/layers/cluster_style/cluster_style.json index 16bd123c7..caaa33559 100644 --- a/assets/layers/cluster_style/cluster_style.json +++ b/assets/layers/cluster_style/cluster_style.json @@ -51,6 +51,28 @@ "location": [ "point" ] + }, + { + "color": { + "render": "#3c3", + "mappings": [ + { + "if": "showCount>200", + "then": "#f33" + }, + { + "if": "showCount>100", + "then": "#c93" + }, + { + "if": "showCount>50", + "then": "#cc3" + } + ] + }, + "width": { + "render": "1" + } } ] } \ No newline at end of file diff --git a/assets/layers/crossings/crossings.json b/assets/layers/crossings/crossings.json index a4e005b6e..0f7f00600 100644 --- a/assets/layers/crossings/crossings.json +++ b/assets/layers/crossings/crossings.json @@ -351,6 +351,9 @@ "location": [ "point" ] + }, + { + "width": "5" } ] } \ No newline at end of file diff --git a/assets/layers/cycleways_and_roads/cycleways_and_roads.json b/assets/layers/cycleways_and_roads/cycleways_and_roads.json index c6eb50400..f8a58aa6a 100644 --- a/assets/layers/cycleways_and_roads/cycleways_and_roads.json +++ b/assets/layers/cycleways_and_roads/cycleways_and_roads.json @@ -1188,6 +1188,75 @@ "location": [ "point" ] + }, + { + "color": { + "render": "rgba(170, 170, 170, 0.7)", + "mappings": [ + { + "if": "highway=cycleway", + "then": "rgba(0, 189, 141, 0.7)" + }, + { + "if": "highway=path", + "then": "rgba(204, 74, 207, 0.7)" + }, + { + "if": "cycleway=track", + "then": "rgba(113, 3, 200, 0.7)" + }, + { + "if": "cycleway=shared_lane", + "then": "rgba(74, 59, 247, 0.7)" + }, + { + "if": "cycleway=lane", + "then": "rgba(254, 155, 6, 0.9)" + }, + { + "if": "cyclestreet=yes", + "then": "rgba(57, 159, 191, 0.7)" + } + ] + }, + "width": { + "render": "8" + }, + "dashArray": { + "render": "", + "mappings": [ + { + "if": { + "or": [ + "oneway=yes", + { + "or": [ + "highway=cycleway", + "highway=path" + ] + } + ] + }, + "then": "" + }, + { + "if": "cycleway=track", + "then": "" + }, + { + "if": "cycleway=shared_lane", + "then": "15 30" + }, + { + "if": "cycleway=lane", + "then": "25 15 15 15 25" + }, + { + "if": "cyclestreet=yes", + "then": "" + } + ] + } } ] } \ No newline at end of file diff --git a/assets/layers/defibrillator/defibrillator.json b/assets/layers/defibrillator/defibrillator.json index a994361e4..9b1b02f8f 100644 --- a/assets/layers/defibrillator/defibrillator.json +++ b/assets/layers/defibrillator/defibrillator.json @@ -573,6 +573,9 @@ "location": [ "point" ] + }, + { + "color": "#0000ff" } ] } \ No newline at end of file diff --git a/assets/layers/direction/direction.json b/assets/layers/direction/direction.json index cb5b8b7da..ca7b58b4c 100644 --- a/assets/layers/direction/direction.json +++ b/assets/layers/direction/direction.json @@ -64,6 +64,9 @@ "rotation": { "render": "{_direction:numerical}deg" } + }, + { + "color": "--catch-detail-color" } ] } \ No newline at end of file diff --git a/assets/layers/etymology/etymology.json b/assets/layers/etymology/etymology.json index 48cd87fe4..f914b9432 100644 --- a/assets/layers/etymology/etymology.json +++ b/assets/layers/etymology/etymology.json @@ -190,6 +190,25 @@ "location": [ "point" ] + }, + { + "color": { + "render": "#05d7fcaa", + "mappings": [ + { + "if": { + "and": [ + "name:etymology=", + "name:etymology:wikidata=" + ] + }, + "then": "#fcca05aa" + } + ] + }, + "width": { + "render": "8" + } } ] } \ No newline at end of file diff --git a/assets/layers/grass_in_parks/grass_in_parks.json b/assets/layers/grass_in_parks/grass_in_parks.json index 321cebcb3..e95a104b1 100644 --- a/assets/layers/grass_in_parks/grass_in_parks.json +++ b/assets/layers/grass_in_parks/grass_in_parks.json @@ -60,6 +60,10 @@ "point", "centroid" ] + }, + { + "color": "#0f0", + "width": "1" } ] } \ No newline at end of file diff --git a/assets/layers/home_location/home_location.json b/assets/layers/home_location/home_location.json index 1cb233440..0fb1af576 100644 --- a/assets/layers/home_location/home_location.json +++ b/assets/layers/home_location/home_location.json @@ -25,6 +25,11 @@ "location": [ "point" ] + }, + { + "color": { + "render": "#00f" + } } ] } \ No newline at end of file diff --git a/assets/layers/information_board/information_board.json b/assets/layers/information_board/information_board.json index 2f93526a8..630461e50 100644 --- a/assets/layers/information_board/information_board.json +++ b/assets/layers/information_board/information_board.json @@ -80,6 +80,11 @@ "location": [ "point" ] + }, + { + "color": { + "render": "#00f" + } } ] } \ No newline at end of file diff --git a/assets/layers/map/map.json b/assets/layers/map/map.json index ca7a5c389..79e02791b 100644 --- a/assets/layers/map/map.json +++ b/assets/layers/map/map.json @@ -281,6 +281,14 @@ "point", "centroid" ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "8" + } } ] } \ No newline at end of file diff --git a/assets/layers/nature_reserve/nature_reserve.json b/assets/layers/nature_reserve/nature_reserve.json index 28b27071c..051cf6c4b 100644 --- a/assets/layers/nature_reserve/nature_reserve.json +++ b/assets/layers/nature_reserve/nature_reserve.json @@ -474,6 +474,14 @@ "point", "centroid" ] + }, + { + "color": { + "render": "#3c3" + }, + "width": { + "render": "1" + } } ] } \ No newline at end of file diff --git a/assets/layers/play_forest/play_forest.json b/assets/layers/play_forest/play_forest.json index 257f9249c..4244e81f5 100644 --- a/assets/layers/play_forest/play_forest.json +++ b/assets/layers/play_forest/play_forest.json @@ -129,6 +129,14 @@ "point", "centroid" ] + }, + { + "color": { + "render": "#007055" + }, + "width": { + "render": "2" + } } ] } \ No newline at end of file diff --git a/assets/layers/playground/playground.json b/assets/layers/playground/playground.json index eb489ed71..87dc9b3ca 100644 --- a/assets/layers/playground/playground.json +++ b/assets/layers/playground/playground.json @@ -578,6 +578,14 @@ "point", "centroid" ] + }, + { + "color": { + "render": "#5dbaa9" + }, + "width": { + "render": "1" + } } ] } \ No newline at end of file diff --git a/assets/layers/public_bookcase/public_bookcase.json b/assets/layers/public_bookcase/public_bookcase.json index 3f9abe815..7011728b0 100644 --- a/assets/layers/public_bookcase/public_bookcase.json +++ b/assets/layers/public_bookcase/public_bookcase.json @@ -503,6 +503,14 @@ "point", "centroid" ] + }, + { + "color": { + "render": "#0000ff" + }, + "width": { + "render": "8" + } } ] } \ No newline at end of file diff --git a/assets/layers/shops/shops.json b/assets/layers/shops/shops.json index 15ced87ad..c0761f84b 100644 --- a/assets/layers/shops/shops.json +++ b/assets/layers/shops/shops.json @@ -342,6 +342,14 @@ "point", "centroid" ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "8" + } } ] } \ No newline at end of file diff --git a/assets/layers/slow_roads/slow_roads.json b/assets/layers/slow_roads/slow_roads.json index f1624c853..e1c344bde 100644 --- a/assets/layers/slow_roads/slow_roads.json +++ b/assets/layers/slow_roads/slow_roads.json @@ -259,6 +259,40 @@ "location": [ "point" ] + }, + { + "color": { + "render": "#eaba2a" + }, + "width": { + "render": "7" + }, + "dashArray": { + "render": "", + "mappings": [ + { + "if": "highway=cycleway", + "then": "" + }, + { + "if": "highway=path", + "then": "0 12" + }, + { + "if": { + "or": [ + "highway=footway", + "highway=pedestrian" + ] + }, + "then": "12 18" + }, + { + "if": "highway=living_street", + "then": "12 12 0 12" + } + ] + } } ] } \ No newline at end of file diff --git a/assets/layers/surveillance_camera/surveillance_camera.json b/assets/layers/surveillance_camera/surveillance_camera.json index e1efe7c79..f43765388 100644 --- a/assets/layers/surveillance_camera/surveillance_camera.json +++ b/assets/layers/surveillance_camera/surveillance_camera.json @@ -531,6 +531,14 @@ } ] } + }, + { + "color": { + "render": "#f00" + }, + "width": { + "render": "8" + } } ] } \ No newline at end of file diff --git a/assets/layers/trail/trail.json b/assets/layers/trail/trail.json index e7579f24b..fe3d6b84f 100644 --- a/assets/layers/trail/trail.json +++ b/assets/layers/trail/trail.json @@ -228,6 +228,23 @@ "location": [ "point" ] + }, + { + "color": { + "render": "#335D9F", + "mappings": [ + { + "if": "colour~*", + "then": "{colour}" + } + ] + }, + "width": { + "render": "3" + }, + "dashArray": { + "render": "5 5" + } } ] } \ No newline at end of file diff --git a/assets/layers/viewpoint/viewpoint.json b/assets/layers/viewpoint/viewpoint.json index 08c05640f..0a5342709 100644 --- a/assets/layers/viewpoint/viewpoint.json +++ b/assets/layers/viewpoint/viewpoint.json @@ -78,6 +78,10 @@ "location": [ "point" ] + }, + { + "color": "#ffffff", + "width": "5" } ] } \ No newline at end of file diff --git a/assets/layers/village_green/village_green.json b/assets/layers/village_green/village_green.json index 447bfe0e9..24b12263c 100644 --- a/assets/layers/village_green/village_green.json +++ b/assets/layers/village_green/village_green.json @@ -44,6 +44,10 @@ "point", "centroid" ] + }, + { + "color": "#937f20", + "width": "1" } ] } \ No newline at end of file diff --git a/assets/layers/waste_basket/waste_basket.json b/assets/layers/waste_basket/waste_basket.json index 9172dd452..05b19dcbe 100644 --- a/assets/layers/waste_basket/waste_basket.json +++ b/assets/layers/waste_basket/waste_basket.json @@ -203,6 +203,14 @@ "location": [ "point" ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "8" + } } ] } \ No newline at end of file diff --git a/assets/themes/aed/aed_brugge.json b/assets/themes/aed/aed_brugge.json index 516569d11..0de0fcb89 100644 --- a/assets/themes/aed/aed_brugge.json +++ b/assets/themes/aed/aed_brugge.json @@ -42,6 +42,24 @@ "iconSize": "20,20,center", "tagRenderings": [ "all_tags" + ], + "mapRendering": [ + { + "icon": { + "render": "circle:red", + "mappings": [ + { + "if": "_has_closeby_feature=yes", + "then": "circle:#008000aa" + } + ] + }, + "iconSize": "20,20,center", + "location": [ + "point" + ] + }, + {} ] } ], diff --git a/assets/themes/buurtnatuur/buurtnatuur.json b/assets/themes/buurtnatuur/buurtnatuur.json index 0f1c8648a..dde19b991 100644 --- a/assets/themes/buurtnatuur/buurtnatuur.json +++ b/assets/themes/buurtnatuur/buurtnatuur.json @@ -123,6 +123,51 @@ "nl": "Voeg een ontbrekend, erkend natuurreservaat toe, bv. een gebied dat beheerd wordt door het ANB of natuurpunt" } } + ], + "mapRendering": [ + { + "icon": { + "render": "circle:#ffffff;./assets/themes/buurtnatuur/nature_reserve.svg" + }, + "iconSize": { + "render": "50,50,center" + }, + "location": [ + "point" + ] + }, + { + "color": { + "render": "#3c3", + "mappings": [ + { + "if": { + "and": [ + "name=", + "noname=", + "operator=", + "access=", + "access:description=", + "leisure=park" + ] + }, + "then": "#cc1100" + }, + { + "if": { + "and": [ + "name=", + "noname=" + ] + }, + "then": "#fccb37" + } + ] + }, + "width": { + "render": "5" + } + } ] }, { @@ -221,6 +266,38 @@ "nl": "Voeg een ontbrekend park toe" } } + ], + "mapRendering": [ + { + "icon": { + "render": "circle:#ffffff;./assets/themes/buurtnatuur/park.svg" + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + }, + { + "color": { + "render": "#3c3", + "mappings": [ + { + "if": { + "and": [ + "name=", + "noname=" + ] + }, + "then": "#fccb37" + } + ] + }, + "width": { + "render": "5" + } + } ] }, { @@ -338,6 +415,56 @@ "nl": "Voeg een ontbrekend bos toe aan de kaart" } } + ], + "mapRendering": [ + { + "icon": { + "render": "circle:#ffffff;./assets/themes/buurtnatuur/forest.svg" + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + }, + { + "color": { + "render": "#3a3", + "mappings": [ + { + "if": { + "and": [ + "operator=", + "access=", + "access:description=" + ] + }, + "then": "#cc1100" + }, + { + "if": { + "and": [ + "operator=" + ] + }, + "then": "#cccc00" + }, + { + "if": { + "and": [ + "name=", + "noname=" + ] + }, + "then": "#fccb37" + } + ] + }, + "width": { + "render": "5" + } + } ] }, "viewpoint" diff --git a/assets/themes/campersite/campersite.json b/assets/themes/campersite/campersite.json index f7d6586c6..bbc377c63 100644 --- a/assets/themes/campersite/campersite.json +++ b/assets/themes/campersite/campersite.json @@ -699,6 +699,14 @@ "point", "centroid" ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "8" + } } ] }, @@ -1111,6 +1119,14 @@ "location": [ "point" ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "8" + } } ] } diff --git a/assets/themes/climbing/climbing.json b/assets/themes/climbing/climbing.json index 5174911cc..7ebb37693 100644 --- a/assets/themes/climbing/climbing.json +++ b/assets/themes/climbing/climbing.json @@ -585,6 +585,14 @@ "point", "centroid" ] + }, + { + "color": { + "render": "#0f0" + }, + "width": { + "render": "4" + } } ] }, @@ -868,6 +876,14 @@ "point", "centroid" ] + }, + { + "color": { + "render": "#d38d5fAA" + }, + "width": { + "render": "8" + } } ] }, @@ -1005,6 +1021,14 @@ "location": [ "point" ] + }, + { + "color": { + "render": "#ddff55AA" + }, + "width": { + "render": "2" + } } ] } diff --git a/assets/themes/cycle_highways/cycle_highways.json b/assets/themes/cycle_highways/cycle_highways.json index 01653d649..884ddf2ad 100644 --- a/assets/themes/cycle_highways/cycle_highways.json +++ b/assets/themes/cycle_highways/cycle_highways.json @@ -239,6 +239,41 @@ "location": [ "point" ] + }, + { + "color": { + "render": "#ff7392", + "mappings": [ + { + "if": "state=", + "then": "#00acfc" + }, + { + "if": "state=temporary", + "then": "#00acfc" + } + ] + }, + "width": { + "render": "4" + }, + "dashArray": { + "render": "", + "mappings": [ + { + "if": "state=temporary", + "then": "12 10" + }, + { + "if": "note:state=has_highway_no", + "then": "0 8" + }, + { + "if": "note:state=has_highway_under_construction", + "then": "12 10" + } + ] + } } ] } diff --git a/assets/themes/cyclestreets/cyclestreets.json b/assets/themes/cyclestreets/cyclestreets.json index 9f332e157..406d7cf9a 100644 --- a/assets/themes/cyclestreets/cyclestreets.json +++ b/assets/themes/cyclestreets/cyclestreets.json @@ -84,6 +84,18 @@ "width": "10", "tagRenderings": [ "images" + ], + "mapRendering": [ + { + "icon": "./assets/themes/cyclestreets/F111.svg", + "location": [ + "point" + ] + }, + { + "color": "#0000ff", + "width": "10" + } ] }, { @@ -136,6 +148,18 @@ "width": "5", "tagRenderings": [ "images" + ], + "mapRendering": [ + { + "icon": "./assets/themes/cyclestreets/F113.svg", + "location": [ + "point" + ] + }, + { + "color": "#09f9dd", + "width": "5" + } ] }, { @@ -201,6 +225,30 @@ }, "tagRenderings": [ "images" + ], + "mapRendering": [ + { + "icon": "./assets/svg/pencil.svg", + "location": [ + "point" + ] + }, + { + "color": { + "render": "#aaaaaa", + "mappings": [ + { + "then": "#0000ff", + "if": "cyclestreet=yes" + }, + { + "then": "#09f9dd", + "if": "proposed:cyclestreet=yes" + } + ] + }, + "width": "5" + } ] } ], diff --git a/assets/themes/fruit_trees/fruit_trees.json b/assets/themes/fruit_trees/fruit_trees.json index 88b145e6b..b6af011b1 100644 --- a/assets/themes/fruit_trees/fruit_trees.json +++ b/assets/themes/fruit_trees/fruit_trees.json @@ -80,6 +80,14 @@ "location": [ "point" ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "8" + } } ] }, @@ -195,6 +203,14 @@ "location": [ "point" ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "8" + } } ] } diff --git a/assets/themes/grb.json b/assets/themes/grb.json index d279a6409..616466aea 100644 --- a/assets/themes/grb.json +++ b/assets/themes/grb.json @@ -215,6 +215,14 @@ "point", "centroid" ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "2" + } } ] } diff --git a/assets/themes/hackerspaces/hackerspaces.json b/assets/themes/hackerspaces/hackerspaces.json index 8744e94c9..7cad69310 100644 --- a/assets/themes/hackerspaces/hackerspaces.json +++ b/assets/themes/hackerspaces/hackerspaces.json @@ -259,6 +259,14 @@ "point", "centroid" ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "8" + } } ] } diff --git a/assets/themes/hailhydrant/hailhydrant.json b/assets/themes/hailhydrant/hailhydrant.json index 1481a652f..665086669 100644 --- a/assets/themes/hailhydrant/hailhydrant.json +++ b/assets/themes/hailhydrant/hailhydrant.json @@ -329,6 +329,14 @@ "point", "centroid" ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "8" + } } ] }, @@ -697,6 +705,14 @@ "point", "centroid" ] + }, + { + "color": { + "render": "#c22" + }, + "width": { + "render": "1" + } } ] }, @@ -912,6 +928,14 @@ "point", "centroid" ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "1" + } } ] } diff --git a/assets/themes/postboxes/postboxes.json b/assets/themes/postboxes/postboxes.json index 6a881b29a..d8ca6024a 100644 --- a/assets/themes/postboxes/postboxes.json +++ b/assets/themes/postboxes/postboxes.json @@ -92,6 +92,14 @@ "point", "centroid" ] + }, + { + "color": { + "render": "#DADADA" + }, + "width": { + "render": "1" + } } ] }, @@ -202,6 +210,14 @@ "point", "centroid" ] + }, + { + "color": { + "render": "#DADADA" + }, + "width": { + "render": "1" + } } ] } diff --git a/assets/themes/speelplekken/speelplekken.json b/assets/themes/speelplekken/speelplekken.json index 702087d8c..175f7685b 100644 --- a/assets/themes/speelplekken/speelplekken.json +++ b/assets/themes/speelplekken/speelplekken.json @@ -42,6 +42,12 @@ "location": [ "point" ] + }, + { + "color": "#444444", + "width": { + "render": "1" + } } ] }, @@ -266,6 +272,24 @@ "location": [ "point" ] + }, + { + "color": { + "render": "#6d6", + "mappings": [ + { + "if": "color~*", + "then": "{color}" + }, + { + "if": "colour~*", + "then": "{colour}" + } + ] + }, + "width": { + "render": "9" + } } ] } diff --git a/assets/themes/uk_addresses/uk_addresses.json b/assets/themes/uk_addresses/uk_addresses.json index ee782ca10..c79225b12 100644 --- a/assets/themes/uk_addresses/uk_addresses.json +++ b/assets/themes/uk_addresses/uk_addresses.json @@ -305,6 +305,30 @@ "location": [ "point" ] + }, + { + "color": { + "render": "#00f", + "mappings": [ + { + "if": { + "or": [ + { + "and": [ + "addr:housenumber=", + "nohousenumber!=yes" + ] + }, + "addr:street=" + ] + }, + "then": "#ff0" + } + ] + }, + "width": { + "render": "8" + } } ] }, @@ -330,6 +354,14 @@ "location": [ "point" ] + }, + { + "color": { + "render": "#ccc" + }, + "width": { + "render": "0" + } } ] } diff --git a/scripts/lint.ts b/scripts/lint.ts index 70558fa4c..771328366 100644 --- a/scripts/lint.ts +++ b/scripts/lint.ts @@ -7,6 +7,8 @@ import ScriptUtils from "./ScriptUtils"; import {writeFileSync} from "fs"; import {LayerConfigJson} from "../Models/ThemeConfig/Json/LayerConfigJson"; +import LineRenderingConfig from "../Models/ThemeConfig/LineRenderingConfig"; +import LineRenderingConfigJson from "../Models/ThemeConfig/Json/LineRenderingConfigJson"; /** * In place fix @@ -26,10 +28,11 @@ function fixLayerConfig(config: LayerConfigJson): void { } } - if(config.mapRendering === undefined){ + if(config.mapRendering === undefined || true){ // This is a legacy format, lets create a pointRendering let location: ("point"|"centroid")[] = ["point"] - if(config.wayHandling === 2){ + let wayHandling: number = config.wayHandling + if(wayHandling === 2){ location = ["point", "centroid"] } config.mapRendering = [ @@ -43,6 +46,27 @@ function fixLayerConfig(config: LayerConfigJson): void { } ] + if(wayHandling !== 1){ + const lineRenderConfig = { + color: config["color"], + width: config["width"], + dashArray: config["dashArray"] + } + if(Object.keys(lineRenderConfig).length > 0){ + config.mapRendering.push(lineRenderConfig) + } + } + /*delete config["color"] + delete config["width"] + delete config["dashArray"] + + delete config["icon"] + delete config["iconOverlays"] + delete config["label"] + delete config["iconSize"] + delete config["rotation"] + */ + } From 24b4d30d6e9f621410d58c53d870b4a0cd406552 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 20 Oct 2021 02:11:44 +0200 Subject: [PATCH 12/95] Add default line rendering --- Models/ThemeConfig/LayerConfig.ts | 4 ++-- UI/Popup/SplitRoadWizard.ts | 2 +- scripts/lint.ts | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts index ff3e9baca..bfb5f2eb6 100644 --- a/Models/ThemeConfig/LayerConfig.ts +++ b/Models/ThemeConfig/LayerConfig.ts @@ -306,9 +306,9 @@ export default class LayerConfig extends WithContextLoader{ const icon = this.mapRendering[0].GenerateLeafletStyle(tags, clickable) - const lineStyle = this.lineRendering[0].GenerateLeafletStyle(tags) + const lineStyle = (this.lineRendering[0] ?? new LineRenderingConfig({}, "default"))?.GenerateLeafletStyle(tags) return { - icon, + icon: icon, ...lineStyle }; } diff --git a/UI/Popup/SplitRoadWizard.ts b/UI/Popup/SplitRoadWizard.ts index 2e2f38c85..9ee39f683 100644 --- a/UI/Popup/SplitRoadWizard.ts +++ b/UI/Popup/SplitRoadWizard.ts @@ -23,7 +23,7 @@ export default class SplitRoadWizard extends Toggle { source: {osmTags: "_cutposition=yes"}, mapRendering: [ { - location: ["point"], + location: ["point","centroid"], icon: {render: "circle:white;./assets/svg/scissors.svg"}, iconSize: {render: "30,30,center"} } diff --git a/scripts/lint.ts b/scripts/lint.ts index 771328366..50b1d9e25 100644 --- a/scripts/lint.ts +++ b/scripts/lint.ts @@ -7,7 +7,6 @@ import ScriptUtils from "./ScriptUtils"; import {writeFileSync} from "fs"; import {LayerConfigJson} from "../Models/ThemeConfig/Json/LayerConfigJson"; -import LineRenderingConfig from "../Models/ThemeConfig/LineRenderingConfig"; import LineRenderingConfigJson from "../Models/ThemeConfig/Json/LineRenderingConfigJson"; /** From 9336e19757759a71441d37cfad1ec8188b7b5b6a Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 21 Oct 2021 01:26:20 +0200 Subject: [PATCH 13/95] Fix build --- Models/ThemeConfig/LayerConfig.ts | 4 +- .../charging_station/charging_station.json | 7313 ++++++++--------- .../charging_station.protojson | 110 +- langs/layers/de.json | 7 + langs/layers/en.json | 29 + langs/layers/fi.json | 7 + langs/layers/fr.json | 18 + langs/layers/it.json | 18 + langs/layers/nl.json | 18 + langs/layers/pt.json | 7 + langs/layers/pt_BR.json | 7 + langs/layers/ru.json | 29 + langs/themes/de.json | 11 + langs/themes/en.json | 11 + 14 files changed, 3801 insertions(+), 3788 deletions(-) diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts index bfb5f2eb6..06c5eca47 100644 --- a/Models/ThemeConfig/LayerConfig.ts +++ b/Models/ThemeConfig/LayerConfig.ts @@ -199,7 +199,9 @@ export default class LayerConfig extends WithContextLoader{ return config; }); - + if(json.mapRendering === undefined){ + throw "MapRendering is undefined in "+context + } this.mapRendering = json.mapRendering .filter(r => r["icon"] !== undefined || r["label"] !== undefined) diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index 67ebde7a0..116f339cd 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -1,3797 +1,3648 @@ { - "id": "charging_station", - "name": { - "en": "Charging stations", - "it": "Stazioni di ricarica", - "ja": "充電ステーション", - "nb_NO": "Ladestasjoner", - "ru": "Зарядные станции", - "zh_Hant": "充電站", - "de": "Ladestationen" + "id": "charging_station", + "name": { + "en": "Charging stations", + "it": "Stazioni di ricarica", + "ja": "充電ステーション", + "nb_NO": "Ladestasjoner", + "ru": "Зарядные станции", + "zh_Hant": "充電站" + }, + "minzoom": 10, + "source": { + "osmTags": { + "or": [ + "amenity=charging_station", + "disused:amenity=charging_station", + "planned:amenity=charging_station", + "construction:amenity=charging_station" + ] + } + }, + "title": { + "render": { + "en": "Charging station", + "it": "Stazione di ricarica", + "ja": "充電ステーション", + "nb_NO": "Ladestasjon", + "ru": "Зарядная станция", + "zh_Hant": "充電站" + } + }, + "description": { + "en": "A charging station", + "it": "Una stazione di ricarica", + "ja": "充電ステーション", + "nb_NO": "En ladestasjon", + "ru": "Зарядная станция", + "zh_Hant": "充電站" + }, + "tagRenderings": [ + "images", + { + "id": "Type", + "question": { + "en": "Which vehicles are allowed to charge here?" + }, + "multiAnswer": true, + "mappings": [ + { + "if": "bicycle=yes", + "ifnot": "bicycle=no", + "then": { + "en": "bicycles can be charged here" + } + }, + { + "if": "motorcar=yes", + "ifnot": "motorcar=no", + "then": { + "en": "Cars can be charged here" + } + }, + { + "if": "scooter=yes", + "ifnot": "scooter=no", + "then": { + "en": "Scooters can be charged here" + } + }, + { + "if": "hgv=yes", + "ifnot": "hgv=no", + "then": { + "en": "Heavy good vehicles (such as trucks) can be charged here" + } + }, + { + "if": "bus=yes", + "ifnot": "bus=no", + "then": { + "en": "Buses can be charged here" + } + } + ] }, - "minzoom": 10, - "source": { - "osmTags": { + { + "id": "access", + "question": { + "en": "Who is allowed to use this charging station?" + }, + "render": { + "en": "Access is {access}" + }, + "freeform": { + "key": "access", + "addExtraTags": [ + "fixme=Freeform field used for access - doublecheck the value" + ] + }, + "mappings": [ + { + "if": "access=yes", + "then": "Anyone can use this charging station (payment might be needed)" + }, + { + "if": { "or": [ - "amenity=charging_station", - "disused:amenity=charging_station", - "planned:amenity=charging_station", - "construction:amenity=charging_station" + "access=permissive", + "access=public" ] + }, + "then": "Anyone can use this charging station (payment might be needed)", + "hideInAnswer": true + }, + { + "if": "access=customers", + "then": "Only customers of the place this station belongs to can use this charging station
E.g. a charging station operated by hotel which is only usable by their guests " + }, + { + "if": "access=private", + "then": "Not accessible to the general public (e.g. only accessible to the owners, employees, ...)" } + ] }, - "title": { - "render": { - "en": "Charging station", - "it": "Stazione di ricarica", - "ja": "充電ステーション", - "nb_NO": "Ladestasjon", - "ru": "Зарядная станция", - "zh_Hant": "充電站" - } + { + "id": "capacity", + "render": { + "en": "{capacity} vehicles can be charged here at the same time", + "nl": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden" + }, + "question": { + "en": "How much vehicles can be charged here at the same time?", + "nl": "Hoeveel voertuigen kunnen hier opgeladen worden?" + }, + "freeform": { + "key": "capacity", + "type": "pnat" + } }, - "description": { - "en": "A charging station", - "it": "Una stazione di ricarica", - "ja": "充電ステーション", - "nb_NO": "En ladestasjon", - "ru": "Зарядная станция", - "zh_Hant": "充電站", - "de": "Eine Ladestation" - }, - "tagRenderings": [ - "images", + { + "id": "Available_charging_stations (generated)", + "question": { + "en": "Which charging stations are available here?" + }, + "multiAnswer": true, + "mappings": [ { - "id": "Type", - "question": { - "en": "Which vehicles are allowed to charge here?", - "de": "Welche Fahrzeuge dürfen hier geladen werden?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "bicycle=yes", - "ifnot": "bicycle=no", - "then": { - "en": "bicycles can be charged here", - "de": "Fahrräder können hier geladen werden" - } - }, - { - "if": "motorcar=yes", - "ifnot": "motorcar=no", - "then": { - "en": "Cars can be charged here", - "de": "Autos können hier geladen werden" - } - }, - { - "if": "scooter=yes", - "ifnot": "scooter=no", - "then": { - "en": "Scooters can be charged here", - "de": " Roller können hier geladen werden" - } - }, - { - "if": "hgv=yes", - "ifnot": "hgv=no", - "then": { - "en": "Heavy good vehicles (such as trucks) can be charged here", - "de": "Lastkraftwagen (LKW) können hier geladen werden" - } - }, - { - "if": "bus=yes", - "ifnot": "bus=no", - "then": { - "en": "Buses can be charged here", - "de": "Busse können hier geladen werden" - } - } + "if": "socket:schuko=1", + "ifnot": "socket:schuko=", + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "hideInAnswer": { + "or": [ + "_country!=be", + "_country!=fr", + "_country!=ma", + "_country!=tn", + "_country!=pl", + "_country!=cs", + "_country!=sk", + "_country!=mo" ] + } }, { - "id": "access", - "question": { - "en": "Who is allowed to use this charging station?", - "de": "Wer darf diese Ladestation benutzen?" - }, - "render": { - "en": "Access is {access}", - "de": "Zugang ist {access}" - }, - "freeform": { - "key": "access", - "addExtraTags": [ - "fixme=Freeform field used for access - doublecheck the value" - ] - }, - "mappings": [ - { - "if": "access=yes", - "then": "Anyone can use this charging station (payment might be needed)" - }, - { - "if": { - "or": [ - "access=permissive", - "access=public" - ] - }, - "then": "Anyone can use this charging station (payment might be needed)", - "hideInAnswer": true - }, - { - "if": "access=customers", - "then": "Only customers of the place this station belongs to can use this charging station
E.g. a charging station operated by hotel which is only usable by their guests " - }, - { - "if": "access=private", - "then": "Not accessible to the general public (e.g. only accessible to the owners, employees, ...)" - } + "if": { + "and": [ + "socket:schuko~*", + "socket:schuko!=1" ] + }, + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "hideInAnswer": true }, { - "id": "capacity", - "render": { - "en": "{capacity} vehicles can be charged here at the same time", - "nl": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden", - "de": "{capacity} Fahrzeuge können hier gleichzeitig geladen werden" - }, - "question": { - "en": "How much vehicles can be charged here at the same time?", - "nl": "Hoeveel voertuigen kunnen hier opgeladen worden?", - "de": "Wie viele Fahrzeuge können hier gleichzeitig geladen werden?" - }, - "freeform": { - "key": "capacity", - "type": "pnat" - } + "if": "socket:typee=1", + "ifnot": "socket:typee=", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" + } }, { - "id": "Available_charging_stations (generated)", - "question": { - "en": "Which charging stations are available here?", - "de": "Welche Ladestationen gibt es hier?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "socket:schuko=1", - "ifnot": "socket:schuko=", - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "hideInAnswer": { - "or": [ - "_country!=be", - "_country!=fr", - "_country!=ma", - "_country!=tn", - "_country!=pl", - "_country!=cs", - "_country!=sk", - "_country!=mo" - ] - } - }, - { - "if": { - "and": [ - "socket:schuko~*", - "socket:schuko!=1" - ] - }, - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:typee=1", - "ifnot": "socket:typee=", - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" - } - }, - { - "if": { - "and": [ - "socket:typee~*", - "socket:typee!=1" - ] - }, - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:chademo=1", - "ifnot": "socket:chademo=", - "then": { - "en": "
Chademo
", - "nl": "
Chademo
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:chademo~*", - "socket:chademo!=1" - ] - }, - "then": { - "en": "
Chademo
", - "nl": "
Chademo
", - "de": "
Chademo
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1_cable=1", - "ifnot": "socket:type1_cable=", - "then": { - "en": "
Type 1 with cable (J1772)
", - "nl": "
Type 1 met kabel (J1772)
", - "de": "
Typ 1 mit Kabel (J1772)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=1" - ] - }, - "then": { - "en": "
Type 1 with cable (J1772)
", - "nl": "
Type 1 met kabel (J1772)
", - "de": "
Typ 1 mit Kabel (J1772)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1=1", - "ifnot": "socket:type1=", - "then": { - "en": "
Type 1 without cable (J1772)
", - "nl": "
Type 1 zonder kabel (J1772)
", - "de": "
Typ 1 ohne Kabel (J1772)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type1~*", - "socket:type1!=1" - ] - }, - "then": { - "en": "
Type 1 without cable (J1772)
", - "nl": "
Type 1 zonder kabel (J1772)
", - "de": "
Typ 1 ohne Kabel (J1772)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1_combo=1", - "ifnot": "socket:type1_combo=", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
", - "de": "
Typ 1 CCS (auch bekannt als Typ 1 Combo)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=1" - ] - }, - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
", - "de": "
Typ 1 CCS (auch bekannt als Typ 1 Combo)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_supercharger=1", - "ifnot": "socket:tesla_supercharger=", - "then": { - "en": "
Tesla Supercharger
", - "nl": "
Tesla Supercharger
", - "de": "
Tesla Supercharger
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=1" - ] - }, - "then": { - "en": "
Tesla Supercharger
", - "nl": "
Tesla Supercharger
", - "de": "
Tesla Supercharger
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type2=1", - "ifnot": "socket:type2=", - "then": { - "en": "
Type 2 (mennekes)
", - "nl": "
Type 2 (mennekes)
", - "de": "
Typ 2 (Mennekes)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type2~*", - "socket:type2!=1" - ] - }, - "then": { - "en": "
Type 2 (mennekes)
", - "nl": "
Type 2 (mennekes)
", - "de": "
Typ 2 (Mennekes)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type2_combo=1", - "ifnot": "socket:type2_combo=", - "then": { - "en": "
Type 2 CCS (mennekes)
", - "nl": "
Type 2 CCS (mennekes)
", - "de": "
Typ 2 CCS (Mennekes)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=1" - ] - }, - "then": { - "en": "
Type 2 CCS (mennekes)
", - "nl": "
Type 2 CCS (mennekes)
", - "de": "
Typ 2 CCS (Mennekes)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type2_cable=1", - "ifnot": "socket:type2_cable=", - "then": { - "en": "
Type 2 with cable (mennekes)
", - "nl": "
Type 2 met kabel (J1772)
", - "de": "
Typ 2 mit Kabel (Mennekes)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=1" - ] - }, - "then": { - "en": "
Type 2 with cable (mennekes)
", - "nl": "
Type 2 met kabel (J1772)
", - "de": "
Typ 2 mit Kabel (Mennekes)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_supercharger_ccs=1", - "ifnot": "socket:tesla_supercharger_ccs=", - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
", - "de": "
Tesla Supercharger CCS (Typ 2 CSS)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=1" - ] - }, - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
", - "de": "
Tesla Supercharger CCS (Typ 2 CSS)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_destination=1", - "ifnot": "socket:tesla_destination=", - "then": { - "en": "
Tesla Supercharger (destination)
", - "nl": "
Tesla Supercharger (destination)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - }, - { - "or": [ - "_country!=us" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=1" - ] - }, - "then": { - "en": "
Tesla Supercharger (destination)
", - "nl": "
Tesla Supercharger (destination)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_destination=1", - "ifnot": "socket:tesla_destination=", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - }, - { - "or": [ - "_country=us" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=1" - ] - }, - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:USB-A=1", - "ifnot": "socket:USB-A=", - "then": { - "en": "
USB to charge phones and small electronics
", - "nl": "
USB om GSMs en kleine electronica op te laden
", - "de": "
USB zum Laden von Smartphones oder Elektrokleingeräten
" - } - }, - { - "if": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=1" - ] - }, - "then": { - "en": "
USB to charge phones and small electronics
", - "nl": "
USB om GSMs en kleine electronica op te laden
", - "de": "
USB zum Laden von Smartphones und Elektrokleingeräten
" - }, - "hideInAnswer": true - }, - { - "if": "socket:bosch_3pin=1", - "ifnot": "socket:bosch_3pin=", - "then": { - "en": "
Bosch Active Connect with 3 pins and cable
", - "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "bicycle=no" - ] - }, - { - "and": [ - { - "or": [ - "car=yes", - "motorcar=yes", - "hgv=yes", - "bus=yes" - ] - }, - "bicycle!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:bosch_3pin~*", - "socket:bosch_3pin!=1" - ] - }, - "then": { - "en": "
Bosch Active Connect with 3 pins and cable
", - "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "hideInAnswer": true - }, - { - "if": "socket:bosch_5pin=1", - "ifnot": "socket:bosch_5pin=", - "then": { - "en": "
Bosch Active Connect with 5 pins and cable
", - "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
", - "de": "
Bosch Active Connect mit 5 Pins und Kabel
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "bicycle=no" - ] - }, - { - "and": [ - { - "or": [ - "car=yes", - "motorcar=yes", - "hgv=yes", - "bus=yes" - ] - }, - "bicycle!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:bosch_5pin~*", - "socket:bosch_5pin!=1" - ] - }, - "then": { - "en": "
Bosch Active Connect with 5 pins and cable
", - "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
", - "de": "
Bosch Active Connect mit 5 Pins und Kabel
" - }, - "hideInAnswer": true - } + "if": { + "and": [ + "socket:typee~*", + "socket:typee!=1" ] + }, + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "hideInAnswer": true }, { - "id": "plugs-0", - "question": { - "en": "How much plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
are available here?", - "nl": "Hoeveel stekkers van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:schuko} plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
available here", - "nl": "Hier zijn {socket:schuko} stekkers van het type
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "freeform": { - "key": "socket:schuko", - "type": "pnat" - }, - "condition": { + "if": "socket:chademo=1", + "ifnot": "socket:chademo=", + "then": { + "en": "
Chademo
", + "nl": "
Chademo
" + }, + "hideInAnswer": { + "or": [ + { "and": [ - "socket:schuko~*", - "socket:schuko!=0" + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" ] - } - }, - { - "id": "voltage-0", - "question": { - "en": "What voltage do the plugs with
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", - "nl": "Welke spanning levert de stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "render": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs {socket:schuko:voltage} volt", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft een spanning van {socket:schuko:voltage} volt" - }, - "freeform": { - "key": "socket:schuko:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:voltage=230 V", - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs 230 volt", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft een spanning van 230 volt" - } - } - ], - "condition": { + }, + { "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "current-0", - "question": { - "en": "What current do the plugs with
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", - "nl": "Welke stroom levert de stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
?" - }, - "render": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most {socket:schuko:current}A", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een stroom van maximaal {socket:schuko:current}A" - }, - "freeform": { - "key": "socket:schuko:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:current=16 A", - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 16 A", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een stroom van maximaal 16 A" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "power-output-0", - "question": { - "en": "What power output does a single plug of type
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
?" - }, - "render": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most {socket:schuko:output}", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een vermogen van maximaal {socket:schuko:output}" - }, - "freeform": { - "key": "socket:schuko:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:output=3.6 kw", - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 3.6 kw", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een vermogen van maximaal 3.6 kw" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "plugs-1", - "question": { - "en": "How much plugs of type
European wall plug with ground pin (CEE7/4 type E)
are available here?", - "nl": "Hoeveel stekkers van type
Europese stekker met aardingspin (CEE7/4 type E)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:typee} plugs of type
European wall plug with ground pin (CEE7/4 type E)
available here", - "nl": "Hier zijn {socket:typee} stekkers van het type
Europese stekker met aardingspin (CEE7/4 type E)
" - }, - "freeform": { - "key": "socket:typee", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "voltage-1", - "question": { - "en": "What voltage do the plugs with
European wall plug with ground pin (CEE7/4 type E)
offer?", - "nl": "Welke spanning levert de stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
" - }, - "render": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs {socket:typee:voltage} volt", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
heeft een spanning van {socket:typee:voltage} volt" - }, - "freeform": { - "key": "socket:typee:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:voltage=230 V", - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs 230 volt", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
heeft een spanning van 230 volt" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "current-1", - "question": { - "en": "What current do the plugs with
European wall plug with ground pin (CEE7/4 type E)
offer?", - "nl": "Welke stroom levert de stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
?" - }, - "render": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most {socket:typee:current}A", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een stroom van maximaal {socket:typee:current}A" - }, - "freeform": { - "key": "socket:typee:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:current=16 A", - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 16 A", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een stroom van maximaal 16 A" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "power-output-1", - "question": { - "en": "What power output does a single plug of type
European wall plug with ground pin (CEE7/4 type E)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
?" - }, - "render": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most {socket:typee:output}", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal {socket:typee:output}" - }, - "freeform": { - "key": "socket:typee:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:output=3 kw", - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 3 kw", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal 3 kw" - } - }, - { - "if": "socket:socket:typee:output=22 kw", - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 22 kw", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "plugs-2", - "question": { - "en": "How much plugs of type
Chademo
are available here?", - "nl": "Hoeveel stekkers van type
Chademo
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:chademo} plugs of type
Chademo
available here", - "nl": "Hier zijn {socket:chademo} stekkers van het type
Chademo
" - }, - "freeform": { - "key": "socket:chademo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "voltage-2", - "question": { - "en": "What voltage do the plugs with
Chademo
offer?", - "nl": "Welke spanning levert de stekker van type
Chademo
" - }, - "render": { - "en": "
Chademo
outputs {socket:chademo:voltage} volt", - "nl": "
Chademo
heeft een spanning van {socket:chademo:voltage} volt" - }, - "freeform": { - "key": "socket:chademo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:voltage=500 V", - "then": { - "en": "
Chademo
outputs 500 volt", - "nl": "
Chademo
heeft een spanning van 500 volt" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "current-2", - "question": { - "en": "What current do the plugs with
Chademo
offer?", - "nl": "Welke stroom levert de stekker van type
Chademo
?" - }, - "render": { - "en": "
Chademo
outputs at most {socket:chademo:current}A", - "nl": "
Chademo
levert een stroom van maximaal {socket:chademo:current}A" - }, - "freeform": { - "key": "socket:chademo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:current=120 A", - "then": { - "en": "
Chademo
outputs at most 120 A", - "nl": "
Chademo
levert een stroom van maximaal 120 A" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "power-output-2", - "question": { - "en": "What power output does a single plug of type
Chademo
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Chademo
?" - }, - "render": { - "en": "
Chademo
outputs at most {socket:chademo:output}", - "nl": "
Chademo
levert een vermogen van maximaal {socket:chademo:output}" - }, - "freeform": { - "key": "socket:chademo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:output=50 kw", - "then": { - "en": "
Chademo
outputs at most 50 kw", - "nl": "
Chademo
levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "plugs-3", - "question": { - "en": "How much plugs of type
Type 1 with cable (J1772)
are available here?", - "nl": "Hoeveel stekkers van type
Type 1 met kabel (J1772)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:type1_cable} plugs of type
Type 1 with cable (J1772)
available here", - "nl": "Hier zijn {socket:type1_cable} stekkers van het type
Type 1 met kabel (J1772)
" - }, - "freeform": { - "key": "socket:type1_cable", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "voltage-3", - "question": { - "en": "What voltage do the plugs with
Type 1 with cable (J1772)
offer?", - "nl": "Welke spanning levert de stekker van type
Type 1 met kabel (J1772)
" - }, - "render": { - "en": "
Type 1 with cable (J1772)
outputs {socket:type1_cable:voltage} volt", - "nl": "
Type 1 met kabel (J1772)
heeft een spanning van {socket:type1_cable:voltage} volt" - }, - "freeform": { - "key": "socket:type1_cable:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:voltage=200 V", - "then": { - "en": "
Type 1 with cable (J1772)
outputs 200 volt", - "nl": "
Type 1 met kabel (J1772)
heeft een spanning van 200 volt" - } - }, - { - "if": "socket:socket:type1_cable:voltage=240 V", - "then": { - "en": "
Type 1 with cable (J1772)
outputs 240 volt", - "nl": "
Type 1 met kabel (J1772)
heeft een spanning van 240 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "current-3", - "question": { - "en": "What current do the plugs with
Type 1 with cable (J1772)
offer?", - "nl": "Welke stroom levert de stekker van type
Type 1 met kabel (J1772)
?" - }, - "render": { - "en": "
Type 1 with cable (J1772)
outputs at most {socket:type1_cable:current}A", - "nl": "
Type 1 met kabel (J1772)
levert een stroom van maximaal {socket:type1_cable:current}A" - }, - "freeform": { - "key": "socket:type1_cable:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:current=32 A", - "then": { - "en": "
Type 1 with cable (J1772)
outputs at most 32 A", - "nl": "
Type 1 met kabel (J1772)
levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "power-output-3", - "question": { - "en": "What power output does a single plug of type
Type 1 with cable (J1772)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Type 1 met kabel (J1772)
?" - }, - "render": { - "en": "
Type 1 with cable (J1772)
outputs at most {socket:type1_cable:output}", - "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal {socket:type1_cable:output}" - }, - "freeform": { - "key": "socket:type1_cable:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:output=3.7 kw", - "then": { - "en": "
Type 1 with cable (J1772)
outputs at most 3.7 kw", - "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal 3.7 kw" - } - }, - { - "if": "socket:socket:type1_cable:output=7 kw", - "then": { - "en": "
Type 1 with cable (J1772)
outputs at most 7 kw", - "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal 7 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "plugs-4", - "question": { - "en": "How much plugs of type
Type 1 without cable (J1772)
are available here?", - "nl": "Hoeveel stekkers van type
Type 1 zonder kabel (J1772)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:type1} plugs of type
Type 1 without cable (J1772)
available here", - "nl": "Hier zijn {socket:type1} stekkers van het type
Type 1 zonder kabel (J1772)
" - }, - "freeform": { - "key": "socket:type1", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "voltage-4", - "question": { - "en": "What voltage do the plugs with
Type 1 without cable (J1772)
offer?", - "nl": "Welke spanning levert de stekker van type
Type 1 zonder kabel (J1772)
" - }, - "render": { - "en": "
Type 1 without cable (J1772)
outputs {socket:type1:voltage} volt", - "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van {socket:type1:voltage} volt" - }, - "freeform": { - "key": "socket:type1:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:voltage=200 V", - "then": { - "en": "
Type 1 without cable (J1772)
outputs 200 volt", - "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van 200 volt" - } - }, - { - "if": "socket:socket:type1:voltage=240 V", - "then": { - "en": "
Type 1 without cable (J1772)
outputs 240 volt", - "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van 240 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "current-4", - "question": { - "en": "What current do the plugs with
Type 1 without cable (J1772)
offer?", - "nl": "Welke stroom levert de stekker van type
Type 1 zonder kabel (J1772)
?" - }, - "render": { - "en": "
Type 1 without cable (J1772)
outputs at most {socket:type1:current}A", - "nl": "
Type 1 zonder kabel (J1772)
levert een stroom van maximaal {socket:type1:current}A" - }, - "freeform": { - "key": "socket:type1:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:current=32 A", - "then": { - "en": "
Type 1 without cable (J1772)
outputs at most 32 A", - "nl": "
Type 1 zonder kabel (J1772)
levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "power-output-4", - "question": { - "en": "What power output does a single plug of type
Type 1 without cable (J1772)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Type 1 zonder kabel (J1772)
?" - }, - "render": { - "en": "
Type 1 without cable (J1772)
outputs at most {socket:type1:output}", - "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal {socket:type1:output}" - }, - "freeform": { - "key": "socket:type1:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:output=3.7 kw", - "then": { - "en": "
Type 1 without cable (J1772)
outputs at most 3.7 kw", - "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 3.7 kw" - } - }, - { - "if": "socket:socket:type1:output=6.6 kw", - "then": { - "en": "
Type 1 without cable (J1772)
outputs at most 6.6 kw", - "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 6.6 kw" - } - }, - { - "if": "socket:socket:type1:output=7 kw", - "then": { - "en": "
Type 1 without cable (J1772)
outputs at most 7 kw", - "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 7 kw" - } - }, - { - "if": "socket:socket:type1:output=7.2 kw", - "then": { - "en": "
Type 1 without cable (J1772)
outputs at most 7.2 kw", - "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 7.2 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "plugs-5", - "question": { - "en": "How much plugs of type
Type 1 CCS (aka Type 1 Combo)
are available here?", - "nl": "Hoeveel stekkers van type
Type 1 CCS (ook gekend als Type 1 Combo)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:type1_combo} plugs of type
Type 1 CCS (aka Type 1 Combo)
available here", - "nl": "Hier zijn {socket:type1_combo} stekkers van het type
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "freeform": { - "key": "socket:type1_combo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "voltage-5", - "question": { - "en": "What voltage do the plugs with
Type 1 CCS (aka Type 1 Combo)
offer?", - "nl": "Welke spanning levert de stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "render": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs {socket:type1_combo:voltage} volt", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van {socket:type1_combo:voltage} volt" - }, - "freeform": { - "key": "socket:type1_combo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:voltage=400 V", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs 400 volt", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van 400 volt" - } - }, - { - "if": "socket:socket:type1_combo:voltage=1000 V", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs 1000 volt", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van 1000 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "current-5", - "question": { - "en": "What current do the plugs with
Type 1 CCS (aka Type 1 Combo)
offer?", - "nl": "Welke stroom levert de stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
?" - }, - "render": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most {socket:type1_combo:current}A", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal {socket:type1_combo:current}A" - }, - "freeform": { - "key": "socket:type1_combo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:current=50 A", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 A", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal 50 A" - } - }, - { - "if": "socket:socket:type1_combo:current=125 A", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 125 A", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal 125 A" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "power-output-5", - "question": { - "en": "What power output does a single plug of type
Type 1 CCS (aka Type 1 Combo)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
?" - }, - "render": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most {socket:type1_combo:output}", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal {socket:type1_combo:output}" - }, - "freeform": { - "key": "socket:type1_combo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:output=50 kw", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 kw", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 50 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=62.5 kw", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 62.5 kw", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 62.5 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=150 kw", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 150 kw", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=350 kw", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 350 kw", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 350 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "plugs-6", - "question": { - "en": "How much plugs of type
Tesla Supercharger
are available here?", - "nl": "Hoeveel stekkers van type
Tesla Supercharger
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:tesla_supercharger} plugs of type
Tesla Supercharger
available here", - "nl": "Hier zijn {socket:tesla_supercharger} stekkers van het type
Tesla Supercharger
" - }, - "freeform": { - "key": "socket:tesla_supercharger", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "voltage-6", - "question": { - "en": "What voltage do the plugs with
Tesla Supercharger
offer?", - "nl": "Welke spanning levert de stekker van type
Tesla Supercharger
" - }, - "render": { - "en": "
Tesla Supercharger
outputs {socket:tesla_supercharger:voltage} volt", - "nl": "
Tesla Supercharger
heeft een spanning van {socket:tesla_supercharger:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_supercharger:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:voltage=480 V", - "then": { - "en": "
Tesla Supercharger
outputs 480 volt", - "nl": "
Tesla Supercharger
heeft een spanning van 480 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "current-6", - "question": { - "en": "What current do the plugs with
Tesla Supercharger
offer?", - "nl": "Welke stroom levert de stekker van type
Tesla Supercharger
?" - }, - "render": { - "en": "
Tesla Supercharger
outputs at most {socket:tesla_supercharger:current}A", - "nl": "
Tesla Supercharger
levert een stroom van maximaal {socket:tesla_supercharger:current}A" - }, - "freeform": { - "key": "socket:tesla_supercharger:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:current=125 A", - "then": { - "en": "
Tesla Supercharger
outputs at most 125 A", - "nl": "
Tesla Supercharger
levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_supercharger:current=350 A", - "then": { - "en": "
Tesla Supercharger
outputs at most 350 A", - "nl": "
Tesla Supercharger
levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "power-output-6", - "question": { - "en": "What power output does a single plug of type
Tesla Supercharger
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger
?" - }, - "render": { - "en": "
Tesla Supercharger
outputs at most {socket:tesla_supercharger:output}", - "nl": "
Tesla Supercharger
levert een vermogen van maximaal {socket:tesla_supercharger:output}" - }, - "freeform": { - "key": "socket:tesla_supercharger:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:output=120 kw", - "then": { - "en": "
Tesla Supercharger
outputs at most 120 kw", - "nl": "
Tesla Supercharger
levert een vermogen van maximaal 120 kw" - } - }, - { - "if": "socket:socket:tesla_supercharger:output=150 kw", - "then": { - "en": "
Tesla Supercharger
outputs at most 150 kw", - "nl": "
Tesla Supercharger
levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:tesla_supercharger:output=250 kw", - "then": { - "en": "
Tesla Supercharger
outputs at most 250 kw", - "nl": "
Tesla Supercharger
levert een vermogen van maximaal 250 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "plugs-7", - "question": { - "en": "How much plugs of type
Type 2 (mennekes)
are available here?", - "nl": "Hoeveel stekkers van type
Type 2 (mennekes)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:type2} plugs of type
Type 2 (mennekes)
available here", - "nl": "Hier zijn {socket:type2} stekkers van het type
Type 2 (mennekes)
" - }, - "freeform": { - "key": "socket:type2", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "voltage-7", - "question": { - "en": "What voltage do the plugs with
Type 2 (mennekes)
offer?", - "nl": "Welke spanning levert de stekker van type
Type 2 (mennekes)
" - }, - "render": { - "en": "
Type 2 (mennekes)
outputs {socket:type2:voltage} volt", - "nl": "
Type 2 (mennekes)
heeft een spanning van {socket:type2:voltage} volt" - }, - "freeform": { - "key": "socket:type2:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:voltage=230 V", - "then": { - "en": "
Type 2 (mennekes)
outputs 230 volt", - "nl": "
Type 2 (mennekes)
heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:type2:voltage=400 V", - "then": { - "en": "
Type 2 (mennekes)
outputs 400 volt", - "nl": "
Type 2 (mennekes)
heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "current-7", - "question": { - "en": "What current do the plugs with
Type 2 (mennekes)
offer?", - "nl": "Welke stroom levert de stekker van type
Type 2 (mennekes)
?" - }, - "render": { - "en": "
Type 2 (mennekes)
outputs at most {socket:type2:current}A", - "nl": "
Type 2 (mennekes)
levert een stroom van maximaal {socket:type2:current}A" - }, - "freeform": { - "key": "socket:type2:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:current=16 A", - "then": { - "en": "
Type 2 (mennekes)
outputs at most 16 A", - "nl": "
Type 2 (mennekes)
levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:type2:current=32 A", - "then": { - "en": "
Type 2 (mennekes)
outputs at most 32 A", - "nl": "
Type 2 (mennekes)
levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "power-output-7", - "question": { - "en": "What power output does a single plug of type
Type 2 (mennekes)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Type 2 (mennekes)
?" - }, - "render": { - "en": "
Type 2 (mennekes)
outputs at most {socket:type2:output}", - "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal {socket:type2:output}" - }, - "freeform": { - "key": "socket:type2:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:output=11 kw", - "then": { - "en": "
Type 2 (mennekes)
outputs at most 11 kw", - "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:type2:output=22 kw", - "then": { - "en": "
Type 2 (mennekes)
outputs at most 22 kw", - "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "plugs-8", - "question": { - "en": "How much plugs of type
Type 2 CCS (mennekes)
are available here?", - "nl": "Hoeveel stekkers van type
Type 2 CCS (mennekes)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:type2_combo} plugs of type
Type 2 CCS (mennekes)
available here", - "nl": "Hier zijn {socket:type2_combo} stekkers van het type
Type 2 CCS (mennekes)
" - }, - "freeform": { - "key": "socket:type2_combo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "voltage-8", - "question": { - "en": "What voltage do the plugs with
Type 2 CCS (mennekes)
offer?", - "nl": "Welke spanning levert de stekker van type
Type 2 CCS (mennekes)
" - }, - "render": { - "en": "
Type 2 CCS (mennekes)
outputs {socket:type2_combo:voltage} volt", - "nl": "
Type 2 CCS (mennekes)
heeft een spanning van {socket:type2_combo:voltage} volt" - }, - "freeform": { - "key": "socket:type2_combo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:voltage=500 V", - "then": { - "en": "
Type 2 CCS (mennekes)
outputs 500 volt", - "nl": "
Type 2 CCS (mennekes)
heeft een spanning van 500 volt" - } - }, - { - "if": "socket:socket:type2_combo:voltage=920 V", - "then": { - "en": "
Type 2 CCS (mennekes)
outputs 920 volt", - "nl": "
Type 2 CCS (mennekes)
heeft een spanning van 920 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "current-8", - "question": { - "en": "What current do the plugs with
Type 2 CCS (mennekes)
offer?", - "nl": "Welke stroom levert de stekker van type
Type 2 CCS (mennekes)
?" - }, - "render": { - "en": "
Type 2 CCS (mennekes)
outputs at most {socket:type2_combo:current}A", - "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal {socket:type2_combo:current}A" - }, - "freeform": { - "key": "socket:type2_combo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:current=125 A", - "then": { - "en": "
Type 2 CCS (mennekes)
outputs at most 125 A", - "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:type2_combo:current=350 A", - "then": { - "en": "
Type 2 CCS (mennekes)
outputs at most 350 A", - "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "power-output-8", - "question": { - "en": "What power output does a single plug of type
Type 2 CCS (mennekes)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Type 2 CCS (mennekes)
?" - }, - "render": { - "en": "
Type 2 CCS (mennekes)
outputs at most {socket:type2_combo:output}", - "nl": "
Type 2 CCS (mennekes)
levert een vermogen van maximaal {socket:type2_combo:output}" - }, - "freeform": { - "key": "socket:type2_combo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:output=50 kw", - "then": { - "en": "
Type 2 CCS (mennekes)
outputs at most 50 kw", - "nl": "
Type 2 CCS (mennekes)
levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "plugs-9", - "question": { - "en": "How much plugs of type
Type 2 with cable (mennekes)
are available here?", - "nl": "Hoeveel stekkers van type
Type 2 met kabel (J1772)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:type2_cable} plugs of type
Type 2 with cable (mennekes)
available here", - "nl": "Hier zijn {socket:type2_cable} stekkers van het type
Type 2 met kabel (J1772)
" - }, - "freeform": { - "key": "socket:type2_cable", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "voltage-9", - "question": { - "en": "What voltage do the plugs with
Type 2 with cable (mennekes)
offer?", - "nl": "Welke spanning levert de stekker van type
Type 2 met kabel (J1772)
" - }, - "render": { - "en": "
Type 2 with cable (mennekes)
outputs {socket:type2_cable:voltage} volt", - "nl": "
Type 2 met kabel (J1772)
heeft een spanning van {socket:type2_cable:voltage} volt" - }, - "freeform": { - "key": "socket:type2_cable:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:voltage=230 V", - "then": { - "en": "
Type 2 with cable (mennekes)
outputs 230 volt", - "nl": "
Type 2 met kabel (J1772)
heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:type2_cable:voltage=400 V", - "then": { - "en": "
Type 2 with cable (mennekes)
outputs 400 volt", - "nl": "
Type 2 met kabel (J1772)
heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "current-9", - "question": { - "en": "What current do the plugs with
Type 2 with cable (mennekes)
offer?", - "nl": "Welke stroom levert de stekker van type
Type 2 met kabel (J1772)
?" - }, - "render": { - "en": "
Type 2 with cable (mennekes)
outputs at most {socket:type2_cable:current}A", - "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal {socket:type2_cable:current}A" - }, - "freeform": { - "key": "socket:type2_cable:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:current=16 A", - "then": { - "en": "
Type 2 with cable (mennekes)
outputs at most 16 A", - "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:type2_cable:current=32 A", - "then": { - "en": "
Type 2 with cable (mennekes)
outputs at most 32 A", - "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "power-output-9", - "question": { - "en": "What power output does a single plug of type
Type 2 with cable (mennekes)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Type 2 met kabel (J1772)
?" - }, - "render": { - "en": "
Type 2 with cable (mennekes)
outputs at most {socket:type2_cable:output}", - "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal {socket:type2_cable:output}" - }, - "freeform": { - "key": "socket:type2_cable:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:output=11 kw", - "then": { - "en": "
Type 2 with cable (mennekes)
outputs at most 11 kw", - "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:type2_cable:output=22 kw", - "then": { - "en": "
Type 2 with cable (mennekes)
outputs at most 22 kw", - "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "plugs-10", - "question": { - "en": "How much plugs of type
Tesla Supercharger CCS (a branded type2_css)
are available here?", - "nl": "Hoeveel stekkers van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:tesla_supercharger_ccs} plugs of type
Tesla Supercharger CCS (a branded type2_css)
available here", - "nl": "Hier zijn {socket:tesla_supercharger_ccs} stekkers van het type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "voltage-10", - "question": { - "en": "What voltage do the plugs with
Tesla Supercharger CCS (a branded type2_css)
offer?", - "nl": "Welke spanning levert de stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "render": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs {socket:tesla_supercharger_ccs:voltage} volt", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:voltage=500 V", - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs 500 volt", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van 500 volt" - } - }, - { - "if": "socket:socket:tesla_supercharger_ccs:voltage=920 V", - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs 920 volt", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van 920 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "current-10", - "question": { - "en": "What current do the plugs with
Tesla Supercharger CCS (a branded type2_css)
offer?", - "nl": "Welke stroom levert de stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
?" - }, - "render": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most {socket:tesla_supercharger_ccs:current}A", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:current=125 A", - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 125 A", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_supercharger_ccs:current=350 A", - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 350 A", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "power-output-10", - "question": { - "en": "What power output does a single plug of type
Tesla Supercharger CCS (a branded type2_css)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
?" - }, - "render": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most {socket:tesla_supercharger_ccs:output}", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:output=50 kw", - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 50 kw", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "plugs-11", - "question": { - "en": "How much plugs of type
Tesla Supercharger (destination)
are available here?", - "nl": "Hoeveel stekkers van type
Tesla Supercharger (destination)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:tesla_destination} plugs of type
Tesla Supercharger (destination)
available here", - "nl": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla Supercharger (destination)
" - }, - "freeform": { - "key": "socket:tesla_destination", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "voltage-11", - "question": { - "en": "What voltage do the plugs with
Tesla Supercharger (destination)
offer?", - "nl": "Welke spanning levert de stekker van type
Tesla Supercharger (destination)
" - }, - "render": { - "en": "
Tesla Supercharger (destination)
outputs {socket:tesla_destination:voltage} volt", - "nl": "
Tesla Supercharger (destination)
heeft een spanning van {socket:tesla_destination:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_destination:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:voltage=480 V", - "then": { - "en": "
Tesla Supercharger (destination)
outputs 480 volt", - "nl": "
Tesla Supercharger (destination)
heeft een spanning van 480 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "current-11", - "question": { - "en": "What current do the plugs with
Tesla Supercharger (destination)
offer?", - "nl": "Welke stroom levert de stekker van type
Tesla Supercharger (destination)
?" - }, - "render": { - "en": "
Tesla Supercharger (destination)
outputs at most {socket:tesla_destination:current}A", - "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal {socket:tesla_destination:current}A" - }, - "freeform": { - "key": "socket:tesla_destination:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:current=125 A", - "then": { - "en": "
Tesla Supercharger (destination)
outputs at most 125 A", - "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_destination:current=350 A", - "then": { - "en": "
Tesla Supercharger (destination)
outputs at most 350 A", - "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "power-output-11", - "question": { - "en": "What power output does a single plug of type
Tesla Supercharger (destination)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger (destination)
?" - }, - "render": { - "en": "
Tesla Supercharger (destination)
outputs at most {socket:tesla_destination:output}", - "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal {socket:tesla_destination:output}" - }, - "freeform": { - "key": "socket:tesla_destination:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:output=120 kw", - "then": { - "en": "
Tesla Supercharger (destination)
outputs at most 120 kw", - "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 120 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=150 kw", - "then": { - "en": "
Tesla Supercharger (destination)
outputs at most 150 kw", - "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=250 kw", - "then": { - "en": "
Tesla Supercharger (destination)
outputs at most 250 kw", - "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 250 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "plugs-12", - "question": { - "en": "How much plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
are available here?", - "nl": "Hoeveel stekkers van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:tesla_destination} plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
available here", - "nl": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "freeform": { - "key": "socket:tesla_destination", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "voltage-12", - "question": { - "en": "What voltage do the plugs with
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", - "nl": "Welke spanning levert de stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "render": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs {socket:tesla_destination:voltage} volt", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van {socket:tesla_destination:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_destination:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:voltage=230 V", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 230 volt", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:tesla_destination:voltage=400 V", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 400 volt", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "current-12", - "question": { - "en": "What current do the plugs with
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", - "nl": "Welke stroom levert de stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
?" - }, - "render": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most {socket:tesla_destination:current}A", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal {socket:tesla_destination:current}A" - }, - "freeform": { - "key": "socket:tesla_destination:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:current=16 A", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 16 A", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:tesla_destination:current=32 A", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 32 A", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "power-output-12", - "question": { - "en": "What power output does a single plug of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
?" - }, - "render": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most {socket:tesla_destination:output}", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal {socket:tesla_destination:output}" - }, - "freeform": { - "key": "socket:tesla_destination:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:output=11 kw", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 11 kw", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=22 kw", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 22 kw", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "plugs-13", - "question": { - "en": "How much plugs of type
USB to charge phones and small electronics
are available here?", - "nl": "Hoeveel stekkers van type
USB om GSMs en kleine electronica op te laden
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:USB-A} plugs of type
USB to charge phones and small electronics
available here", - "nl": "Hier zijn {socket:USB-A} stekkers van het type
USB om GSMs en kleine electronica op te laden
" - }, - "freeform": { - "key": "socket:USB-A", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "voltage-13", - "question": { - "en": "What voltage do the plugs with
USB to charge phones and small electronics
offer?", - "nl": "Welke spanning levert de stekker van type
USB om GSMs en kleine electronica op te laden
" - }, - "render": { - "en": "
USB to charge phones and small electronics
outputs {socket:USB-A:voltage} volt", - "nl": "
USB om GSMs en kleine electronica op te laden
heeft een spanning van {socket:USB-A:voltage} volt" - }, - "freeform": { - "key": "socket:USB-A:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:USB-A:voltage=5 V", - "then": { - "en": "
USB to charge phones and small electronics
outputs 5 volt", - "nl": "
USB om GSMs en kleine electronica op te laden
heeft een spanning van 5 volt" - } - } - ], - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "current-13", - "question": { - "en": "What current do the plugs with
USB to charge phones and small electronics
offer?", - "nl": "Welke stroom levert de stekker van type
USB om GSMs en kleine electronica op te laden
?" - }, - "render": { - "en": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:current}A", - "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal {socket:USB-A:current}A" - }, - "freeform": { - "key": "socket:USB-A:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:USB-A:current=1 A", - "then": { - "en": "
USB to charge phones and small electronics
outputs at most 1 A", - "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal 1 A" - } - }, - { - "if": "socket:socket:USB-A:current=2 A", - "then": { - "en": "
USB to charge phones and small electronics
outputs at most 2 A", - "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal 2 A" - } - } - ], - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "power-output-13", - "question": { - "en": "What power output does a single plug of type
USB to charge phones and small electronics
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
USB om GSMs en kleine electronica op te laden
?" - }, - "render": { - "en": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:output}", - "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal {socket:USB-A:output}" - }, - "freeform": { - "key": "socket:USB-A:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:USB-A:output=5w", - "then": { - "en": "
USB to charge phones and small electronics
outputs at most 5w", - "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal 5w" - } - }, - { - "if": "socket:socket:USB-A:output=10w", - "then": { - "en": "
USB to charge phones and small electronics
outputs at most 10w", - "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal 10w" - } - } - ], - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "plugs-14", - "question": { - "en": "How much plugs of type
Bosch Active Connect with 3 pins and cable
are available here?", - "nl": "Hoeveel stekkers van type
Bosch Active Connect met 3 pinnen aan een kabel
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:bosch_3pin} plugs of type
Bosch Active Connect with 3 pins and cable
available here", - "nl": "Hier zijn {socket:bosch_3pin} stekkers van het type
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "freeform": { - "key": "socket:bosch_3pin", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:bosch_3pin~*", - "socket:bosch_3pin!=0" - ] - } - }, - { - "id": "voltage-14", - "question": { - "en": "What voltage do the plugs with
Bosch Active Connect with 3 pins and cable
offer?", - "nl": "Welke spanning levert de stekker van type
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "render": { - "en": "
Bosch Active Connect with 3 pins and cable
outputs {socket:bosch_3pin:voltage} volt", - "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
heeft een spanning van {socket:bosch_3pin:voltage} volt" - }, - "freeform": { - "key": "socket:bosch_3pin:voltage", - "type": "pfloat" - }, - "mappings": [], - "condition": { - "and": [ - "socket:bosch_3pin~*", - "socket:bosch_3pin!=0" - ] - } - }, - { - "id": "current-14", - "question": { - "en": "What current do the plugs with
Bosch Active Connect with 3 pins and cable
offer?", - "nl": "Welke stroom levert de stekker van type
Bosch Active Connect met 3 pinnen aan een kabel
?" - }, - "render": { - "en": "
Bosch Active Connect with 3 pins and cable
outputs at most {socket:bosch_3pin:current}A", - "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
levert een stroom van maximaal {socket:bosch_3pin:current}A" - }, - "freeform": { - "key": "socket:bosch_3pin:current", - "type": "pfloat" - }, - "mappings": [], - "condition": { - "and": [ - "socket:bosch_3pin~*", - "socket:bosch_3pin!=0" - ] - } - }, - { - "id": "power-output-14", - "question": { - "en": "What power output does a single plug of type
Bosch Active Connect with 3 pins and cable
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Bosch Active Connect met 3 pinnen aan een kabel
?" - }, - "render": { - "en": "
Bosch Active Connect with 3 pins and cable
outputs at most {socket:bosch_3pin:output}", - "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
levert een vermogen van maximaal {socket:bosch_3pin:output}" - }, - "freeform": { - "key": "socket:bosch_3pin:output", - "type": "pfloat" - }, - "mappings": [], - "condition": { - "and": [ - "socket:bosch_3pin~*", - "socket:bosch_3pin!=0" - ] - } - }, - { - "id": "plugs-15", - "question": { - "en": "How much plugs of type
Bosch Active Connect with 5 pins and cable
are available here?", - "nl": "Hoeveel stekkers van type
Bosch Active Connect met 5 pinnen aan een kabel
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:bosch_5pin} plugs of type
Bosch Active Connect with 5 pins and cable
available here", - "nl": "Hier zijn {socket:bosch_5pin} stekkers van het type
Bosch Active Connect met 5 pinnen aan een kabel
" - }, - "freeform": { - "key": "socket:bosch_5pin", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:bosch_5pin~*", - "socket:bosch_5pin!=0" - ] - } - }, - { - "id": "voltage-15", - "question": { - "en": "What voltage do the plugs with
Bosch Active Connect with 5 pins and cable
offer?", - "nl": "Welke spanning levert de stekker van type
Bosch Active Connect met 5 pinnen aan een kabel
" - }, - "render": { - "en": "
Bosch Active Connect with 5 pins and cable
outputs {socket:bosch_5pin:voltage} volt", - "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
heeft een spanning van {socket:bosch_5pin:voltage} volt" - }, - "freeform": { - "key": "socket:bosch_5pin:voltage", - "type": "pfloat" - }, - "mappings": [], - "condition": { - "and": [ - "socket:bosch_5pin~*", - "socket:bosch_5pin!=0" - ] - } - }, - { - "id": "current-15", - "question": { - "en": "What current do the plugs with
Bosch Active Connect with 5 pins and cable
offer?", - "nl": "Welke stroom levert de stekker van type
Bosch Active Connect met 5 pinnen aan een kabel
?" - }, - "render": { - "en": "
Bosch Active Connect with 5 pins and cable
outputs at most {socket:bosch_5pin:current}A", - "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
levert een stroom van maximaal {socket:bosch_5pin:current}A" - }, - "freeform": { - "key": "socket:bosch_5pin:current", - "type": "pfloat" - }, - "mappings": [], - "condition": { - "and": [ - "socket:bosch_5pin~*", - "socket:bosch_5pin!=0" - ] - } - }, - { - "id": "power-output-15", - "question": { - "en": "What power output does a single plug of type
Bosch Active Connect with 5 pins and cable
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Bosch Active Connect met 5 pinnen aan een kabel
?" - }, - "render": { - "en": "
Bosch Active Connect with 5 pins and cable
outputs at most {socket:bosch_5pin:output}", - "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
levert een vermogen van maximaal {socket:bosch_5pin:output}" - }, - "freeform": { - "key": "socket:bosch_5pin:output", - "type": "pfloat" - }, - "mappings": [], - "condition": { - "and": [ - "socket:bosch_5pin~*", - "socket:bosch_5pin!=0" - ] - } - }, - { - "id": "Authentication", - "question": { - "en": "What kind of authentication is available at the charging station?", - "it": "Quali sono gli orari di apertura di questa stazione di ricarica?", - "ja": "この充電ステーションはいつオープンしますか?", - "nb_NO": "Når åpnet denne ladestasjonen?", - "ru": "В какое время работает эта зарядная станция?", - "zh_Hant": "何時是充電站開放使用的時間?", - "de": "Welche Authentifizierung ist an der Ladestation möglich?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "authentication:membership_card=yes", - "ifnot": "authentication:membership_card=no", - "then": { - "en": "Authentication by a membership card", - "de": "Authentifizierung durch eine Mitgliedskarte" - } - }, - { - "if": "authentication:app=yes", - "ifnot": "authentication:app=no", - "then": { - "en": "Authentication by an app", - "de": "Authentifizierung durch eine App" - } - }, - { - "if": "authentication:phone_call=yes", - "ifnot": "authentication:phone_call=no", - "then": { - "en": "Authentication via phone call is available", - "de": "Authentifizierung per Anruf ist möglich" - } - }, - { - "if": "authentication:short_message=yes", - "ifnot": "authentication:short_message=no", - "then": { - "en": "Authentication via phone call is available", - "de": "Authentifizierung per Anruf ist möglich" - } - }, - { - "if": "authentication:nfc=yes", - "ifnot": "authentication:nfc=no", - "then": { - "en": "Authentication via NFC is available", - "de": "Authentifizierung über NFC ist möglich" - } - }, - { - "if": "authentication:money_card=yes", - "ifnot": "authentication:money_card=no", - "then": { - "en": "Authentication via Money Card is available", - "de": "Authentifizierung über Geldkarte ist möglich" - } - }, - { - "if": "authentication:debit_card=yes", - "ifnot": "authentication:debit_card=no", - "then": { - "en": "Authentication via debit card is available", - "de": "Authentifizierung per Debitkarte ist möglich" - } - }, - { - "if": "authentication:none=yes", - "ifnot": "authentication:none=no", - "then": { - "en": "Charging here is (also) possible without authentication", - "de": "Keine Authentifizierung erforderlich" - } - } - ] - }, - { - "id": "Auth phone", - "render": { - "en": "Authenticate by calling or SMS'ing to {authentication:phone_call:number}", - "it": "{network}", - "ja": "{network}", - "nb_NO": "{network}", - "ru": "{network}", - "zh_Hant": "{network}", - "de": "Authentifizierung durch Anruf oder SMS an {authentication:phone_call:number}" - }, - "question": { - "en": "What's the phone number for authentication call or SMS?", - "it": "A quale rete appartiene questa stazione di ricarica?", - "ja": "この充電ステーションの運営チェーンはどこですか?", - "ru": "К какой сети относится эта станция?", - "zh_Hant": "充電站所屬的網路是?", - "de": "Wie lautet die Telefonnummer für den Authentifizierungsanruf oder die SMS?" - }, - "freeform": { - "key": "authentication:phone_call:number", - "type": "phone" - }, - "condition": { - "or": [ - "authentication:phone_call=yes", - "authentication:short_message=yes" - ] - }, - "it": { - "0": { - "then": "Non appartiene a una rete" - } - }, - "ja": { - "0": { - "then": "大規模な運営チェーンの一部ではない" - } - }, - "ru": { - "0": { - "then": "Не является частью более крупной сети" - } - }, - "zh_Hant": { - "0": { - "then": "不屬於大型網路" - } - } - }, - { - "id": "OH", - "render": "{opening_hours_table(opening_hours)}", - "freeform": { - "key": "opening_hours", - "type": "opening_hours" - }, - "question": { - "en": "When is this charging station opened?", - "de": "Wann ist diese Ladestation geöffnet?" - }, - "mappings": [ - { - "if": "opening_hours=24/7", - "then": { - "en": "24/7 opened (including holidays)", - "de": "durchgehend geöffnet (auch an Feiertagen)" - } - } - ] - }, - { - "id": "fee/charge", - "question": { - "en": "How much does one have to pay to use this charging station?", - "nl": "Hoeveel kost het gebruik van dit oplaadpunt?" - }, - "freeform": { - "key": "charge", - "addExtraTags": [ - "fee=yes" - ] - }, - "render": { - "en": "Using this charging station costs {charge}", - "nl": "Dit oplaadpunt gebruiken kost {charge}" - }, - "mappings": [ - { - "if": { - "and": [ - "fee=no", - "charge=" - ] - }, - "then": { - "nl": "Gratis te gebruiken", - "en": "Free to use" - } - } - ] - }, - { - "id": "payment-options", - "builtin": "payment-options", - "override": { - "condition": { + { "or": [ - "fee=yes", - "charge~*" + "bicycle=yes", + "scooter=yes" ] - }, - "mappings+": [ - { - "if": "payment:app=yes", - "ifnot": "payment:app=no", - "then": { - "en": "Payment is done using a dedicated app", - "nl": "Betalen via een app van het netwerk" - } - }, - { - "if": "payment:membership_card=yes", - "ifnot": "payment:membership_card=no", - "then": { - "en": "Payment is done using a membership card", - "nl": "Betalen via een lidkaart van het netwerk" - } - } + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" ] - } - }, - { - "id": "maxstay", - "question": { - "en": "What is the maximum amount of time one is allowed to stay here?", - "nl": "Hoelang mag een voertuig hier blijven staan?" - }, - "freeform": { - "key": "maxstay" - }, - "render": { - "en": "One can stay at most {canonical(maxstay)}", - "nl": "De maximale parkeertijd hier is {canonical(maxstay)}" - }, - "mappings": [ - { - "if": "maxstay=unlimited", - "then": { - "en": "No timelimit on leaving your vehicle here", - "nl": "Geen maximum parkeertijd" - } - } + } ] + } }, { - "id": "Network", - "render": { - "en": "Part of the network {network}", - "de": "Teil des Netzwerks {network}" - }, - "question": { - "en": "Is this charging station part of a network?", - "de": "Ist diese Ladestation Teil eines Netzwerks?" - }, - "freeform": { - "key": "network" - }, - "mappings": [ - { - "if": "no:network=yes", - "then": { - "en": "Not part of a bigger network", - "de": "Nicht Teil eines größeren Netzwerks" - } - }, - { - "if": "network=none", - "then": { - "en": "Not part of a bigger network", - "de": "Nicht Teil eines größeren Netzwerks" - }, - "hideInAnswer": true - }, - { - "if": "network=AeroVironment", - "then": "AeroVironment" - }, - { - "if": "network=Blink", - "then": "Blink" - }, - { - "if": "network=eVgo", - "then": "eVgo" - } + "if": { + "and": [ + "socket:chademo~*", + "socket:chademo!=1" ] + }, + "then": { + "en": "
Chademo
", + "nl": "
Chademo
" + }, + "hideInAnswer": true }, { - "id": "Operator", - "question": { - "en": "Who is the operator of this charging station?", - "de": "Wer ist der Betreiber dieser Ladestation?" - }, - "render": { - "en": "This charging station is operated by {operator}", - "de": "Diese Ladestation wird betrieben von {operator}" - }, - "freeform": { - "key": "operator" - }, - "mappings": [ - { - "if": { - "and": [ - "network:={operator}" - ] - }, - "then": { - "en": "Actually, {operator} is the network", - "de": "Eigentlich ist {operator} das Netzwerk" - }, - "addExtraTags": [ - "operator=" - ], - "hideInAnswer": "operator=" - } + "if": "socket:type1_cable=1", + "ifnot": "socket:type1_cable=", + "then": { + "en": "
Type 1 with cable (J1772)
", + "nl": "
Type 1 met kabel (J1772)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } ] + } }, { - "id": "phone", - "question": { - "en": "What number can one call if there is a problem with this charging station?" - }, - "render": { - "en": "In case of problems, call {phone}" - }, - "freeform": { - "key": "phone", - "type": "phone" - } - }, - { - "id": "email", - "question": { - "en": "What is the email address of the operator?" - }, - "render": { - "en": "In case of problems, send an email to {email}" - }, - "freeform": { - "key": "email", - "type": "email" - } - }, - { - "id": "website", - "question": { - "en": "What is the website of the operator?" - }, - "render": { - "en": "More info on {website}" - }, - "freeform": { - "key": "website", - "type": "url" - } - }, - "level", - { - "id": "ref", - "question": { - "en": "What is the reference number of this charging station?" - }, - "render": { - "en": "Reference number is {ref}" - }, - "freeform": { - "key": "ref" - } - }, - { - "id": "Operational status", - "question": { - "en": "Is this charging point in use?", - "nl": "Is dit oplaadpunt operationeel?", - "de": "Ist dieser Ladepunkt in Betrieb?" - }, - "mappings": [ - { - "if": "operational_status=broken", - "then": { - "en": "This charging station is broken", - "nl": "Dit oplaadpunt is kapot", - "de": "Diese Ladestation ist kaputt" - } - }, - { - "if": { - "and": [ - "planned:amenity=charging_station", - "amenity=" - ] - }, - "then": { - "en": "A charging station is planned here", - "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden", - "de": "Hier ist eine Ladestation geplant" - } - }, - { - "if": { - "and": [ - "construction:amenity=charging_station", - "amenity=" - ] - }, - "then": { - "en": "A charging station is constructed here", - "nl": "Hier wordt op dit moment een oplaadpunt gebouwd", - "de": "Hier wird eine Ladestation gebaut" - } - }, - { - "if": { - "and": [ - "disused:amenity=charging_station", - "amenity=" - ] - }, - "then": { - "en": "This charging station has beed permanently disabled and is not in use anymore but is still visible", - "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig", - "de": "Diese Ladestation wurde dauerhaft deaktiviert und wird nicht mehr benutzt, ist aber noch sichtbar" - } - }, - { - "if": { - "and": [ - "amenity=charging_station", - "operational_status=" - ] - }, - "then": { - "en": "This charging station works", - "nl": "Dit oplaadpunt werkt", - "de": "Diese Ladestation funktioniert" - } - } + "if": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=1" ] + }, + "then": { + "en": "
Type 1 with cable (J1772)
", + "nl": "
Type 1 met kabel (J1772)
" + }, + "hideInAnswer": true }, { - "id": "Parking:fee", - "question": { - "en": "Does one have to pay a parking fee while charging?", - "de": "Muss man beim Laden eine Parkgebühr bezahlen?" - }, - "mappings": [ - { - "if": "parking:fee=no", - "then": { - "en": "No additional parking cost while charging", - "de": "Keine zusätzlichen Parkgebühren beim Laden" - } - }, - { - "if": "parking:fee=yes", - "then": { - "en": "An additional parking fee should be paid while charging", - "de": "Beim Laden ist eine zusätzliche Parkgebühr zu entrichten" - } - } + "if": "socket:type1=1", + "ifnot": "socket:type1=", + "then": { + "en": "
Type 1 without cable (J1772)
", + "nl": "
Type 1 zonder kabel (J1772)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } ] + } + }, + { + "if": { + "and": [ + "socket:type1~*", + "socket:type1!=1" + ] + }, + "then": { + "en": "
Type 1 without cable (J1772)
", + "nl": "
Type 1 zonder kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1_combo=1", + "ifnot": "socket:type1_combo=", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=1" + ] + }, + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_supercharger=1", + "ifnot": "socket:tesla_supercharger=", + "then": { + "en": "
Tesla Supercharger
", + "nl": "
Tesla Supercharger
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger
", + "nl": "
Tesla Supercharger
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2=1", + "ifnot": "socket:type2=", + "then": { + "en": "
Type 2 (mennekes)
", + "nl": "
Type 2 (mennekes)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type2~*", + "socket:type2!=1" + ] + }, + "then": { + "en": "
Type 2 (mennekes)
", + "nl": "
Type 2 (mennekes)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2_combo=1", + "ifnot": "socket:type2_combo=", + "then": { + "en": "
Type 2 CCS (mennekes)
", + "nl": "
Type 2 CCS (mennekes)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=1" + ] + }, + "then": { + "en": "
Type 2 CCS (mennekes)
", + "nl": "
Type 2 CCS (mennekes)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2_cable=1", + "ifnot": "socket:type2_cable=", + "then": { + "en": "
Type 2 with cable (mennekes)
", + "nl": "
Type 2 met kabel (J1772)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=1" + ] + }, + "then": { + "en": "
Type 2 with cable (mennekes)
", + "nl": "
Type 2 met kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_supercharger_ccs=1", + "ifnot": "socket:tesla_supercharger_ccs=", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_destination=1", + "ifnot": "socket:tesla_destination=", + "then": { + "en": "
Tesla Supercharger (destination)
", + "nl": "
Tesla Supercharger (destination)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + }, + { + "or": [ + "_country!=us" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger (destination)
", + "nl": "
Tesla Supercharger (destination)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_destination=1", + "ifnot": "socket:tesla_destination=", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + }, + { + "or": [ + "_country=us" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=1" + ] + }, + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:USB-A=1", + "ifnot": "socket:USB-A=", + "then": { + "en": "
USB to charge phones and small electronics
", + "nl": "
USB om GSMs en kleine electronica op te laden
" + } + }, + { + "if": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=1" + ] + }, + "then": { + "en": "
USB to charge phones and small electronics
", + "nl": "
USB om GSMs en kleine electronica op te laden
" + }, + "hideInAnswer": true + }, + { + "if": "socket:bosch_3pin=1", + "ifnot": "socket:bosch_3pin=", + "then": { + "en": "
Bosch Active Connect with 3 pins and cable
", + "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "bicycle=no" + ] + }, + { + "and": [ + { + "or": [ + "car=yes", + "motorcar=yes", + "hgv=yes", + "bus=yes" + ] + }, + "bicycle!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:bosch_3pin~*", + "socket:bosch_3pin!=1" + ] + }, + "then": { + "en": "
Bosch Active Connect with 3 pins and cable
", + "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
" + }, + "hideInAnswer": true + }, + { + "if": "socket:bosch_5pin=1", + "ifnot": "socket:bosch_5pin=", + "then": { + "en": "
Bosch Active Connect with 5 pins and cable
", + "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "bicycle=no" + ] + }, + { + "and": [ + { + "or": [ + "car=yes", + "motorcar=yes", + "hgv=yes", + "bus=yes" + ] + }, + "bicycle!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:bosch_5pin~*", + "socket:bosch_5pin!=1" + ] + }, + "then": { + "en": "
Bosch Active Connect with 5 pins and cable
", + "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
" + }, + "hideInAnswer": true } - ], - "icon": { + ] + }, + { + "id": "plugs-0", + "question": { + "en": "How much plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
are available here?", + "nl": "Hoeveel stekkers van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:schuko} plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
available here", + "nl": "Hier zijn {socket:schuko} stekkers van het type
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "freeform": { + "key": "socket:schuko", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "voltage-0", + "question": { + "en": "What voltage do the plugs with
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", + "nl": "Welke spanning levert de stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "render": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs {socket:schuko:voltage} volt", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft een spanning van {socket:schuko:voltage} volt" + }, + "freeform": { + "key": "socket:schuko:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:schuko:voltage=230 V", + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs 230 volt", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft een spanning van 230 volt" + } + } + ], + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "current-0", + "question": { + "en": "What current do the plugs with
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", + "nl": "Welke stroom levert de stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
?" + }, + "render": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most {socket:schuko:current}A", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een stroom van maximaal {socket:schuko:current}A" + }, + "freeform": { + "key": "socket:schuko:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:schuko:current=16 A", + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 16 A", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een stroom van maximaal 16 A" + } + } + ], + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "power-output-0", + "question": { + "en": "What power output does a single plug of type
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
?" + }, + "render": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most {socket:schuko:output}", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een vermogen van maximaal {socket:schuko:output}" + }, + "freeform": { + "key": "socket:schuko:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:schuko:output=3.6 kw", + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 3.6 kw", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een vermogen van maximaal 3.6 kw" + } + } + ], + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "plugs-1", + "question": { + "en": "How much plugs of type
European wall plug with ground pin (CEE7/4 type E)
are available here?", + "nl": "Hoeveel stekkers van type
Europese stekker met aardingspin (CEE7/4 type E)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:typee} plugs of type
European wall plug with ground pin (CEE7/4 type E)
available here", + "nl": "Hier zijn {socket:typee} stekkers van het type
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "freeform": { + "key": "socket:typee", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "voltage-1", + "question": { + "en": "What voltage do the plugs with
European wall plug with ground pin (CEE7/4 type E)
offer?", + "nl": "Welke spanning levert de stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "render": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs {socket:typee:voltage} volt", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
heeft een spanning van {socket:typee:voltage} volt" + }, + "freeform": { + "key": "socket:typee:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:typee:voltage=230 V", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs 230 volt", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
heeft een spanning van 230 volt" + } + } + ], + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "current-1", + "question": { + "en": "What current do the plugs with
European wall plug with ground pin (CEE7/4 type E)
offer?", + "nl": "Welke stroom levert de stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
?" + }, + "render": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most {socket:typee:current}A", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een stroom van maximaal {socket:typee:current}A" + }, + "freeform": { + "key": "socket:typee:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:typee:current=16 A", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 16 A", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een stroom van maximaal 16 A" + } + } + ], + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "power-output-1", + "question": { + "en": "What power output does a single plug of type
European wall plug with ground pin (CEE7/4 type E)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
?" + }, + "render": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most {socket:typee:output}", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal {socket:typee:output}" + }, + "freeform": { + "key": "socket:typee:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:typee:output=3 kw", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 3 kw", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal 3 kw" + } + }, + { + "if": "socket:socket:typee:output=22 kw", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 22 kw", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "plugs-2", + "question": { + "en": "How much plugs of type
Chademo
are available here?", + "nl": "Hoeveel stekkers van type
Chademo
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:chademo} plugs of type
Chademo
available here", + "nl": "Hier zijn {socket:chademo} stekkers van het type
Chademo
" + }, + "freeform": { + "key": "socket:chademo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "voltage-2", + "question": { + "en": "What voltage do the plugs with
Chademo
offer?", + "nl": "Welke spanning levert de stekker van type
Chademo
" + }, + "render": { + "en": "
Chademo
outputs {socket:chademo:voltage} volt", + "nl": "
Chademo
heeft een spanning van {socket:chademo:voltage} volt" + }, + "freeform": { + "key": "socket:chademo:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:chademo:voltage=500 V", + "then": { + "en": "
Chademo
outputs 500 volt", + "nl": "
Chademo
heeft een spanning van 500 volt" + } + } + ], + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "current-2", + "question": { + "en": "What current do the plugs with
Chademo
offer?", + "nl": "Welke stroom levert de stekker van type
Chademo
?" + }, + "render": { + "en": "
Chademo
outputs at most {socket:chademo:current}A", + "nl": "
Chademo
levert een stroom van maximaal {socket:chademo:current}A" + }, + "freeform": { + "key": "socket:chademo:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:chademo:current=120 A", + "then": { + "en": "
Chademo
outputs at most 120 A", + "nl": "
Chademo
levert een stroom van maximaal 120 A" + } + } + ], + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "power-output-2", + "question": { + "en": "What power output does a single plug of type
Chademo
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Chademo
?" + }, + "render": { + "en": "
Chademo
outputs at most {socket:chademo:output}", + "nl": "
Chademo
levert een vermogen van maximaal {socket:chademo:output}" + }, + "freeform": { + "key": "socket:chademo:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:chademo:output=50 kw", + "then": { + "en": "
Chademo
outputs at most 50 kw", + "nl": "
Chademo
levert een vermogen van maximaal 50 kw" + } + } + ], + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "plugs-3", + "question": { + "en": "How much plugs of type
Type 1 with cable (J1772)
are available here?", + "nl": "Hoeveel stekkers van type
Type 1 met kabel (J1772)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type1_cable} plugs of type
Type 1 with cable (J1772)
available here", + "nl": "Hier zijn {socket:type1_cable} stekkers van het type
Type 1 met kabel (J1772)
" + }, + "freeform": { + "key": "socket:type1_cable", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "voltage-3", + "question": { + "en": "What voltage do the plugs with
Type 1 with cable (J1772)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 1 met kabel (J1772)
" + }, + "render": { + "en": "
Type 1 with cable (J1772)
outputs {socket:type1_cable:voltage} volt", + "nl": "
Type 1 met kabel (J1772)
heeft een spanning van {socket:type1_cable:voltage} volt" + }, + "freeform": { + "key": "socket:type1_cable:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_cable:voltage=200 V", + "then": { + "en": "
Type 1 with cable (J1772)
outputs 200 volt", + "nl": "
Type 1 met kabel (J1772)
heeft een spanning van 200 volt" + } + }, + { + "if": "socket:socket:type1_cable:voltage=240 V", + "then": { + "en": "
Type 1 with cable (J1772)
outputs 240 volt", + "nl": "
Type 1 met kabel (J1772)
heeft een spanning van 240 volt" + } + } + ], + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "current-3", + "question": { + "en": "What current do the plugs with
Type 1 with cable (J1772)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 1 met kabel (J1772)
?" + }, + "render": { + "en": "
Type 1 with cable (J1772)
outputs at most {socket:type1_cable:current}A", + "nl": "
Type 1 met kabel (J1772)
levert een stroom van maximaal {socket:type1_cable:current}A" + }, + "freeform": { + "key": "socket:type1_cable:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_cable:current=32 A", + "then": { + "en": "
Type 1 with cable (J1772)
outputs at most 32 A", + "nl": "
Type 1 met kabel (J1772)
levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "power-output-3", + "question": { + "en": "What power output does a single plug of type
Type 1 with cable (J1772)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 1 met kabel (J1772)
?" + }, + "render": { + "en": "
Type 1 with cable (J1772)
outputs at most {socket:type1_cable:output}", + "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal {socket:type1_cable:output}" + }, + "freeform": { + "key": "socket:type1_cable:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_cable:output=3.7 kw", + "then": { + "en": "
Type 1 with cable (J1772)
outputs at most 3.7 kw", + "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal 3.7 kw" + } + }, + { + "if": "socket:socket:type1_cable:output=7 kw", + "then": { + "en": "
Type 1 with cable (J1772)
outputs at most 7 kw", + "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal 7 kw" + } + } + ], + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "plugs-4", + "question": { + "en": "How much plugs of type
Type 1 without cable (J1772)
are available here?", + "nl": "Hoeveel stekkers van type
Type 1 zonder kabel (J1772)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type1} plugs of type
Type 1 without cable (J1772)
available here", + "nl": "Hier zijn {socket:type1} stekkers van het type
Type 1 zonder kabel (J1772)
" + }, + "freeform": { + "key": "socket:type1", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "voltage-4", + "question": { + "en": "What voltage do the plugs with
Type 1 without cable (J1772)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 1 zonder kabel (J1772)
" + }, + "render": { + "en": "
Type 1 without cable (J1772)
outputs {socket:type1:voltage} volt", + "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van {socket:type1:voltage} volt" + }, + "freeform": { + "key": "socket:type1:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1:voltage=200 V", + "then": { + "en": "
Type 1 without cable (J1772)
outputs 200 volt", + "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van 200 volt" + } + }, + { + "if": "socket:socket:type1:voltage=240 V", + "then": { + "en": "
Type 1 without cable (J1772)
outputs 240 volt", + "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van 240 volt" + } + } + ], + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "current-4", + "question": { + "en": "What current do the plugs with
Type 1 without cable (J1772)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 1 zonder kabel (J1772)
?" + }, + "render": { + "en": "
Type 1 without cable (J1772)
outputs at most {socket:type1:current}A", + "nl": "
Type 1 zonder kabel (J1772)
levert een stroom van maximaal {socket:type1:current}A" + }, + "freeform": { + "key": "socket:type1:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1:current=32 A", + "then": { + "en": "
Type 1 without cable (J1772)
outputs at most 32 A", + "nl": "
Type 1 zonder kabel (J1772)
levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "power-output-4", + "question": { + "en": "What power output does a single plug of type
Type 1 without cable (J1772)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 1 zonder kabel (J1772)
?" + }, + "render": { + "en": "
Type 1 without cable (J1772)
outputs at most {socket:type1:output}", + "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal {socket:type1:output}" + }, + "freeform": { + "key": "socket:type1:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1:output=3.7 kw", + "then": { + "en": "
Type 1 without cable (J1772)
outputs at most 3.7 kw", + "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 3.7 kw" + } + }, + { + "if": "socket:socket:type1:output=6.6 kw", + "then": { + "en": "
Type 1 without cable (J1772)
outputs at most 6.6 kw", + "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 6.6 kw" + } + }, + { + "if": "socket:socket:type1:output=7 kw", + "then": { + "en": "
Type 1 without cable (J1772)
outputs at most 7 kw", + "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 7 kw" + } + }, + { + "if": "socket:socket:type1:output=7.2 kw", + "then": { + "en": "
Type 1 without cable (J1772)
outputs at most 7.2 kw", + "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 7.2 kw" + } + } + ], + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "plugs-5", + "question": { + "en": "How much plugs of type
Type 1 CCS (aka Type 1 Combo)
are available here?", + "nl": "Hoeveel stekkers van type
Type 1 CCS (ook gekend als Type 1 Combo)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type1_combo} plugs of type
Type 1 CCS (aka Type 1 Combo)
available here", + "nl": "Hier zijn {socket:type1_combo} stekkers van het type
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "freeform": { + "key": "socket:type1_combo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "voltage-5", + "question": { + "en": "What voltage do the plugs with
Type 1 CCS (aka Type 1 Combo)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "render": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs {socket:type1_combo:voltage} volt", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van {socket:type1_combo:voltage} volt" + }, + "freeform": { + "key": "socket:type1_combo:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_combo:voltage=400 V", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs 400 volt", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van 400 volt" + } + }, + { + "if": "socket:socket:type1_combo:voltage=1000 V", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs 1000 volt", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van 1000 volt" + } + } + ], + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "current-5", + "question": { + "en": "What current do the plugs with
Type 1 CCS (aka Type 1 Combo)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
?" + }, + "render": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most {socket:type1_combo:current}A", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal {socket:type1_combo:current}A" + }, + "freeform": { + "key": "socket:type1_combo:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_combo:current=50 A", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 A", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal 50 A" + } + }, + { + "if": "socket:socket:type1_combo:current=125 A", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 125 A", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal 125 A" + } + } + ], + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "power-output-5", + "question": { + "en": "What power output does a single plug of type
Type 1 CCS (aka Type 1 Combo)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
?" + }, + "render": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most {socket:type1_combo:output}", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal {socket:type1_combo:output}" + }, + "freeform": { + "key": "socket:type1_combo:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_combo:output=50 kw", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 kw", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 50 kw" + } + }, + { + "if": "socket:socket:type1_combo:output=62.5 kw", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 62.5 kw", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 62.5 kw" + } + }, + { + "if": "socket:socket:type1_combo:output=150 kw", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 150 kw", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 150 kw" + } + }, + { + "if": "socket:socket:type1_combo:output=350 kw", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 350 kw", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 350 kw" + } + } + ], + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "plugs-6", + "question": { + "en": "How much plugs of type
Tesla Supercharger
are available here?", + "nl": "Hoeveel stekkers van type
Tesla Supercharger
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:tesla_supercharger} plugs of type
Tesla Supercharger
available here", + "nl": "Hier zijn {socket:tesla_supercharger} stekkers van het type
Tesla Supercharger
" + }, + "freeform": { + "key": "socket:tesla_supercharger", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "voltage-6", + "question": { + "en": "What voltage do the plugs with
Tesla Supercharger
offer?", + "nl": "Welke spanning levert de stekker van type
Tesla Supercharger
" + }, + "render": { + "en": "
Tesla Supercharger
outputs {socket:tesla_supercharger:voltage} volt", + "nl": "
Tesla Supercharger
heeft een spanning van {socket:tesla_supercharger:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_supercharger:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger:voltage=480 V", + "then": { + "en": "
Tesla Supercharger
outputs 480 volt", + "nl": "
Tesla Supercharger
heeft een spanning van 480 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "current-6", + "question": { + "en": "What current do the plugs with
Tesla Supercharger
offer?", + "nl": "Welke stroom levert de stekker van type
Tesla Supercharger
?" + }, + "render": { + "en": "
Tesla Supercharger
outputs at most {socket:tesla_supercharger:current}A", + "nl": "
Tesla Supercharger
levert een stroom van maximaal {socket:tesla_supercharger:current}A" + }, + "freeform": { + "key": "socket:tesla_supercharger:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger:current=125 A", + "then": { + "en": "
Tesla Supercharger
outputs at most 125 A", + "nl": "
Tesla Supercharger
levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:tesla_supercharger:current=350 A", + "then": { + "en": "
Tesla Supercharger
outputs at most 350 A", + "nl": "
Tesla Supercharger
levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "power-output-6", + "question": { + "en": "What power output does a single plug of type
Tesla Supercharger
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger
?" + }, + "render": { + "en": "
Tesla Supercharger
outputs at most {socket:tesla_supercharger:output}", + "nl": "
Tesla Supercharger
levert een vermogen van maximaal {socket:tesla_supercharger:output}" + }, + "freeform": { + "key": "socket:tesla_supercharger:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger:output=120 kw", + "then": { + "en": "
Tesla Supercharger
outputs at most 120 kw", + "nl": "
Tesla Supercharger
levert een vermogen van maximaal 120 kw" + } + }, + { + "if": "socket:socket:tesla_supercharger:output=150 kw", + "then": { + "en": "
Tesla Supercharger
outputs at most 150 kw", + "nl": "
Tesla Supercharger
levert een vermogen van maximaal 150 kw" + } + }, + { + "if": "socket:socket:tesla_supercharger:output=250 kw", + "then": { + "en": "
Tesla Supercharger
outputs at most 250 kw", + "nl": "
Tesla Supercharger
levert een vermogen van maximaal 250 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "plugs-7", + "question": { + "en": "How much plugs of type
Type 2 (mennekes)
are available here?", + "nl": "Hoeveel stekkers van type
Type 2 (mennekes)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type2} plugs of type
Type 2 (mennekes)
available here", + "nl": "Hier zijn {socket:type2} stekkers van het type
Type 2 (mennekes)
" + }, + "freeform": { + "key": "socket:type2", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "voltage-7", + "question": { + "en": "What voltage do the plugs with
Type 2 (mennekes)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 2 (mennekes)
" + }, + "render": { + "en": "
Type 2 (mennekes)
outputs {socket:type2:voltage} volt", + "nl": "
Type 2 (mennekes)
heeft een spanning van {socket:type2:voltage} volt" + }, + "freeform": { + "key": "socket:type2:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2:voltage=230 V", + "then": { + "en": "
Type 2 (mennekes)
outputs 230 volt", + "nl": "
Type 2 (mennekes)
heeft een spanning van 230 volt" + } + }, + { + "if": "socket:socket:type2:voltage=400 V", + "then": { + "en": "
Type 2 (mennekes)
outputs 400 volt", + "nl": "
Type 2 (mennekes)
heeft een spanning van 400 volt" + } + } + ], + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "current-7", + "question": { + "en": "What current do the plugs with
Type 2 (mennekes)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 2 (mennekes)
?" + }, + "render": { + "en": "
Type 2 (mennekes)
outputs at most {socket:type2:current}A", + "nl": "
Type 2 (mennekes)
levert een stroom van maximaal {socket:type2:current}A" + }, + "freeform": { + "key": "socket:type2:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2:current=16 A", + "then": { + "en": "
Type 2 (mennekes)
outputs at most 16 A", + "nl": "
Type 2 (mennekes)
levert een stroom van maximaal 16 A" + } + }, + { + "if": "socket:socket:type2:current=32 A", + "then": { + "en": "
Type 2 (mennekes)
outputs at most 32 A", + "nl": "
Type 2 (mennekes)
levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "power-output-7", + "question": { + "en": "What power output does a single plug of type
Type 2 (mennekes)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 2 (mennekes)
?" + }, + "render": { + "en": "
Type 2 (mennekes)
outputs at most {socket:type2:output}", + "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal {socket:type2:output}" + }, + "freeform": { + "key": "socket:type2:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2:output=11 kw", + "then": { + "en": "
Type 2 (mennekes)
outputs at most 11 kw", + "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal 11 kw" + } + }, + { + "if": "socket:socket:type2:output=22 kw", + "then": { + "en": "
Type 2 (mennekes)
outputs at most 22 kw", + "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "plugs-8", + "question": { + "en": "How much plugs of type
Type 2 CCS (mennekes)
are available here?", + "nl": "Hoeveel stekkers van type
Type 2 CCS (mennekes)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type2_combo} plugs of type
Type 2 CCS (mennekes)
available here", + "nl": "Hier zijn {socket:type2_combo} stekkers van het type
Type 2 CCS (mennekes)
" + }, + "freeform": { + "key": "socket:type2_combo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "voltage-8", + "question": { + "en": "What voltage do the plugs with
Type 2 CCS (mennekes)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 2 CCS (mennekes)
" + }, + "render": { + "en": "
Type 2 CCS (mennekes)
outputs {socket:type2_combo:voltage} volt", + "nl": "
Type 2 CCS (mennekes)
heeft een spanning van {socket:type2_combo:voltage} volt" + }, + "freeform": { + "key": "socket:type2_combo:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_combo:voltage=500 V", + "then": { + "en": "
Type 2 CCS (mennekes)
outputs 500 volt", + "nl": "
Type 2 CCS (mennekes)
heeft een spanning van 500 volt" + } + }, + { + "if": "socket:socket:type2_combo:voltage=920 V", + "then": { + "en": "
Type 2 CCS (mennekes)
outputs 920 volt", + "nl": "
Type 2 CCS (mennekes)
heeft een spanning van 920 volt" + } + } + ], + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "current-8", + "question": { + "en": "What current do the plugs with
Type 2 CCS (mennekes)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 2 CCS (mennekes)
?" + }, + "render": { + "en": "
Type 2 CCS (mennekes)
outputs at most {socket:type2_combo:current}A", + "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal {socket:type2_combo:current}A" + }, + "freeform": { + "key": "socket:type2_combo:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_combo:current=125 A", + "then": { + "en": "
Type 2 CCS (mennekes)
outputs at most 125 A", + "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:type2_combo:current=350 A", + "then": { + "en": "
Type 2 CCS (mennekes)
outputs at most 350 A", + "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "power-output-8", + "question": { + "en": "What power output does a single plug of type
Type 2 CCS (mennekes)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 2 CCS (mennekes)
?" + }, + "render": { + "en": "
Type 2 CCS (mennekes)
outputs at most {socket:type2_combo:output}", + "nl": "
Type 2 CCS (mennekes)
levert een vermogen van maximaal {socket:type2_combo:output}" + }, + "freeform": { + "key": "socket:type2_combo:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_combo:output=50 kw", + "then": { + "en": "
Type 2 CCS (mennekes)
outputs at most 50 kw", + "nl": "
Type 2 CCS (mennekes)
levert een vermogen van maximaal 50 kw" + } + } + ], + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "plugs-9", + "question": { + "en": "How much plugs of type
Type 2 with cable (mennekes)
are available here?", + "nl": "Hoeveel stekkers van type
Type 2 met kabel (J1772)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type2_cable} plugs of type
Type 2 with cable (mennekes)
available here", + "nl": "Hier zijn {socket:type2_cable} stekkers van het type
Type 2 met kabel (J1772)
" + }, + "freeform": { + "key": "socket:type2_cable", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "voltage-9", + "question": { + "en": "What voltage do the plugs with
Type 2 with cable (mennekes)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 2 met kabel (J1772)
" + }, + "render": { + "en": "
Type 2 with cable (mennekes)
outputs {socket:type2_cable:voltage} volt", + "nl": "
Type 2 met kabel (J1772)
heeft een spanning van {socket:type2_cable:voltage} volt" + }, + "freeform": { + "key": "socket:type2_cable:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_cable:voltage=230 V", + "then": { + "en": "
Type 2 with cable (mennekes)
outputs 230 volt", + "nl": "
Type 2 met kabel (J1772)
heeft een spanning van 230 volt" + } + }, + { + "if": "socket:socket:type2_cable:voltage=400 V", + "then": { + "en": "
Type 2 with cable (mennekes)
outputs 400 volt", + "nl": "
Type 2 met kabel (J1772)
heeft een spanning van 400 volt" + } + } + ], + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "current-9", + "question": { + "en": "What current do the plugs with
Type 2 with cable (mennekes)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 2 met kabel (J1772)
?" + }, + "render": { + "en": "
Type 2 with cable (mennekes)
outputs at most {socket:type2_cable:current}A", + "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal {socket:type2_cable:current}A" + }, + "freeform": { + "key": "socket:type2_cable:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_cable:current=16 A", + "then": { + "en": "
Type 2 with cable (mennekes)
outputs at most 16 A", + "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal 16 A" + } + }, + { + "if": "socket:socket:type2_cable:current=32 A", + "then": { + "en": "
Type 2 with cable (mennekes)
outputs at most 32 A", + "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "power-output-9", + "question": { + "en": "What power output does a single plug of type
Type 2 with cable (mennekes)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 2 met kabel (J1772)
?" + }, + "render": { + "en": "
Type 2 with cable (mennekes)
outputs at most {socket:type2_cable:output}", + "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal {socket:type2_cable:output}" + }, + "freeform": { + "key": "socket:type2_cable:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_cable:output=11 kw", + "then": { + "en": "
Type 2 with cable (mennekes)
outputs at most 11 kw", + "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal 11 kw" + } + }, + { + "if": "socket:socket:type2_cable:output=22 kw", + "then": { + "en": "
Type 2 with cable (mennekes)
outputs at most 22 kw", + "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "plugs-10", + "question": { + "en": "How much plugs of type
Tesla Supercharger CCS (a branded type2_css)
are available here?", + "nl": "Hoeveel stekkers van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:tesla_supercharger_ccs} plugs of type
Tesla Supercharger CCS (a branded type2_css)
available here", + "nl": "Hier zijn {socket:tesla_supercharger_ccs} stekkers van het type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "voltage-10", + "question": { + "en": "What voltage do the plugs with
Tesla Supercharger CCS (a branded type2_css)
offer?", + "nl": "Welke spanning levert de stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "render": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs {socket:tesla_supercharger_ccs:voltage} volt", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger_ccs:voltage=500 V", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs 500 volt", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van 500 volt" + } + }, + { + "if": "socket:socket:tesla_supercharger_ccs:voltage=920 V", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs 920 volt", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van 920 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "current-10", + "question": { + "en": "What current do the plugs with
Tesla Supercharger CCS (a branded type2_css)
offer?", + "nl": "Welke stroom levert de stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
?" + }, + "render": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most {socket:tesla_supercharger_ccs:current}A", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger_ccs:current=125 A", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 125 A", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:tesla_supercharger_ccs:current=350 A", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 350 A", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "power-output-10", + "question": { + "en": "What power output does a single plug of type
Tesla Supercharger CCS (a branded type2_css)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
?" + }, + "render": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most {socket:tesla_supercharger_ccs:output}", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger_ccs:output=50 kw", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 50 kw", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een vermogen van maximaal 50 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "plugs-11", + "question": { + "en": "How much plugs of type
Tesla Supercharger (destination)
are available here?", + "nl": "Hoeveel stekkers van type
Tesla Supercharger (destination)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:tesla_destination} plugs of type
Tesla Supercharger (destination)
available here", + "nl": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla Supercharger (destination)
" + }, + "freeform": { + "key": "socket:tesla_destination", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "voltage-11", + "question": { + "en": "What voltage do the plugs with
Tesla Supercharger (destination)
offer?", + "nl": "Welke spanning levert de stekker van type
Tesla Supercharger (destination)
" + }, + "render": { + "en": "
Tesla Supercharger (destination)
outputs {socket:tesla_destination:voltage} volt", + "nl": "
Tesla Supercharger (destination)
heeft een spanning van {socket:tesla_destination:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_destination:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:voltage=480 V", + "then": { + "en": "
Tesla Supercharger (destination)
outputs 480 volt", + "nl": "
Tesla Supercharger (destination)
heeft een spanning van 480 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "current-11", + "question": { + "en": "What current do the plugs with
Tesla Supercharger (destination)
offer?", + "nl": "Welke stroom levert de stekker van type
Tesla Supercharger (destination)
?" + }, + "render": { + "en": "
Tesla Supercharger (destination)
outputs at most {socket:tesla_destination:current}A", + "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal {socket:tesla_destination:current}A" + }, + "freeform": { + "key": "socket:tesla_destination:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:current=125 A", + "then": { + "en": "
Tesla Supercharger (destination)
outputs at most 125 A", + "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:tesla_destination:current=350 A", + "then": { + "en": "
Tesla Supercharger (destination)
outputs at most 350 A", + "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "power-output-11", + "question": { + "en": "What power output does a single plug of type
Tesla Supercharger (destination)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger (destination)
?" + }, + "render": { + "en": "
Tesla Supercharger (destination)
outputs at most {socket:tesla_destination:output}", + "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal {socket:tesla_destination:output}" + }, + "freeform": { + "key": "socket:tesla_destination:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:output=120 kw", + "then": { + "en": "
Tesla Supercharger (destination)
outputs at most 120 kw", + "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 120 kw" + } + }, + { + "if": "socket:socket:tesla_destination:output=150 kw", + "then": { + "en": "
Tesla Supercharger (destination)
outputs at most 150 kw", + "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 150 kw" + } + }, + { + "if": "socket:socket:tesla_destination:output=250 kw", + "then": { + "en": "
Tesla Supercharger (destination)
outputs at most 250 kw", + "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 250 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "plugs-12", + "question": { + "en": "How much plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
are available here?", + "nl": "Hoeveel stekkers van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:tesla_destination} plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
available here", + "nl": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "freeform": { + "key": "socket:tesla_destination", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "voltage-12", + "question": { + "en": "What voltage do the plugs with
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", + "nl": "Welke spanning levert de stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "render": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs {socket:tesla_destination:voltage} volt", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van {socket:tesla_destination:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_destination:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:voltage=230 V", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 230 volt", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van 230 volt" + } + }, + { + "if": "socket:socket:tesla_destination:voltage=400 V", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 400 volt", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van 400 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "current-12", + "question": { + "en": "What current do the plugs with
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", + "nl": "Welke stroom levert de stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
?" + }, + "render": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most {socket:tesla_destination:current}A", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal {socket:tesla_destination:current}A" + }, + "freeform": { + "key": "socket:tesla_destination:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:current=16 A", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 16 A", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal 16 A" + } + }, + { + "if": "socket:socket:tesla_destination:current=32 A", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 32 A", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "power-output-12", + "question": { + "en": "What power output does a single plug of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
?" + }, + "render": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most {socket:tesla_destination:output}", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal {socket:tesla_destination:output}" + }, + "freeform": { + "key": "socket:tesla_destination:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:output=11 kw", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 11 kw", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal 11 kw" + } + }, + { + "if": "socket:socket:tesla_destination:output=22 kw", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 22 kw", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "plugs-13", + "question": { + "en": "How much plugs of type
USB to charge phones and small electronics
are available here?", + "nl": "Hoeveel stekkers van type
USB om GSMs en kleine electronica op te laden
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:USB-A} plugs of type
USB to charge phones and small electronics
available here", + "nl": "Hier zijn {socket:USB-A} stekkers van het type
USB om GSMs en kleine electronica op te laden
" + }, + "freeform": { + "key": "socket:USB-A", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "voltage-13", + "question": { + "en": "What voltage do the plugs with
USB to charge phones and small electronics
offer?", + "nl": "Welke spanning levert de stekker van type
USB om GSMs en kleine electronica op te laden
" + }, + "render": { + "en": "
USB to charge phones and small electronics
outputs {socket:USB-A:voltage} volt", + "nl": "
USB om GSMs en kleine electronica op te laden
heeft een spanning van {socket:USB-A:voltage} volt" + }, + "freeform": { + "key": "socket:USB-A:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:voltage=5 V", + "then": { + "en": "
USB to charge phones and small electronics
outputs 5 volt", + "nl": "
USB om GSMs en kleine electronica op te laden
heeft een spanning van 5 volt" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "current-13", + "question": { + "en": "What current do the plugs with
USB to charge phones and small electronics
offer?", + "nl": "Welke stroom levert de stekker van type
USB om GSMs en kleine electronica op te laden
?" + }, + "render": { + "en": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:current}A", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal {socket:USB-A:current}A" + }, + "freeform": { + "key": "socket:USB-A:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:current=1 A", + "then": { + "en": "
USB to charge phones and small electronics
outputs at most 1 A", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal 1 A" + } + }, + { + "if": "socket:socket:USB-A:current=2 A", + "then": { + "en": "
USB to charge phones and small electronics
outputs at most 2 A", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal 2 A" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "power-output-13", + "question": { + "en": "What power output does a single plug of type
USB to charge phones and small electronics
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
USB om GSMs en kleine electronica op te laden
?" + }, + "render": { + "en": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:output}", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal {socket:USB-A:output}" + }, + "freeform": { + "key": "socket:USB-A:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:output=5w", + "then": { + "en": "
USB to charge phones and small electronics
outputs at most 5w", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal 5w" + } + }, + { + "if": "socket:socket:USB-A:output=10w", + "then": { + "en": "
USB to charge phones and small electronics
outputs at most 10w", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal 10w" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "plugs-14", + "question": { + "en": "How much plugs of type
Bosch Active Connect with 3 pins and cable
are available here?", + "nl": "Hoeveel stekkers van type
Bosch Active Connect met 3 pinnen aan een kabel
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:bosch_3pin} plugs of type
Bosch Active Connect with 3 pins and cable
available here", + "nl": "Hier zijn {socket:bosch_3pin} stekkers van het type
Bosch Active Connect met 3 pinnen aan een kabel
" + }, + "freeform": { + "key": "socket:bosch_3pin", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:bosch_3pin~*", + "socket:bosch_3pin!=0" + ] + } + }, + { + "id": "voltage-14", + "question": { + "en": "What voltage do the plugs with
Bosch Active Connect with 3 pins and cable
offer?", + "nl": "Welke spanning levert de stekker van type
Bosch Active Connect met 3 pinnen aan een kabel
" + }, + "render": { + "en": "
Bosch Active Connect with 3 pins and cable
outputs {socket:bosch_3pin:voltage} volt", + "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
heeft een spanning van {socket:bosch_3pin:voltage} volt" + }, + "freeform": { + "key": "socket:bosch_3pin:voltage", + "type": "pfloat" + }, + "mappings": [], + "condition": { + "and": [ + "socket:bosch_3pin~*", + "socket:bosch_3pin!=0" + ] + } + }, + { + "id": "current-14", + "question": { + "en": "What current do the plugs with
Bosch Active Connect with 3 pins and cable
offer?", + "nl": "Welke stroom levert de stekker van type
Bosch Active Connect met 3 pinnen aan een kabel
?" + }, + "render": { + "en": "
Bosch Active Connect with 3 pins and cable
outputs at most {socket:bosch_3pin:current}A", + "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
levert een stroom van maximaal {socket:bosch_3pin:current}A" + }, + "freeform": { + "key": "socket:bosch_3pin:current", + "type": "pfloat" + }, + "mappings": [], + "condition": { + "and": [ + "socket:bosch_3pin~*", + "socket:bosch_3pin!=0" + ] + } + }, + { + "id": "power-output-14", + "question": { + "en": "What power output does a single plug of type
Bosch Active Connect with 3 pins and cable
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Bosch Active Connect met 3 pinnen aan een kabel
?" + }, + "render": { + "en": "
Bosch Active Connect with 3 pins and cable
outputs at most {socket:bosch_3pin:output}", + "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
levert een vermogen van maximaal {socket:bosch_3pin:output}" + }, + "freeform": { + "key": "socket:bosch_3pin:output", + "type": "pfloat" + }, + "mappings": [], + "condition": { + "and": [ + "socket:bosch_3pin~*", + "socket:bosch_3pin!=0" + ] + } + }, + { + "id": "plugs-15", + "question": { + "en": "How much plugs of type
Bosch Active Connect with 5 pins and cable
are available here?", + "nl": "Hoeveel stekkers van type
Bosch Active Connect met 5 pinnen aan een kabel
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:bosch_5pin} plugs of type
Bosch Active Connect with 5 pins and cable
available here", + "nl": "Hier zijn {socket:bosch_5pin} stekkers van het type
Bosch Active Connect met 5 pinnen aan een kabel
" + }, + "freeform": { + "key": "socket:bosch_5pin", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:bosch_5pin~*", + "socket:bosch_5pin!=0" + ] + } + }, + { + "id": "voltage-15", + "question": { + "en": "What voltage do the plugs with
Bosch Active Connect with 5 pins and cable
offer?", + "nl": "Welke spanning levert de stekker van type
Bosch Active Connect met 5 pinnen aan een kabel
" + }, + "render": { + "en": "
Bosch Active Connect with 5 pins and cable
outputs {socket:bosch_5pin:voltage} volt", + "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
heeft een spanning van {socket:bosch_5pin:voltage} volt" + }, + "freeform": { + "key": "socket:bosch_5pin:voltage", + "type": "pfloat" + }, + "mappings": [], + "condition": { + "and": [ + "socket:bosch_5pin~*", + "socket:bosch_5pin!=0" + ] + } + }, + { + "id": "current-15", + "question": { + "en": "What current do the plugs with
Bosch Active Connect with 5 pins and cable
offer?", + "nl": "Welke stroom levert de stekker van type
Bosch Active Connect met 5 pinnen aan een kabel
?" + }, + "render": { + "en": "
Bosch Active Connect with 5 pins and cable
outputs at most {socket:bosch_5pin:current}A", + "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
levert een stroom van maximaal {socket:bosch_5pin:current}A" + }, + "freeform": { + "key": "socket:bosch_5pin:current", + "type": "pfloat" + }, + "mappings": [], + "condition": { + "and": [ + "socket:bosch_5pin~*", + "socket:bosch_5pin!=0" + ] + } + }, + { + "id": "power-output-15", + "question": { + "en": "What power output does a single plug of type
Bosch Active Connect with 5 pins and cable
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Bosch Active Connect met 5 pinnen aan een kabel
?" + }, + "render": { + "en": "
Bosch Active Connect with 5 pins and cable
outputs at most {socket:bosch_5pin:output}", + "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
levert een vermogen van maximaal {socket:bosch_5pin:output}" + }, + "freeform": { + "key": "socket:bosch_5pin:output", + "type": "pfloat" + }, + "mappings": [], + "condition": { + "and": [ + "socket:bosch_5pin~*", + "socket:bosch_5pin!=0" + ] + } + }, + { + "id": "Authentication", + "question": { + "en": "What kind of authentication is available at the charging station?", + "it": "Quali sono gli orari di apertura di questa stazione di ricarica?", + "ja": "この充電ステーションはいつオープンしますか?", + "nb_NO": "Når åpnet denne ladestasjonen?", + "ru": "В какое время работает эта зарядная станция?", + "zh_Hant": "何時是充電站開放使用的時間?" + }, + "multiAnswer": true, + "mappings": [ + { + "if": "authentication:membership_card=yes", + "ifnot": "authentication:membership_card=no", + "then": { + "en": "Authentication by a membership card" + } + }, + { + "if": "authentication:app=yes", + "ifnot": "authentication:app=no", + "then": { + "en": "Authentication by an app" + } + }, + { + "if": "authentication:phone_call=yes", + "ifnot": "authentication:phone_call=no", + "then": { + "en": "Authentication via phone call is available" + } + }, + { + "if": "authentication:short_message=yes", + "ifnot": "authentication:short_message=no", + "then": { + "en": "Authentication via phone call is available" + } + }, + { + "if": "authentication:nfc=yes", + "ifnot": "authentication:nfc=no", + "then": { + "en": "Authentication via NFC is available" + } + }, + { + "if": "authentication:money_card=yes", + "ifnot": "authentication:money_card=no", + "then": { + "en": "Authentication via Money Card is available" + } + }, + { + "if": "authentication:debit_card=yes", + "ifnot": "authentication:debit_card=no", + "then": { + "en": "Authentication via debit card is available" + } + }, + { + "if": "authentication:none=yes", + "ifnot": "authentication:none=no", + "then": { + "en": "Charging here is (also) possible without authentication" + } + } + ] + }, + { + "id": "Auth phone", + "render": { + "en": "Authenticate by calling or SMS'ing to {authentication:phone_call:number}", + "it": "{network}", + "ja": "{network}", + "nb_NO": "{network}", + "ru": "{network}", + "zh_Hant": "{network}" + }, + "question": { + "en": "What's the phone number for authentication call or SMS?", + "it": "A quale rete appartiene questa stazione di ricarica?", + "ja": "この充電ステーションの運営チェーンはどこですか?", + "ru": "К какой сети относится эта станция?", + "zh_Hant": "充電站所屬的網路是?" + }, + "freeform": { + "key": "authentication:phone_call:number", + "type": "phone" + }, + "condition": { + "or": [ + "authentication:phone_call=yes", + "authentication:short_message=yes" + ] + }, + "it": { + "0": { + "then": "Non appartiene a una rete" + } + }, + "ja": { + "0": { + "then": "大規模な運営チェーンの一部ではない" + } + }, + "ru": { + "0": { + "then": "Не является частью более крупной сети" + } + }, + "zh_Hant": { + "0": { + "then": "不屬於大型網路" + } + } + }, + { + "id": "OH", + "render": "{opening_hours_table(opening_hours)}", + "freeform": { + "key": "opening_hours", + "type": "opening_hours" + }, + "question": { + "en": "When is this charging station opened?" + }, + "mappings": [ + { + "if": "opening_hours=24/7", + "then": { + "en": "24/7 opened (including holidays)" + } + } + ] + }, + { + "id": "fee/charge", + "question": { + "en": "How much does one have to pay to use this charging station?", + "nl": "Hoeveel kost het gebruik van dit oplaadpunt?" + }, + "freeform": { + "key": "charge", + "addExtraTags": [ + "fee=yes" + ] + }, + "render": { + "en": "Using this charging station costs {charge}", + "nl": "Dit oplaadpunt gebruiken kost {charge}" + }, + "mappings": [ + { + "if": { + "and": [ + "fee=no", + "charge=" + ] + }, + "then": { + "nl": "Gratis te gebruiken", + "en": "Free to use" + } + } + ] + }, + { + "id": "payment-options", + "builtin": "payment-options", + "override": { + "condition": { + "or": [ + "fee=yes", + "charge~*" + ] + }, + "mappings+": [ + { + "if": "payment:app=yes", + "ifnot": "payment:app=no", + "then": { + "en": "Payment is done using a dedicated app", + "nl": "Betalen via een app van het netwerk" + } + }, + { + "if": "payment:membership_card=yes", + "ifnot": "payment:membership_card=no", + "then": { + "en": "Payment is done using a membership card", + "nl": "Betalen via een lidkaart van het netwerk" + } + } + ] + } + }, + { + "id": "maxstay", + "question": { + "en": "What is the maximum amount of time one is allowed to stay here?", + "nl": "Hoelang mag een voertuig hier blijven staan?" + }, + "freeform": { + "key": "maxstay" + }, + "render": { + "en": "One can stay at most {canonical(maxstay)}", + "nl": "De maximale parkeertijd hier is {canonical(maxstay)}" + }, + "mappings": [ + { + "if": "maxstay=unlimited", + "then": { + "en": "No timelimit on leaving your vehicle here", + "nl": "Geen maximum parkeertijd" + } + } + ] + }, + { + "id": "Network", + "render": { + "en": "Part of the network {network}" + }, + "question": { + "en": "Is this charging station part of a network?" + }, + "freeform": { + "key": "network" + }, + "mappings": [ + { + "if": "no:network=yes", + "then": { + "en": "Not part of a bigger network" + } + }, + { + "if": "network=none", + "then": { + "en": "Not part of a bigger network" + }, + "hideInAnswer": true + }, + { + "if": "network=AeroVironment", + "then": "AeroVironment" + }, + { + "if": "network=Blink", + "then": "Blink" + }, + { + "if": "network=eVgo", + "then": "eVgo" + } + ] + }, + { + "id": "Operator", + "question": { + "en": "Who is the operator of this charging station?" + }, + "render": { + "en": "This charging station is operated by {operator}" + }, + "freeform": { + "key": "operator" + }, + "mappings": [ + { + "if": { + "and": [ + "network:={operator}" + ] + }, + "then": { + "en": "Actually, {operator} is the network" + }, + "addExtraTags": [ + "operator=" + ], + "hideInAnswer": "operator=" + } + ] + }, + { + "id": "phone", + "question": { + "en": "What number can one call if there is a problem with this charging station?" + }, + "render": { + "en": "In case of problems, call {phone}" + }, + "freeform": { + "key": "phone", + "type": "phone" + } + }, + { + "id": "email", + "question": { + "en": "What is the email address of the operator?" + }, + "render": { + "en": "In case of problems, send an email to {email}" + }, + "freeform": { + "key": "email", + "type": "email" + } + }, + { + "id": "website", + "question": { + "en": "What is the website of the operator?" + }, + "render": { + "en": "More info on {website}" + }, + "freeform": { + "key": "website", + "type": "url" + } + }, + "level", + { + "id": "ref", + "question": { + "en": "What is the reference number of this charging station?" + }, + "render": { + "en": "Reference number is {ref}" + }, + "freeform": { + "key": "ref" + } + }, + { + "id": "Operational status", + "question": { + "en": "Is this charging point in use?", + "nl": "Is dit oplaadpunt operationeel?" + }, + "mappings": [ + { + "if": "operational_status=broken", + "then": { + "en": "This charging station is broken", + "nl": "Dit oplaadpunt is kapot" + } + }, + { + "if": { + "and": [ + "planned:amenity=charging_station", + "amenity=" + ] + }, + "then": { + "en": "A charging station is planned here", + "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden" + } + }, + { + "if": { + "and": [ + "construction:amenity=charging_station", + "amenity=" + ] + }, + "then": { + "en": "A charging station is constructed here", + "nl": "Hier wordt op dit moment een oplaadpunt gebouwd" + } + }, + { + "if": { + "and": [ + "disused:amenity=charging_station", + "amenity=" + ] + }, + "then": { + "en": "This charging station has beed permanently disabled and is not in use anymore but is still visible", + "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig" + } + }, + { + "if": { + "and": [ + "amenity=charging_station", + "operational_status=" + ] + }, + "then": { + "en": "This charging station works", + "nl": "Dit oplaadpunt werkt" + } + } + ] + }, + { + "id": "Parking:fee", + "question": { + "en": "Does one have to pay a parking fee while charging?" + }, + "mappings": [ + { + "if": "parking:fee=no", + "then": { + "en": "No additional parking cost while charging" + } + }, + { + "if": "parking:fee=yes", + "then": { + "en": "An additional parking fee should be paid while charging" + } + } + ] + } + ], + "mapRendering": [ + { + "icon": { "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", "mappings": [ - { - "if": "bicycle=yes", - "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" + { + "if": "bicycle=yes", + "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" + }, + { + "if": { + "or": [ + "car=yes", + "motorcar=yes" + ] }, - { - "if": { - "or": [ - "car=yes", - "motorcar=yes" - ] - }, - "then": "pin:#fff;./assets/themes/charging_stations/car.svg" - } + "then": "pin:#fff;./assets/themes/charging_stations/car.svg" + } ] - }, - "iconOverlays": [ + }, + "iconOverlays": [ { - "if": { - "or": [ - "disused:amenity=charging_station", - "operational_status=broken" - ] - }, - "then": "cross_bottom_right:#c22;" + "if": { + "or": [ + "disused:amenity=charging_station", + "operational_status=broken" + ] + }, + "then": "cross_bottom_right:#c22;" }, { - "if": { - "or": [ - "proposed:amenity=charging_station", - "planned:amenity=charging_station" - ] - }, - "then": "./assets/layers/charging_station/under_construction.svg", - "badge": true + "if": { + "or": [ + "proposed:amenity=charging_station", + "planned:amenity=charging_station" + ] + }, + "then": "./assets/layers/charging_station/under_construction.svg", + "badge": true }, { - "if": { - "and": [ - "bicycle=yes", - { - "or": [ - "motorcar=yes", - "car=yes" - ] - } + "if": { + "and": [ + "bicycle=yes", + { + "or": [ + "motorcar=yes", + "car=yes" ] - }, - "then": "circle:#fff;./assets/themes/charging_stations/car.svg", - "badge": true + } + ] + }, + "then": "circle:#fff;./assets/themes/charging_stations/car.svg", + "badge": true } - ], - "width": { - "render": "8" - }, - "iconSize": { + ], + "iconSize": { "render": "50,50,bottom" + } + } + ], + "presets": [ + { + "tags": [ + "amenity=charging_station" + ], + "title": { + "en": "Charging station" + }, + "preciseInput": { + "preferredBackground": "map" + } + } + ], + "wayHandling": 1, + "filter": [ + { + "id": "vehicle-type", + "options": [ + { + "question": { + "en": "All vehicle types", + "nl": "Alle voertuigen" + } + }, + { + "question": { + "en": "Charging station for bicycles", + "nl": "Oplaadpunten voor fietsen" + }, + "osmTags": "bicycle=yes" + }, + { + "question": { + "en": "Charging station for cars", + "nl": "Oplaadpunten voor auto's" + }, + "osmTags": { + "or": [ + "car=yes", + "motorcar=yes" + ] + } + } + ] }, - "color": { - "render": "#00f" + { + "id": "working", + "options": [ + { + "question": { + "en": "Only working charging stations" + }, + "osmTags": { + "and": [ + "operational_status!=broken", + "amenity=charging_station" + ] + } + } + ] }, - "presets": [ + { + "id": "connection_type", + "options": [ { - "tags": [ - "amenity=charging_station" - ], - "title": { - "en": "Charging station", - "de": "Ladestation", - "ru": "Зарядная станция" - }, - "preciseInput": { - "preferredBackground": "map" - } + "question": { + "en": "All connectors", + "nl": "Alle types" + } + }, + { + "question": { + "en": "Has a
Schuko wall plug without ground pin (CEE7/4 type F)
connector", + "nl": "Heeft een
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "osmTags": "socket:schuko~*" + }, + { + "question": { + "en": "Has a
European wall plug with ground pin (CEE7/4 type E)
connector", + "nl": "Heeft een
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "osmTags": "socket:typee~*" + }, + { + "question": { + "en": "Has a
Chademo
connector", + "nl": "Heeft een
Chademo
" + }, + "osmTags": "socket:chademo~*" + }, + { + "question": { + "en": "Has a
Type 1 with cable (J1772)
connector", + "nl": "Heeft een
Type 1 met kabel (J1772)
" + }, + "osmTags": "socket:type1_cable~*" + }, + { + "question": { + "en": "Has a
Type 1 without cable (J1772)
connector", + "nl": "Heeft een
Type 1 zonder kabel (J1772)
" + }, + "osmTags": "socket:type1~*" + }, + { + "question": { + "en": "Has a
Type 1 CCS (aka Type 1 Combo)
connector", + "nl": "Heeft een
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "osmTags": "socket:type1_combo~*" + }, + { + "question": { + "en": "Has a
Tesla Supercharger
connector", + "nl": "Heeft een
Tesla Supercharger
" + }, + "osmTags": "socket:tesla_supercharger~*" + }, + { + "question": { + "en": "Has a
Type 2 (mennekes)
connector", + "nl": "Heeft een
Type 2 (mennekes)
" + }, + "osmTags": "socket:type2~*" + }, + { + "question": { + "en": "Has a
Type 2 CCS (mennekes)
connector", + "nl": "Heeft een
Type 2 CCS (mennekes)
" + }, + "osmTags": "socket:type2_combo~*" + }, + { + "question": { + "en": "Has a
Type 2 with cable (mennekes)
connector", + "nl": "Heeft een
Type 2 met kabel (J1772)
" + }, + "osmTags": "socket:type2_cable~*" + }, + { + "question": { + "en": "Has a
Tesla Supercharger CCS (a branded type2_css)
connector", + "nl": "Heeft een
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "osmTags": "socket:tesla_supercharger_ccs~*" + }, + { + "question": { + "en": "Has a
Tesla Supercharger (destination)
connector", + "nl": "Heeft een
Tesla Supercharger (destination)
" + }, + "osmTags": "socket:tesla_destination~*" + }, + { + "question": { + "en": "Has a
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
connector", + "nl": "Heeft een
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "osmTags": "socket:tesla_destination~*" + }, + { + "question": { + "en": "Has a
USB to charge phones and small electronics
connector", + "nl": "Heeft een
USB om GSMs en kleine electronica op te laden
" + }, + "osmTags": "socket:USB-A~*" + }, + { + "question": { + "en": "Has a
Bosch Active Connect with 3 pins and cable
connector", + "nl": "Heeft een
Bosch Active Connect met 3 pinnen aan een kabel
" + }, + "osmTags": "socket:bosch_3pin~*" + }, + { + "question": { + "en": "Has a
Bosch Active Connect with 5 pins and cable
connector", + "nl": "Heeft een
Bosch Active Connect met 5 pinnen aan een kabel
" + }, + "osmTags": "socket:bosch_5pin~*" } - ], - "wayHandling": 1, - "filter": [ + ] + } + ], + "units": [ + { + "appliesToKey": [ + "maxstay" + ], + "applicableUnits": [ { - "id": "vehicle-type", - "options": [ - { - "question": { - "en": "All vehicle types", - "nl": "Alle voertuigen", - "de": "Alle Fahrzeugtypen" - } - }, - { - "question": { - "en": "Charging station for bicycles", - "nl": "Oplaadpunten voor fietsen", - "de": "Ladestation für Fahrräder" - }, - "osmTags": "bicycle=yes" - }, - { - "question": { - "en": "Charging station for cars", - "nl": "Oplaadpunten voor auto's", - "de": "Ladestation für Autos" - }, - "osmTags": { - "or": [ - "car=yes", - "motorcar=yes" - ] - } - } - ] + "canonicalDenomination": "minutes", + "canonicalDenominationSingular": "minute", + "alternativeDenomination": [ + "m", + "min", + "mins", + "minuten", + "mns" + ], + "human": { + "en": " minutes", + "nl": " minuten" + }, + "humanSingular": { + "en": " minute", + "nl": " minuut" + } }, { - "id": "working", - "options": [ - { - "question": { - "en": "Only working charging stations", - "de": "Nur funktionierende Ladestationen" - }, - "osmTags": { - "and": [ - "operational_status!=broken", - "amenity=charging_station" - ] - } - } - ] + "canonicalDenomination": "hours", + "canonicalDenominationSingular": "hour", + "alternativeDenomination": [ + "h", + "hrs", + "hours", + "u", + "uur", + "uren" + ], + "human": { + "en": " hours", + "nl": " uren" + }, + "humanSingular": { + "en": " hour", + "nl": " uur" + } }, { - "id": "connection_type", - "options": [ - { - "question": { - "en": "All connectors", - "nl": "Alle types", - "de": "Alle Anschlüsse" - } - }, - { - "question": { - "en": "Has a
Schuko wall plug without ground pin (CEE7/4 type F)
connector", - "nl": "Heeft een
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "osmTags": "socket:schuko~*" - }, - { - "question": { - "en": "Has a
European wall plug with ground pin (CEE7/4 type E)
connector", - "nl": "Heeft een
Europese stekker met aardingspin (CEE7/4 type E)
" - }, - "osmTags": "socket:typee~*" - }, - { - "question": { - "en": "Has a
Chademo
connector", - "nl": "Heeft een
Chademo
" - }, - "osmTags": "socket:chademo~*" - }, - { - "question": { - "en": "Has a
Type 1 with cable (J1772)
connector", - "nl": "Heeft een
Type 1 met kabel (J1772)
" - }, - "osmTags": "socket:type1_cable~*" - }, - { - "question": { - "en": "Has a
Type 1 without cable (J1772)
connector", - "nl": "Heeft een
Type 1 zonder kabel (J1772)
" - }, - "osmTags": "socket:type1~*" - }, - { - "question": { - "en": "Has a
Type 1 CCS (aka Type 1 Combo)
connector", - "nl": "Heeft een
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "osmTags": "socket:type1_combo~*" - }, - { - "question": { - "en": "Has a
Tesla Supercharger
connector", - "nl": "Heeft een
Tesla Supercharger
" - }, - "osmTags": "socket:tesla_supercharger~*" - }, - { - "question": { - "en": "Has a
Type 2 (mennekes)
connector", - "nl": "Heeft een
Type 2 (mennekes)
" - }, - "osmTags": "socket:type2~*" - }, - { - "question": { - "en": "Has a
Type 2 CCS (mennekes)
connector", - "nl": "Heeft een
Type 2 CCS (mennekes)
" - }, - "osmTags": "socket:type2_combo~*" - }, - { - "question": { - "en": "Has a
Type 2 with cable (mennekes)
connector", - "nl": "Heeft een
Type 2 met kabel (J1772)
" - }, - "osmTags": "socket:type2_cable~*" - }, - { - "question": { - "en": "Has a
Tesla Supercharger CCS (a branded type2_css)
connector", - "nl": "Heeft een
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "osmTags": "socket:tesla_supercharger_ccs~*" - }, - { - "question": { - "en": "Has a
Tesla Supercharger (destination)
connector", - "nl": "Heeft een
Tesla Supercharger (destination)
" - }, - "osmTags": "socket:tesla_destination~*" - }, - { - "question": { - "en": "Has a
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
connector", - "nl": "Heeft een
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "osmTags": "socket:tesla_destination~*" - }, - { - "question": { - "en": "Has a
USB to charge phones and small electronics
connector", - "nl": "Heeft een
USB om GSMs en kleine electronica op te laden
" - }, - "osmTags": "socket:USB-A~*" - }, - { - "question": { - "en": "Has a
Bosch Active Connect with 3 pins and cable
connector", - "nl": "Heeft een
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "osmTags": "socket:bosch_3pin~*" - }, - { - "question": { - "en": "Has a
Bosch Active Connect with 5 pins and cable
connector", - "nl": "Heeft een
Bosch Active Connect met 5 pinnen aan een kabel
" - }, - "osmTags": "socket:bosch_5pin~*" - } - ] + "canonicalDenomination": "days", + "canonicalDenominationSingular": "day", + "alternativeDenomination": [ + "dys", + "dagen", + "dag" + ], + "human": { + "en": " days", + "nl": " day" + }, + "humanSingular": { + "en": " day", + "nl": " dag" + } } - ], - "units": [ + ] + }, + { + "appliesToKey": [ + "socket:schuko:voltage", + "socket:typee:voltage", + "socket:chademo:voltage", + "socket:type1_cable:voltage", + "socket:type1:voltage", + "socket:type1_combo:voltage", + "socket:tesla_supercharger:voltage", + "socket:type2:voltage", + "socket:type2_combo:voltage", + "socket:type2_cable:voltage", + "socket:tesla_supercharger_ccs:voltage", + "socket:tesla_destination:voltage", + "socket:tesla_destination:voltage", + "socket:USB-A:voltage", + "socket:bosch_3pin:voltage", + "socket:bosch_5pin:voltage" + ], + "applicableUnits": [ { - "appliesToKey": [ - "maxstay" - ], - "applicableUnits": [ - { - "canonicalDenomination": "minutes", - "canonicalDenominationSingular": "minute", - "alternativeDenomination": [ - "m", - "min", - "mins", - "minuten", - "mns" - ], - "human": { - "en": " minutes", - "nl": " minuten", - "de": " Minuten", - "ru": " минут" - }, - "humanSingular": { - "en": " minute", - "nl": " minuut", - "de": " Minute", - "ru": " минута" - } - }, - { - "canonicalDenomination": "hours", - "canonicalDenominationSingular": "hour", - "alternativeDenomination": [ - "h", - "hrs", - "hours", - "u", - "uur", - "uren" - ], - "human": { - "en": " hours", - "nl": " uren", - "de": " Stunden", - "ru": " часов" - }, - "humanSingular": { - "en": " hour", - "nl": " uur", - "ru": " час" - } - }, - { - "canonicalDenomination": "days", - "canonicalDenominationSingular": "day", - "alternativeDenomination": [ - "dys", - "dagen", - "dag" - ], - "human": { - "en": " days", - "nl": " day", - "de": " Tage", - "ru": " дней" - }, - "humanSingular": { - "en": " day", - "nl": " dag", - "ru": " день" - } - } - ] - }, - { - "appliesToKey": [ - "socket:schuko:voltage", - "socket:typee:voltage", - "socket:chademo:voltage", - "socket:type1_cable:voltage", - "socket:type1:voltage", - "socket:type1_combo:voltage", - "socket:tesla_supercharger:voltage", - "socket:type2:voltage", - "socket:type2_combo:voltage", - "socket:type2_cable:voltage", - "socket:tesla_supercharger_ccs:voltage", - "socket:tesla_destination:voltage", - "socket:tesla_destination:voltage", - "socket:USB-A:voltage", - "socket:bosch_3pin:voltage", - "socket:bosch_5pin:voltage" - ], - "applicableUnits": [ - { - "canonicalDenomination": "V", - "alternativeDenomination": [ - "v", - "volt", - "voltage", - "V", - "Volt" - ], - "human": { - "en": "Volts", - "nl": "volt", - "ru": "Вольт" - } - } - ], - "eraseInvalidValues": true - }, - { - "appliesToKey": [ - "socket:schuko:current", - "socket:typee:current", - "socket:chademo:current", - "socket:type1_cable:current", - "socket:type1:current", - "socket:type1_combo:current", - "socket:tesla_supercharger:current", - "socket:type2:current", - "socket:type2_combo:current", - "socket:type2_cable:current", - "socket:tesla_supercharger_ccs:current", - "socket:tesla_destination:current", - "socket:tesla_destination:current", - "socket:USB-A:current", - "socket:bosch_3pin:current", - "socket:bosch_5pin:current" - ], - "applicableUnits": [ - { - "canonicalDenomination": "A", - "alternativeDenomination": [ - "a", - "amp", - "amperage", - "A" - ], - "human": { - "en": "A", - "nl": "A" - } - } - ], - "eraseInvalidValues": true - }, - { - "appliesToKey": [ - "socket:schuko:output", - "socket:typee:output", - "socket:chademo:output", - "socket:type1_cable:output", - "socket:type1:output", - "socket:type1_combo:output", - "socket:tesla_supercharger:output", - "socket:type2:output", - "socket:type2_combo:output", - "socket:type2_cable:output", - "socket:tesla_supercharger_ccs:output", - "socket:tesla_destination:output", - "socket:tesla_destination:output", - "socket:USB-A:output", - "socket:bosch_3pin:output", - "socket:bosch_5pin:output" - ], - "applicableUnits": [ - { - "canonicalDenomination": "kW", - "alternativeDenomination": [ - "kilowatt" - ], - "human": { - "en": "kilowatt", - "nl": "kilowatt", - "ru": "киловатт" - } - }, - { - "canonicalDenomination": "mW", - "alternativeDenomination": [ - "megawatt" - ], - "human": { - "en": "megawatt", - "nl": "megawatt", - "ru": "мегаватт" - } - } - ], - "eraseInvalidValues": true + "canonicalDenomination": "V", + "alternativeDenomination": [ + "v", + "volt", + "voltage", + "V", + "Volt" + ], + "human": { + "en": "Volts", + "nl": "volt" + } } - ], - "mapRendering": [ + ], + "eraseInvalidValues": true + }, + { + "appliesToKey": [ + "socket:schuko:current", + "socket:typee:current", + "socket:chademo:current", + "socket:type1_cable:current", + "socket:type1:current", + "socket:type1_combo:current", + "socket:tesla_supercharger:current", + "socket:type2:current", + "socket:type2_combo:current", + "socket:type2_cable:current", + "socket:tesla_supercharger_ccs:current", + "socket:tesla_destination:current", + "socket:tesla_destination:current", + "socket:USB-A:current", + "socket:bosch_3pin:current", + "socket:bosch_5pin:current" + ], + "applicableUnits": [ { - "icon": { - "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", - "mappings": [ - { - "if": "bicycle=yes", - "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" - }, - { - "if": { - "or": [ - "car=yes", - "motorcar=yes" - ] - }, - "then": "pin:#fff;./assets/themes/charging_stations/car.svg" - } - ] - }, - "iconOverlays": [ - { - "if": { - "or": [ - "disused:amenity=charging_station", - "operational_status=broken" - ] - }, - "then": "cross_bottom_right:#c22;" - }, - { - "if": { - "or": [ - "proposed:amenity=charging_station", - "planned:amenity=charging_station" - ] - }, - "then": "./assets/layers/charging_station/under_construction.svg", - "badge": true - }, - { - "if": { - "and": [ - "bicycle=yes", - { - "or": [ - "motorcar=yes", - "car=yes" - ] - } - ] - }, - "then": "circle:#fff;./assets/themes/charging_stations/car.svg", - "badge": true - } - ], - "iconSize": { - "render": "50,50,bottom" - }, - "location": [ - "point" - ] + "canonicalDenomination": "A", + "alternativeDenomination": [ + "a", + "amp", + "amperage", + "A" + ], + "human": { + "en": "A", + "nl": "A" + } } - ] + ], + "eraseInvalidValues": true + }, + { + "appliesToKey": [ + "socket:schuko:output", + "socket:typee:output", + "socket:chademo:output", + "socket:type1_cable:output", + "socket:type1:output", + "socket:type1_combo:output", + "socket:tesla_supercharger:output", + "socket:type2:output", + "socket:type2_combo:output", + "socket:type2_cable:output", + "socket:tesla_supercharger_ccs:output", + "socket:tesla_destination:output", + "socket:tesla_destination:output", + "socket:USB-A:output", + "socket:bosch_3pin:output", + "socket:bosch_5pin:output" + ], + "applicableUnits": [ + { + "canonicalDenomination": "kW", + "alternativeDenomination": [ + "kilowatt" + ], + "human": { + "en": "kilowatt", + "nl": "kilowatt" + } + }, + { + "canonicalDenomination": "mW", + "alternativeDenomination": [ + "megawatt" + ], + "human": { + "en": "megawatt", + "nl": "megawatt" + } + } + ], + "eraseInvalidValues": true + } + ] } \ No newline at end of file diff --git a/assets/layers/charging_station/charging_station.protojson b/assets/layers/charging_station/charging_station.protojson index 410404d4e..a44a660ba 100644 --- a/assets/layers/charging_station/charging_station.protojson +++ b/assets/layers/charging_station/charging_station.protojson @@ -560,69 +560,67 @@ ] } ], - "icon": { - "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", - "mappings": [ - { - "if": "bicycle=yes", - "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" - }, - { - "if": { - "or": [ - "car=yes", - "motorcar=yes" - ] - }, - "then": "pin:#fff;./assets/themes/charging_stations/car.svg" - } - ] - }, - "iconOverlays": [ + "mapRendering": [ { - "if": { - "or": [ - "disused:amenity=charging_station", - "operational_status=broken" - ] - }, - "then": "cross_bottom_right:#c22;" - }, - { - "if": { - "or": [ - "proposed:amenity=charging_station", - "planned:amenity=charging_station" - ] - }, - "then": "./assets/layers/charging_station/under_construction.svg", - "badge": true - }, - { - "if": { - "and": [ - "bicycle=yes", + "icon": { + "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", + "mappings": [ { - "or": [ - "motorcar=yes", - "car=yes" - ] + "if": "bicycle=yes", + "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" + }, + { + "if": { + "or": [ + "car=yes", + "motorcar=yes" + ] + }, + "then": "pin:#fff;./assets/themes/charging_stations/car.svg" } ] }, - "then": "circle:#fff;./assets/themes/charging_stations/car.svg", - "badge": true + "iconOverlays": [ + { + "if": { + "or": [ + "disused:amenity=charging_station", + "operational_status=broken" + ] + }, + "then": "cross_bottom_right:#c22;" + }, + { + "if": { + "or": [ + "proposed:amenity=charging_station", + "planned:amenity=charging_station" + ] + }, + "then": "./assets/layers/charging_station/under_construction.svg", + "badge": true + }, + { + "if": { + "and": [ + "bicycle=yes", + { + "or": [ + "motorcar=yes", + "car=yes" + ] + } + ] + }, + "then": "circle:#fff;./assets/themes/charging_stations/car.svg", + "badge": true + } + ], + "iconSize": { + "render": "50,50,bottom" + } } ], - "width": { - "render": "8" - }, - "iconSize": { - "render": "50,50,bottom" - }, - "color": { - "render": "#00f" - }, "presets": [ { "tags": [ diff --git a/langs/layers/de.json b/langs/layers/de.json index 40a1b38c9..3e15741f4 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -544,6 +544,13 @@ "icon": { "render": "./assets/layers/bike_repair_station/repair_station.svg" }, + "mapRendering": { + "0": { + "icon": { + "render": "./assets/layers/bike_repair_station/repair_station.svg" + } + } + }, "name": "Fahrradstationen (Reparatur, Pumpe oder beides)", "presets": { "0": { diff --git a/langs/layers/en.json b/langs/layers/en.json index f32bf146e..ec5f82bd1 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -547,6 +547,13 @@ "icon": { "render": "./assets/layers/bike_repair_station/repair_station.svg" }, + "mapRendering": { + "0": { + "icon": { + "render": "./assets/layers/bike_repair_station/repair_station.svg" + } + } + }, "name": "Bike stations (repair, pump or both)", "presets": { "0": { @@ -2409,6 +2416,17 @@ } } }, + "mapRendering": { + "0": { + "icon": { + "mappings": { + "0": { + "then": "./assets/layers/defibrillator/aed_checked.svg" + } + } + } + } + }, "name": "Defibrillators", "presets": { "0": { @@ -3848,6 +3866,17 @@ } } }, + "mapRendering": { + "0": { + "iconSize": { + "mappings": { + "0": { + "then": "Waste Basket" + } + } + } + } + }, "name": "Waste Basket", "presets": { "0": { diff --git a/langs/layers/fi.json b/langs/layers/fi.json index a13231cd6..8f7e9b2a5 100644 --- a/langs/layers/fi.json +++ b/langs/layers/fi.json @@ -93,6 +93,13 @@ "icon": { "render": "./assets/layers/bike_repair_station/repair_station.svg" }, + "mapRendering": { + "0": { + "icon": { + "render": "./assets/layers/bike_repair_station/repair_station.svg" + } + } + }, "presets": { "0": { "title": "Pyöräpumppu" diff --git a/langs/layers/fr.json b/langs/layers/fr.json index 7892204ad..053afa259 100644 --- a/langs/layers/fr.json +++ b/langs/layers/fr.json @@ -456,6 +456,13 @@ "icon": { "render": "./assets/layers/bike_repair_station/repair_station.svg" }, + "mapRendering": { + "0": { + "icon": { + "render": "./assets/layers/bike_repair_station/repair_station.svg" + } + } + }, "name": "Station velo (réparation, pompe à vélo)", "presets": { "0": { @@ -756,6 +763,17 @@ } } }, + "mapRendering": { + "0": { + "icon": { + "mappings": { + "0": { + "then": "./assets/layers/defibrillator/aed_checked.svg" + } + } + } + } + }, "name": "Défibrillateurs", "presets": { "0": { diff --git a/langs/layers/it.json b/langs/layers/it.json index aa52a5224..0a99c373f 100644 --- a/langs/layers/it.json +++ b/langs/layers/it.json @@ -456,6 +456,13 @@ "icon": { "render": "./assets/layers/bike_repair_station/repair_station.svg" }, + "mapRendering": { + "0": { + "icon": { + "render": "./assets/layers/bike_repair_station/repair_station.svg" + } + } + }, "name": "Stazioni bici (riparazione, gonfiaggio o entrambi)", "presets": { "0": { @@ -772,6 +779,17 @@ } } }, + "mapRendering": { + "0": { + "icon": { + "mappings": { + "0": { + "then": "./assets/layers/defibrillator/aed_checked.svg" + } + } + } + } + }, "name": "Defibrillatori", "presets": { "0": { diff --git a/langs/layers/nl.json b/langs/layers/nl.json index fb5c7fbb8..5666dadd1 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -896,6 +896,13 @@ "icon": { "render": "./assets/layers/birdhide/birdhide.svg" }, + "mapRendering": { + "0": { + "icon": { + "render": "./assets/layers/birdhide/birdhide.svg" + } + } + }, "name": "Vogelkijkhutten", "presets": { "0": { @@ -4131,6 +4138,17 @@ } } }, + "mapRendering": { + "0": { + "iconSize": { + "mappings": { + "0": { + "then": "Vuilnisbak" + } + } + } + } + }, "name": "Vuilnisbak", "presets": { "0": { diff --git a/langs/layers/pt.json b/langs/layers/pt.json index 5cdf2f56a..9378ee12d 100644 --- a/langs/layers/pt.json +++ b/langs/layers/pt.json @@ -341,6 +341,13 @@ "icon": { "render": "./assets/layers/bike_repair_station/repair_station.svg" }, + "mapRendering": { + "0": { + "icon": { + "render": "./assets/layers/bike_repair_station/repair_station.svg" + } + } + }, "presets": { "0": { "description": "Um aparelho para encher os seus pneus num local fixa no espaço público

Exemplos de bombas de bicicletas

" diff --git a/langs/layers/pt_BR.json b/langs/layers/pt_BR.json index 923a5d76d..5b8bcb9eb 100644 --- a/langs/layers/pt_BR.json +++ b/langs/layers/pt_BR.json @@ -341,6 +341,13 @@ "icon": { "render": "./assets/layers/bike_repair_station/repair_station.svg" }, + "mapRendering": { + "0": { + "icon": { + "render": "./assets/layers/bike_repair_station/repair_station.svg" + } + } + }, "name": "Estações de bicicletas (reparo, bomba ou ambos)", "presets": { "0": { diff --git a/langs/layers/ru.json b/langs/layers/ru.json index 66f7d0cb0..905e79cad 100644 --- a/langs/layers/ru.json +++ b/langs/layers/ru.json @@ -403,6 +403,13 @@ "icon": { "render": "./assets/layers/bike_repair_station/repair_station.svg" }, + "mapRendering": { + "0": { + "icon": { + "render": "./assets/layers/bike_repair_station/repair_station.svg" + } + } + }, "presets": { "0": { "title": "Велосипедный насос" @@ -722,6 +729,17 @@ } } }, + "mapRendering": { + "0": { + "icon": { + "mappings": { + "0": { + "then": "./assets/layers/defibrillator/aed_checked.svg" + } + } + } + } + }, "name": "Дефибрилляторы", "presets": { "0": { @@ -1446,6 +1464,17 @@ } } }, + "mapRendering": { + "0": { + "iconSize": { + "mappings": { + "0": { + "then": "Контейнер для мусора" + } + } + } + } + }, "name": "Контейнер для мусора", "presets": { "0": { diff --git a/langs/themes/de.json b/langs/themes/de.json index f7680b81b..8c59fd659 100644 --- a/langs/themes/de.json +++ b/langs/themes/de.json @@ -751,6 +751,17 @@ } } }, + "mapRendering": { + "0": { + "icon": { + "mappings": { + "0": { + "then": "./assets/themes/hackerspaces/led.png" + } + } + } + } + }, "name": "Hackerspace", "presets": { "0": { diff --git a/langs/themes/en.json b/langs/themes/en.json index 54905e273..9f96d084e 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -804,6 +804,17 @@ } } }, + "mapRendering": { + "0": { + "icon": { + "mappings": { + "0": { + "then": "./assets/themes/hackerspaces/led.png" + } + } + } + } + }, "name": "Hackerspace", "presets": { "0": { From b9b8a5c71a5000fd185b98d94c7e2614ff3e40b2 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 21 Oct 2021 21:41:45 +0200 Subject: [PATCH 14/95] Linting themes, fix userbadges --- Models/ThemeConfig/Json/LayerConfigJson.ts | 9 - .../Json/PointRenderingConfigJson.ts | 10 +- Models/ThemeConfig/LayerConfig.ts | 5 - Models/ThemeConfig/PointRenderingConfig.ts | 244 +- Models/ThemeConfig/TagRenderingConfig.ts | 9 +- .../bicycle_library/bicycle_library.json | 14 +- .../bicycle_tube_vending_machine.json | 8 +- .../layers/bike_cleaning/bike_cleaning.json | 8 +- .../bike_repair_station.json | 8 +- assets/layers/bike_shop/bike_shop.json | 20 +- assets/layers/cafe_pub/cafe_pub.json | 8 +- .../charging_station/charging_station.json | 7168 ++++++++--------- .../layers/drinking_water/drinking_water.json | 8 +- assets/layers/food/food.json | 14 +- assets/layers/playground/playground.json | 8 +- .../public_bookcase/public_bookcase.json | 2 +- assets/layers/shops/shops.json | 8 +- assets/layers/sport_pitch/sport_pitch.json | 14 +- assets/svg/license_info.json | 22 +- assets/svg/teardrop.svg | 104 + assets/svg/teardrop_with_hole_green.svg | 130 + assets/themes/climbing/climbing.json | 16 +- .../themes/facadegardens/facadegardens.json | 38 +- assets/themes/postboxes/postboxes.json | 8 +- css/index-tailwind-output.css | 41 +- index.css | 9 + scripts/lint.ts | 44 +- 27 files changed, 4074 insertions(+), 3903 deletions(-) create mode 100644 assets/svg/teardrop.svg create mode 100644 assets/svg/teardrop_with_hole_green.svg diff --git a/Models/ThemeConfig/Json/LayerConfigJson.ts b/Models/ThemeConfig/Json/LayerConfigJson.ts index 6c6bc107a..353cfcce5 100644 --- a/Models/ThemeConfig/Json/LayerConfigJson.ts +++ b/Models/ThemeConfig/Json/LayerConfigJson.ts @@ -124,15 +124,6 @@ export interface LayerConfigJson { mapRendering: (PointRenderingConfigJson | LineRenderingConfigJson)[] - - /** - * Wayhandling: should a way/area be displayed as: - * 0) The way itself - * 1) Only the centerpoint - * 2) The centerpoint and the way - */ - wayHandling?: number; - /** * If set, this layer will pass all the features it receives onto the next layer. * This is ideal for decoration, e.g. directionss on cameras diff --git a/Models/ThemeConfig/Json/PointRenderingConfigJson.ts b/Models/ThemeConfig/Json/PointRenderingConfigJson.ts index 694d98f26..1ab46c8d0 100644 --- a/Models/ThemeConfig/Json/PointRenderingConfigJson.ts +++ b/Models/ThemeConfig/Json/PointRenderingConfigJson.ts @@ -31,14 +31,12 @@ export default interface PointRenderingConfigJson { icon?: string | TagRenderingConfigJson; /** - * IconsOverlays are a list of extra icons/badges to overlay over the icon. - * The 'badge'-toggle changes their behaviour. - * If badge is set, it will be added as a 25% height icon at the bottom right of the icon, with all the badges in a flex layout. - * If badges is false, it'll be a simple overlay + * A list of extra badges to show next to the icon as small badge + * They will be added as a 25% height icon at the bottom right of the icon, with all the badges in a flex layout. * - * Note: strings are interpreted as icons, so layering and substituting is supported + * Note: strings are interpreted as icons, so layering and substituting is supported. You can use `circle:white;./my_icon.svg` to add a background circle */ - iconOverlays?: { if: string | AndOrTagConfigJson, then: string | TagRenderingConfigJson, badge?: boolean }[] + iconBadges?: { if: string | AndOrTagConfigJson, then: string | TagRenderingConfigJson }[] /** diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts index 06c5eca47..7bb4db470 100644 --- a/Models/ThemeConfig/LayerConfig.ts +++ b/Models/ThemeConfig/LayerConfig.ts @@ -20,9 +20,6 @@ import PointRenderingConfigJson from "./Json/PointRenderingConfigJson"; import LineRenderingConfigJson from "./Json/LineRenderingConfigJson"; export default class LayerConfig extends WithContextLoader{ - static WAYHANDLING_DEFAULT = 0; - static WAYHANDLING_CENTER_ONLY = 1; - static WAYHANDLING_CENTER_AND_WAY = 2; id: string; name: Translation; @@ -41,7 +38,6 @@ export default class LayerConfig extends WithContextLoader{ public readonly mapRendering: PointRenderingConfig[] public readonly lineRendering: LineRenderingConfig[] - wayHandling: number; public readonly units: Unit[]; public readonly deletion: DeleteConfig | null; public readonly allowMove: MoveConfig | null @@ -153,7 +149,6 @@ export default class LayerConfig extends WithContextLoader{ this.passAllFeatures = json.passAllFeatures ?? false; this.minzoom = json.minzoom ?? 0; this.minzoomVisible = json.minzoomVisible ?? this.minzoom; - this.wayHandling = json.wayHandling ?? 0; if (json.presets !== undefined && json.presets?.map === undefined) { throw "Presets should be a list of items (at " + context + ")" } diff --git a/Models/ThemeConfig/PointRenderingConfig.ts b/Models/ThemeConfig/PointRenderingConfig.ts index 51cfc18d6..4da8ae32f 100644 --- a/Models/ThemeConfig/PointRenderingConfig.ts +++ b/Models/ThemeConfig/PointRenderingConfig.ts @@ -16,7 +16,7 @@ import {VariableUiElement} from "../../UI/Base/VariableUIElement"; export default class PointRenderingConfig extends WithContextLoader { public readonly icon: TagRenderingConfig; - public readonly iconOverlays: { if: TagsFilter; then: TagRenderingConfig; badge: boolean }[]; + public readonly iconBadges: { if: TagsFilter; then: TagRenderingConfig }[]; public readonly iconSize: TagRenderingConfig; public readonly label: TagRenderingConfig; public readonly rotation: TagRenderingConfig; @@ -24,21 +24,20 @@ export default class PointRenderingConfig extends WithContextLoader { constructor(json: PointRenderingConfigJson, context: string) { super(json, context) this.icon = this.tr("icon", ""); - this.iconOverlays = (json.iconOverlays ?? []).map((overlay, i) => { - let tr = new TagRenderingConfig( - overlay.then, - `iconoverlays.${i}` - ); - if ( - typeof overlay.then === "string" && - SharedTagRenderings.SharedIcons.get(overlay.then) !== undefined - ) { + this.iconBadges = (json.iconBadges ?? []).map((overlay, i) => { + let tr : TagRenderingConfig; + if (typeof overlay.then === "string" && + SharedTagRenderings.SharedIcons.get(overlay.then) !== undefined) { tr = SharedTagRenderings.SharedIcons.get(overlay.then); + }else{ + tr = new TagRenderingConfig( + overlay.then, + `iconBadges.${i}` + ); } return { if: TagUtils.Tag(overlay.if), - then: tr, - badge: overlay.badge ?? false, + then: tr }; }); @@ -50,7 +49,7 @@ export default class PointRenderingConfig extends WithContextLoader { } } this.iconSize = this.tr("iconSize", "40,40,center"); - this.label = this.tr("label", ""); + this.label = this.tr("label", undefined); this.rotation = this.tr("rotation", "0"); } @@ -59,7 +58,7 @@ export default class PointRenderingConfig extends WithContextLoader { const parts: Set[] = []; parts.push(this.icon?.ExtractImages(true)); parts.push( - ...this.iconOverlays?.map((overlay) => overlay.then.ExtractImages(true)) + ...this.iconBadges?.map((overlay) => overlay.then.ExtractImages(true)) ); const allIcons = new Set(); @@ -69,21 +68,115 @@ export default class PointRenderingConfig extends WithContextLoader { return allIcons; } + /** + * Given a single HTML spec (either a single image path OR "image_path_to_known_svg:fill-colour", returns a fixedUIElement containing that + * The element will fill 100% and be positioned absolutely with top:0 and left: 0 + */ + private static FromHtmlSpec(htmlSpec: string, style: string, isBadge = false): BaseUIElement { + if (htmlSpec === undefined) { + return undefined; + } + const match = htmlSpec.match(/([a-zA-Z0-9_]*):([^;]*)/); + if (match !== null && Svg.All[match[1] + ".svg"] !== undefined) { + const svg = (Svg.All[match[1] + ".svg"] as string) + const targetColor = match[2] + const img = new Img(svg.replace(/#000000/g, targetColor), true) + .SetStyle(style) + if(isBadge){ + img.SetClass("badge") + } + return img + } else { + return new FixedUiElement(``); + } + } + + private static FromHtmlMulti(multiSpec: string, rotation: string , isBadge: boolean, defaultElement: BaseUIElement = undefined){ + if(multiSpec === undefined){ + return defaultElement + } + const style = `width:100%;height:100%;transform: rotate( ${rotation} );display:block;position: absolute; top: 0; left: 0`; + const htmlDefs = multiSpec.trim()?.split(";") ?? [] + const elements = Utils.NoEmpty(htmlDefs).map(def => PointRenderingConfig.FromHtmlSpec(def, style, isBadge)) + if (elements.length === 0) { + return defaultElement + } else { + return new Combine(elements).SetClass("relative block w-full h-full") + } + } + + public GetSimpleIcon(tags: UIEventSource): BaseUIElement { + const self = this; + if (this.icon === undefined) { + return undefined; + } + return new VariableUiElement(tags.map(tags => { + const rotation = self.rotation?.GetRenderValue(tags)?.txt ?? "0deg" + + const htmlDefs = self.icon.GetRenderValue(tags)?.txt + let defaultPin : BaseUIElement = undefined + if(self.label === undefined){ + defaultPin = Svg.teardrop_with_hole_green_svg() + } + return PointRenderingConfig.FromHtmlMulti(htmlDefs, rotation,false, defaultPin) + })).SetClass("w-full h-full block") + } + + private GetBadges(tags: UIEventSource): BaseUIElement { + if (this.iconBadges.length === 0) { + return undefined + } + return new VariableUiElement( + tags.map(tags => { + + const badgeElements = this.iconBadges.map(badge => { + + if (!badge.if.matchesProperties(tags)) { + // Doesn't match... + return undefined + } + + const htmlDefs = badge.then.GetRenderValue(tags)?.txt + const badgeElement= PointRenderingConfig.FromHtmlMulti(htmlDefs, "0", true)?.SetClass("block relative") + if(badgeElement === undefined){ + return undefined; + } + return new Combine([badgeElement]).SetStyle("width: 1.5rem").SetClass("block") + + }) + + return new Combine(badgeElements).SetClass("inline-flex h-full") + })).SetClass("absolute bottom-0 right-1/3 h-1/2 w-0") + } + + private GetLabel(tags: UIEventSource): BaseUIElement { + if (this.label === undefined) { + return undefined; + } + const self = this; + return new VariableUiElement(tags.map(tags => { + const label = self.label + ?.GetRenderValue(tags) + ?.Subs(tags) + ?.SetClass("block text-center") + return new Combine([label]).SetClass("flex flex-col items-center mt-1") + })) + + } public GenerateLeafletStyle( tags: UIEventSource, clickable: boolean - ): - { + ): + { html: BaseUIElement; iconSize: [number, number]; iconAnchor: [number, number]; popupAnchor: [number, number]; iconUrl: string; className: string; - } - { + } { function num(str, deflt = 40) { const n = Number(str); if (isNaN(n)) { @@ -122,113 +215,22 @@ export default class PointRenderingConfig extends WithContextLoader { anchorH = iconH; } - const iconUrlStatic = render(this.icon); - const self = this; - function genHtmlFromString(sourcePart: string, rotation: string): BaseUIElement { - const style = `width:100%;height:100%;transform: rotate( ${rotation} );display:block;position: absolute; top: 0; left: 0`; - let html: BaseUIElement = new FixedUiElement( - `` - ); - const match = sourcePart.match(/([a-zA-Z0-9_]*):([^;]*)/); - if (match !== null && Svg.All[match[1] + ".svg"] !== undefined) { - html = new Img( - (Svg.All[match[1] + ".svg"] as string).replace( - /#000000/g, - match[2] - ), - true - ).SetStyle(style); - } - return html; - } + const iconAndBadges = new Combine([this.GetSimpleIcon(tags), this.GetBadges(tags)]) + .SetStyle(`width: ${iconW}px; height: ${iconH}px`) + .SetClass("block relative") - const mappedHtml = tags?.map((tgs) => { - // What do you mean, 'tgs' is never read? - // It is read implicitly in the 'render' method - const iconUrl = render(self.icon); - const rotation = render(self.rotation, "0deg"); - - let htmlParts: BaseUIElement[] = []; - let sourceParts = Utils.NoNull( - iconUrl.split(";").filter((prt) => prt != "") - ); - for (const sourcePart of sourceParts) { - htmlParts.push(genHtmlFromString(sourcePart, rotation)); - } - - let badges = []; - for (const iconOverlay of self.iconOverlays) { - if (!iconOverlay.if.matchesProperties(tgs)) { - continue; - } - if (iconOverlay.badge) { - const badgeParts: BaseUIElement[] = []; - const renderValue = iconOverlay - .then - .GetRenderValue(tgs) - - if (renderValue === undefined) { - continue; - } - - const partDefs = renderValue.txt.split(";") - .filter((prt) => prt != ""); - - for (const badgePartStr of partDefs) { - badgeParts.push(genHtmlFromString(badgePartStr, "0")); - } - - const badgeCompound = new Combine(badgeParts).SetStyle( - "display:flex;position:relative;width:100%;height:100%;" - ); - - badges.push(badgeCompound); - } else { - htmlParts.push( - genHtmlFromString(iconOverlay.then.GetRenderValue(tgs).txt, "0") - ); - } - } - - if (badges.length > 0) { - const badgesComponent = new Combine(badges).SetStyle( - "display:flex;height:50%;width:100%;position:absolute;top:50%;left:50%;" - ); - htmlParts.push(badgesComponent); - } - - if (sourceParts.length == 0) { - iconH = 0; - } - try { - const label = self.label - ?.GetRenderValue(tgs) - ?.Subs(tgs) - ?.SetClass("block text-center") - ?.SetStyle("margin-top: " + (iconH + 2) + "px"); - if (label !== undefined) { - htmlParts.push( - new Combine([label]).SetClass("flex flex-col items-center") - ); - } - } catch (e) { - console.error(e, tgs); - } - return new Combine(htmlParts); - }); - return { - html: mappedHtml === undefined ? new FixedUiElement(self.icon.render.txt) : new VariableUiElement(mappedHtml), - iconSize: [iconW, iconH], - iconAnchor: [anchorW, anchorH], - popupAnchor: [0, 3 - anchorH], - iconUrl: iconUrlStatic, - className: clickable - ? "leaflet-div-icon" - : "leaflet-div-icon unclickable", + html: new Combine([iconAndBadges, this.GetLabel(tags)]).SetStyle("flex flex-col"), + iconSize: [iconW, iconH], + iconAnchor: [anchorW, anchorH], + popupAnchor: [0, 3 - anchorH], + iconUrl: undefined, + className: clickable + ? "leaflet-div-icon" + : "leaflet-div-icon unclickable", }; } - + } \ No newline at end of file diff --git a/Models/ThemeConfig/TagRenderingConfig.ts b/Models/ThemeConfig/TagRenderingConfig.ts index 42c8e60ea..a4499f46c 100644 --- a/Models/ThemeConfig/TagRenderingConfig.ts +++ b/Models/ThemeConfig/TagRenderingConfig.ts @@ -246,11 +246,6 @@ export default class TagRenderingConfig { return false; } - - public IsQuestionBoxElement(): boolean { - return this.question === null && this.condition === null; - } - /** * Gets all the render values. Will return multiple render values if 'multianswer' is enabled. * The result will equal [GetRenderValue] if not 'multiAnswer' @@ -295,7 +290,7 @@ export default class TagRenderingConfig { * Not compatible with multiAnswer - use GetRenderValueS instead in that case * @constructor */ - public GetRenderValue(tags: any): Translation { + public GetRenderValue(tags: any, defltValue: any = undefined): Translation { if (this.mappings !== undefined && !this.multiAnswer) { for (const mapping of this.mappings) { if (mapping.if === undefined) { @@ -315,7 +310,7 @@ export default class TagRenderingConfig { if (tags[this.freeform.key] !== undefined) { return this.render; } - return undefined; + return defltValue; } public ExtractImages(isIcon: boolean): Set { diff --git a/assets/layers/bicycle_library/bicycle_library.json b/assets/layers/bicycle_library/bicycle_library.json index 327f22c57..27bee2fd3 100644 --- a/assets/layers/bicycle_library/bicycle_library.json +++ b/assets/layers/bicycle_library/bicycle_library.json @@ -269,13 +269,11 @@ "iconOverlays": [ { "if": "opening_hours~*", - "then": "isOpen", - "badge": true + "then": "isOpen" }, { "if": "service:bicycle:pump=yes", - "then": "circle:#e2783d;./assets/layers/bike_repair_station/pump.svg", - "badge": true + "then": "circle:#e2783d;./assets/layers/bike_repair_station/pump.svg" } ], "width": { @@ -293,16 +291,14 @@ "icon": { "render": "pin:#22ff55;./assets/layers/bicycle_library/bicycle_library.svg" }, - "iconOverlays": [ + "iconBadges": [ { "if": "opening_hours~*", - "then": "isOpen", - "badge": true + "then": "isOpen" }, { "if": "service:bicycle:pump=yes", - "then": "circle:#e2783d;./assets/layers/bike_repair_station/pump.svg", - "badge": true + "then": "circle:#e2783d;./assets/layers/bike_repair_station/pump.svg" } ], "iconSize": { diff --git a/assets/layers/bicycle_tube_vending_machine/bicycle_tube_vending_machine.json b/assets/layers/bicycle_tube_vending_machine/bicycle_tube_vending_machine.json index 55918d203..8cb34bda5 100644 --- a/assets/layers/bicycle_tube_vending_machine/bicycle_tube_vending_machine.json +++ b/assets/layers/bicycle_tube_vending_machine/bicycle_tube_vending_machine.json @@ -48,8 +48,7 @@ "operational_status=closed" ] }, - "then": "close:#c33", - "badge": true + "then": "close:#c33" } ], "iconSize": "50,50,bottom", @@ -282,7 +281,7 @@ "icon": { "render": "pin:#ffffff;./assets/layers/bicycle_tube_vending_machine/pinIcon.svg" }, - "iconOverlays": [ + "iconBadges": [ { "if": { "or": [ @@ -290,8 +289,7 @@ "operational_status=closed" ] }, - "then": "close:#c33", - "badge": true + "then": "close:#c33" } ], "iconSize": "50,50,bottom", diff --git a/assets/layers/bike_cleaning/bike_cleaning.json b/assets/layers/bike_cleaning/bike_cleaning.json index 0d6217933..eb70066a4 100644 --- a/assets/layers/bike_cleaning/bike_cleaning.json +++ b/assets/layers/bike_cleaning/bike_cleaning.json @@ -81,8 +81,7 @@ "then": { "render": "./assets/layers/bike_cleaning/bike_cleaning_icon.svg", "roaming": true - }, - "badge": true + } } ], "titleIcons": [ @@ -168,7 +167,7 @@ "icon": { "render": "./assets/layers/bike_cleaning/bike_cleaning.svg" }, - "iconOverlays": [ + "iconBadges": [ { "if": { "and": [ @@ -179,8 +178,7 @@ "then": { "render": "./assets/layers/bike_cleaning/bike_cleaning_icon.svg", "roaming": true - }, - "badge": true + } } ], "iconSize": "50,50,bottom", diff --git a/assets/layers/bike_repair_station/bike_repair_station.json b/assets/layers/bike_repair_station/bike_repair_station.json index abf579d52..828a1968b 100644 --- a/assets/layers/bike_repair_station/bike_repair_station.json +++ b/assets/layers/bike_repair_station/bike_repair_station.json @@ -673,8 +673,7 @@ "iconOverlays": [ { "if": "operator=De Fietsambassade Gent", - "then": "./assets/themes/cyclofix/fietsambassade_gent_logo_small.svg", - "badge": true + "then": "./assets/themes/cyclofix/fietsambassade_gent_logo_small.svg" } ], "iconSize": { @@ -826,11 +825,10 @@ } ] }, - "iconOverlays": [ + "iconBadges": [ { "if": "operator=De Fietsambassade Gent", - "then": "./assets/themes/cyclofix/fietsambassade_gent_logo_small.svg", - "badge": true + "then": "./assets/themes/cyclofix/fietsambassade_gent_logo_small.svg" } ], "iconSize": { diff --git a/assets/layers/bike_shop/bike_shop.json b/assets/layers/bike_shop/bike_shop.json index 9f52b53ef..436c4604b 100644 --- a/assets/layers/bike_shop/bike_shop.json +++ b/assets/layers/bike_shop/bike_shop.json @@ -723,13 +723,11 @@ "iconOverlays": [ { "if": "opening_hours~*", - "then": "isOpen", - "badge": true + "then": "isOpen" }, { "if": "service:bicycle:pump=yes", - "then": "circle:#e2783d;./assets/layers/bike_repair_station/pump.svg", - "badge": true + "then": "circle:#e2783d;./assets/layers/bike_repair_station/pump.svg" }, { "if": { @@ -739,8 +737,7 @@ }, "then": { "render": "./assets/layers/bike_cleaning/bike_cleaning_icon.svg" - }, - "badge": true + } } ], "width": { @@ -768,16 +765,14 @@ } ] }, - "iconOverlays": [ + "iconBadges": [ { "if": "opening_hours~*", - "then": "isOpen", - "badge": true + "then": "isOpen" }, { "if": "service:bicycle:pump=yes", - "then": "circle:#e2783d;./assets/layers/bike_repair_station/pump.svg", - "badge": true + "then": "circle:#e2783d;./assets/layers/bike_repair_station/pump.svg" }, { "if": { @@ -787,8 +782,7 @@ }, "then": { "render": "./assets/layers/bike_cleaning/bike_cleaning_icon.svg" - }, - "badge": true + } } ], "iconSize": { diff --git a/assets/layers/cafe_pub/cafe_pub.json b/assets/layers/cafe_pub/cafe_pub.json index 6fcbf9d14..dbd65532a 100644 --- a/assets/layers/cafe_pub/cafe_pub.json +++ b/assets/layers/cafe_pub/cafe_pub.json @@ -28,8 +28,7 @@ "iconOverlays": [ { "if": "opening_hours~*", - "then": "isOpen", - "badge": true + "then": "isOpen" } ], "label": { @@ -215,11 +214,10 @@ } ] }, - "iconOverlays": [ + "iconBadges": [ { "if": "opening_hours~*", - "then": "isOpen", - "badge": true + "then": "isOpen" } ], "label": { diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index 116f339cd..fc738cdaa 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -1,3648 +1,3594 @@ { - "id": "charging_station", - "name": { - "en": "Charging stations", - "it": "Stazioni di ricarica", - "ja": "充電ステーション", - "nb_NO": "Ladestasjoner", - "ru": "Зарядные станции", - "zh_Hant": "充電站" - }, - "minzoom": 10, - "source": { - "osmTags": { - "or": [ - "amenity=charging_station", - "disused:amenity=charging_station", - "planned:amenity=charging_station", - "construction:amenity=charging_station" - ] - } - }, - "title": { - "render": { - "en": "Charging station", - "it": "Stazione di ricarica", - "ja": "充電ステーション", - "nb_NO": "Ladestasjon", - "ru": "Зарядная станция", - "zh_Hant": "充電站" - } - }, - "description": { - "en": "A charging station", - "it": "Una stazione di ricarica", - "ja": "充電ステーション", - "nb_NO": "En ladestasjon", - "ru": "Зарядная станция", - "zh_Hant": "充電站" - }, - "tagRenderings": [ - "images", - { - "id": "Type", - "question": { - "en": "Which vehicles are allowed to charge here?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "bicycle=yes", - "ifnot": "bicycle=no", - "then": { - "en": "bicycles can be charged here" - } - }, - { - "if": "motorcar=yes", - "ifnot": "motorcar=no", - "then": { - "en": "Cars can be charged here" - } - }, - { - "if": "scooter=yes", - "ifnot": "scooter=no", - "then": { - "en": "Scooters can be charged here" - } - }, - { - "if": "hgv=yes", - "ifnot": "hgv=no", - "then": { - "en": "Heavy good vehicles (such as trucks) can be charged here" - } - }, - { - "if": "bus=yes", - "ifnot": "bus=no", - "then": { - "en": "Buses can be charged here" - } - } - ] + "id": "charging_station", + "name": { + "en": "Charging stations", + "it": "Stazioni di ricarica", + "ja": "充電ステーション", + "nb_NO": "Ladestasjoner", + "ru": "Зарядные станции", + "zh_Hant": "充電站" }, - { - "id": "access", - "question": { - "en": "Who is allowed to use this charging station?" - }, - "render": { - "en": "Access is {access}" - }, - "freeform": { - "key": "access", - "addExtraTags": [ - "fixme=Freeform field used for access - doublecheck the value" - ] - }, - "mappings": [ - { - "if": "access=yes", - "then": "Anyone can use this charging station (payment might be needed)" - }, - { - "if": { + "minzoom": 10, + "source": { + "osmTags": { "or": [ - "access=permissive", - "access=public" + "amenity=charging_station", + "disused:amenity=charging_station", + "planned:amenity=charging_station", + "construction:amenity=charging_station" ] - }, - "then": "Anyone can use this charging station (payment might be needed)", - "hideInAnswer": true - }, - { - "if": "access=customers", - "then": "Only customers of the place this station belongs to can use this charging station
E.g. a charging station operated by hotel which is only usable by their guests " - }, - { - "if": "access=private", - "then": "Not accessible to the general public (e.g. only accessible to the owners, employees, ...)" } - ] }, - { - "id": "capacity", - "render": { - "en": "{capacity} vehicles can be charged here at the same time", - "nl": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden" - }, - "question": { - "en": "How much vehicles can be charged here at the same time?", - "nl": "Hoeveel voertuigen kunnen hier opgeladen worden?" - }, - "freeform": { - "key": "capacity", - "type": "pnat" - } - }, - { - "id": "Available_charging_stations (generated)", - "question": { - "en": "Which charging stations are available here?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "socket:schuko=1", - "ifnot": "socket:schuko=", - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "hideInAnswer": { - "or": [ - "_country!=be", - "_country!=fr", - "_country!=ma", - "_country!=tn", - "_country!=pl", - "_country!=cs", - "_country!=sk", - "_country!=mo" - ] - } - }, - { - "if": { - "and": [ - "socket:schuko~*", - "socket:schuko!=1" - ] - }, - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:typee=1", - "ifnot": "socket:typee=", - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" - } - }, - { - "if": { - "and": [ - "socket:typee~*", - "socket:typee!=1" - ] - }, - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:chademo=1", - "ifnot": "socket:chademo=", - "then": { - "en": "
Chademo
", - "nl": "
Chademo
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:chademo~*", - "socket:chademo!=1" - ] - }, - "then": { - "en": "
Chademo
", - "nl": "
Chademo
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1_cable=1", - "ifnot": "socket:type1_cable=", - "then": { - "en": "
Type 1 with cable (J1772)
", - "nl": "
Type 1 met kabel (J1772)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=1" - ] - }, - "then": { - "en": "
Type 1 with cable (J1772)
", - "nl": "
Type 1 met kabel (J1772)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1=1", - "ifnot": "socket:type1=", - "then": { - "en": "
Type 1 without cable (J1772)
", - "nl": "
Type 1 zonder kabel (J1772)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type1~*", - "socket:type1!=1" - ] - }, - "then": { - "en": "
Type 1 without cable (J1772)
", - "nl": "
Type 1 zonder kabel (J1772)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1_combo=1", - "ifnot": "socket:type1_combo=", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=1" - ] - }, - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_supercharger=1", - "ifnot": "socket:tesla_supercharger=", - "then": { - "en": "
Tesla Supercharger
", - "nl": "
Tesla Supercharger
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=1" - ] - }, - "then": { - "en": "
Tesla Supercharger
", - "nl": "
Tesla Supercharger
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type2=1", - "ifnot": "socket:type2=", - "then": { - "en": "
Type 2 (mennekes)
", - "nl": "
Type 2 (mennekes)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type2~*", - "socket:type2!=1" - ] - }, - "then": { - "en": "
Type 2 (mennekes)
", - "nl": "
Type 2 (mennekes)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type2_combo=1", - "ifnot": "socket:type2_combo=", - "then": { - "en": "
Type 2 CCS (mennekes)
", - "nl": "
Type 2 CCS (mennekes)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=1" - ] - }, - "then": { - "en": "
Type 2 CCS (mennekes)
", - "nl": "
Type 2 CCS (mennekes)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type2_cable=1", - "ifnot": "socket:type2_cable=", - "then": { - "en": "
Type 2 with cable (mennekes)
", - "nl": "
Type 2 met kabel (J1772)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=1" - ] - }, - "then": { - "en": "
Type 2 with cable (mennekes)
", - "nl": "
Type 2 met kabel (J1772)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_supercharger_ccs=1", - "ifnot": "socket:tesla_supercharger_ccs=", - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=1" - ] - }, - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_destination=1", - "ifnot": "socket:tesla_destination=", - "then": { - "en": "
Tesla Supercharger (destination)
", - "nl": "
Tesla Supercharger (destination)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - }, - { - "or": [ - "_country!=us" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=1" - ] - }, - "then": { - "en": "
Tesla Supercharger (destination)
", - "nl": "
Tesla Supercharger (destination)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_destination=1", - "ifnot": "socket:tesla_destination=", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - }, - { - "or": [ - "_country=us" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=1" - ] - }, - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:USB-A=1", - "ifnot": "socket:USB-A=", - "then": { - "en": "
USB to charge phones and small electronics
", - "nl": "
USB om GSMs en kleine electronica op te laden
" - } - }, - { - "if": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=1" - ] - }, - "then": { - "en": "
USB to charge phones and small electronics
", - "nl": "
USB om GSMs en kleine electronica op te laden
" - }, - "hideInAnswer": true - }, - { - "if": "socket:bosch_3pin=1", - "ifnot": "socket:bosch_3pin=", - "then": { - "en": "
Bosch Active Connect with 3 pins and cable
", - "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "bicycle=no" - ] - }, - { - "and": [ - { - "or": [ - "car=yes", - "motorcar=yes", - "hgv=yes", - "bus=yes" - ] - }, - "bicycle!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:bosch_3pin~*", - "socket:bosch_3pin!=1" - ] - }, - "then": { - "en": "
Bosch Active Connect with 3 pins and cable
", - "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "hideInAnswer": true - }, - { - "if": "socket:bosch_5pin=1", - "ifnot": "socket:bosch_5pin=", - "then": { - "en": "
Bosch Active Connect with 5 pins and cable
", - "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "bicycle=no" - ] - }, - { - "and": [ - { - "or": [ - "car=yes", - "motorcar=yes", - "hgv=yes", - "bus=yes" - ] - }, - "bicycle!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:bosch_5pin~*", - "socket:bosch_5pin!=1" - ] - }, - "then": { - "en": "
Bosch Active Connect with 5 pins and cable
", - "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
" - }, - "hideInAnswer": true + "title": { + "render": { + "en": "Charging station", + "it": "Stazione di ricarica", + "ja": "充電ステーション", + "nb_NO": "Ladestasjon", + "ru": "Зарядная станция", + "zh_Hant": "充電站" } - ] }, - { - "id": "plugs-0", - "question": { - "en": "How much plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
are available here?", - "nl": "Hoeveel stekkers van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:schuko} plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
available here", - "nl": "Hier zijn {socket:schuko} stekkers van het type
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "freeform": { - "key": "socket:schuko", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } + "description": { + "en": "A charging station", + "it": "Una stazione di ricarica", + "ja": "充電ステーション", + "nb_NO": "En ladestasjon", + "ru": "Зарядная станция", + "zh_Hant": "充電站" }, - { - "id": "voltage-0", - "question": { - "en": "What voltage do the plugs with
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", - "nl": "Welke spanning levert de stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "render": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs {socket:schuko:voltage} volt", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft een spanning van {socket:schuko:voltage} volt" - }, - "freeform": { - "key": "socket:schuko:voltage", - "type": "pfloat" - }, - "mappings": [ + "tagRenderings": [ + "images", { - "if": "socket:socket:schuko:voltage=230 V", - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs 230 volt", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft een spanning van 230 volt" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "current-0", - "question": { - "en": "What current do the plugs with
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", - "nl": "Welke stroom levert de stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
?" - }, - "render": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most {socket:schuko:current}A", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een stroom van maximaal {socket:schuko:current}A" - }, - "freeform": { - "key": "socket:schuko:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:current=16 A", - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 16 A", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een stroom van maximaal 16 A" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "power-output-0", - "question": { - "en": "What power output does a single plug of type
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
?" - }, - "render": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most {socket:schuko:output}", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een vermogen van maximaal {socket:schuko:output}" - }, - "freeform": { - "key": "socket:schuko:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:output=3.6 kw", - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 3.6 kw", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een vermogen van maximaal 3.6 kw" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "plugs-1", - "question": { - "en": "How much plugs of type
European wall plug with ground pin (CEE7/4 type E)
are available here?", - "nl": "Hoeveel stekkers van type
Europese stekker met aardingspin (CEE7/4 type E)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:typee} plugs of type
European wall plug with ground pin (CEE7/4 type E)
available here", - "nl": "Hier zijn {socket:typee} stekkers van het type
Europese stekker met aardingspin (CEE7/4 type E)
" - }, - "freeform": { - "key": "socket:typee", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "voltage-1", - "question": { - "en": "What voltage do the plugs with
European wall plug with ground pin (CEE7/4 type E)
offer?", - "nl": "Welke spanning levert de stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
" - }, - "render": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs {socket:typee:voltage} volt", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
heeft een spanning van {socket:typee:voltage} volt" - }, - "freeform": { - "key": "socket:typee:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:voltage=230 V", - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs 230 volt", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
heeft een spanning van 230 volt" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "current-1", - "question": { - "en": "What current do the plugs with
European wall plug with ground pin (CEE7/4 type E)
offer?", - "nl": "Welke stroom levert de stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
?" - }, - "render": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most {socket:typee:current}A", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een stroom van maximaal {socket:typee:current}A" - }, - "freeform": { - "key": "socket:typee:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:current=16 A", - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 16 A", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een stroom van maximaal 16 A" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "power-output-1", - "question": { - "en": "What power output does a single plug of type
European wall plug with ground pin (CEE7/4 type E)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
?" - }, - "render": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most {socket:typee:output}", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal {socket:typee:output}" - }, - "freeform": { - "key": "socket:typee:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:output=3 kw", - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 3 kw", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal 3 kw" - } - }, - { - "if": "socket:socket:typee:output=22 kw", - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 22 kw", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "plugs-2", - "question": { - "en": "How much plugs of type
Chademo
are available here?", - "nl": "Hoeveel stekkers van type
Chademo
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:chademo} plugs of type
Chademo
available here", - "nl": "Hier zijn {socket:chademo} stekkers van het type
Chademo
" - }, - "freeform": { - "key": "socket:chademo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "voltage-2", - "question": { - "en": "What voltage do the plugs with
Chademo
offer?", - "nl": "Welke spanning levert de stekker van type
Chademo
" - }, - "render": { - "en": "
Chademo
outputs {socket:chademo:voltage} volt", - "nl": "
Chademo
heeft een spanning van {socket:chademo:voltage} volt" - }, - "freeform": { - "key": "socket:chademo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:voltage=500 V", - "then": { - "en": "
Chademo
outputs 500 volt", - "nl": "
Chademo
heeft een spanning van 500 volt" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "current-2", - "question": { - "en": "What current do the plugs with
Chademo
offer?", - "nl": "Welke stroom levert de stekker van type
Chademo
?" - }, - "render": { - "en": "
Chademo
outputs at most {socket:chademo:current}A", - "nl": "
Chademo
levert een stroom van maximaal {socket:chademo:current}A" - }, - "freeform": { - "key": "socket:chademo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:current=120 A", - "then": { - "en": "
Chademo
outputs at most 120 A", - "nl": "
Chademo
levert een stroom van maximaal 120 A" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "power-output-2", - "question": { - "en": "What power output does a single plug of type
Chademo
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Chademo
?" - }, - "render": { - "en": "
Chademo
outputs at most {socket:chademo:output}", - "nl": "
Chademo
levert een vermogen van maximaal {socket:chademo:output}" - }, - "freeform": { - "key": "socket:chademo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:output=50 kw", - "then": { - "en": "
Chademo
outputs at most 50 kw", - "nl": "
Chademo
levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "plugs-3", - "question": { - "en": "How much plugs of type
Type 1 with cable (J1772)
are available here?", - "nl": "Hoeveel stekkers van type
Type 1 met kabel (J1772)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:type1_cable} plugs of type
Type 1 with cable (J1772)
available here", - "nl": "Hier zijn {socket:type1_cable} stekkers van het type
Type 1 met kabel (J1772)
" - }, - "freeform": { - "key": "socket:type1_cable", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "voltage-3", - "question": { - "en": "What voltage do the plugs with
Type 1 with cable (J1772)
offer?", - "nl": "Welke spanning levert de stekker van type
Type 1 met kabel (J1772)
" - }, - "render": { - "en": "
Type 1 with cable (J1772)
outputs {socket:type1_cable:voltage} volt", - "nl": "
Type 1 met kabel (J1772)
heeft een spanning van {socket:type1_cable:voltage} volt" - }, - "freeform": { - "key": "socket:type1_cable:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:voltage=200 V", - "then": { - "en": "
Type 1 with cable (J1772)
outputs 200 volt", - "nl": "
Type 1 met kabel (J1772)
heeft een spanning van 200 volt" - } - }, - { - "if": "socket:socket:type1_cable:voltage=240 V", - "then": { - "en": "
Type 1 with cable (J1772)
outputs 240 volt", - "nl": "
Type 1 met kabel (J1772)
heeft een spanning van 240 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "current-3", - "question": { - "en": "What current do the plugs with
Type 1 with cable (J1772)
offer?", - "nl": "Welke stroom levert de stekker van type
Type 1 met kabel (J1772)
?" - }, - "render": { - "en": "
Type 1 with cable (J1772)
outputs at most {socket:type1_cable:current}A", - "nl": "
Type 1 met kabel (J1772)
levert een stroom van maximaal {socket:type1_cable:current}A" - }, - "freeform": { - "key": "socket:type1_cable:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:current=32 A", - "then": { - "en": "
Type 1 with cable (J1772)
outputs at most 32 A", - "nl": "
Type 1 met kabel (J1772)
levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "power-output-3", - "question": { - "en": "What power output does a single plug of type
Type 1 with cable (J1772)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Type 1 met kabel (J1772)
?" - }, - "render": { - "en": "
Type 1 with cable (J1772)
outputs at most {socket:type1_cable:output}", - "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal {socket:type1_cable:output}" - }, - "freeform": { - "key": "socket:type1_cable:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:output=3.7 kw", - "then": { - "en": "
Type 1 with cable (J1772)
outputs at most 3.7 kw", - "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal 3.7 kw" - } - }, - { - "if": "socket:socket:type1_cable:output=7 kw", - "then": { - "en": "
Type 1 with cable (J1772)
outputs at most 7 kw", - "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal 7 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "plugs-4", - "question": { - "en": "How much plugs of type
Type 1 without cable (J1772)
are available here?", - "nl": "Hoeveel stekkers van type
Type 1 zonder kabel (J1772)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:type1} plugs of type
Type 1 without cable (J1772)
available here", - "nl": "Hier zijn {socket:type1} stekkers van het type
Type 1 zonder kabel (J1772)
" - }, - "freeform": { - "key": "socket:type1", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "voltage-4", - "question": { - "en": "What voltage do the plugs with
Type 1 without cable (J1772)
offer?", - "nl": "Welke spanning levert de stekker van type
Type 1 zonder kabel (J1772)
" - }, - "render": { - "en": "
Type 1 without cable (J1772)
outputs {socket:type1:voltage} volt", - "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van {socket:type1:voltage} volt" - }, - "freeform": { - "key": "socket:type1:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:voltage=200 V", - "then": { - "en": "
Type 1 without cable (J1772)
outputs 200 volt", - "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van 200 volt" - } - }, - { - "if": "socket:socket:type1:voltage=240 V", - "then": { - "en": "
Type 1 without cable (J1772)
outputs 240 volt", - "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van 240 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "current-4", - "question": { - "en": "What current do the plugs with
Type 1 without cable (J1772)
offer?", - "nl": "Welke stroom levert de stekker van type
Type 1 zonder kabel (J1772)
?" - }, - "render": { - "en": "
Type 1 without cable (J1772)
outputs at most {socket:type1:current}A", - "nl": "
Type 1 zonder kabel (J1772)
levert een stroom van maximaal {socket:type1:current}A" - }, - "freeform": { - "key": "socket:type1:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:current=32 A", - "then": { - "en": "
Type 1 without cable (J1772)
outputs at most 32 A", - "nl": "
Type 1 zonder kabel (J1772)
levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "power-output-4", - "question": { - "en": "What power output does a single plug of type
Type 1 without cable (J1772)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Type 1 zonder kabel (J1772)
?" - }, - "render": { - "en": "
Type 1 without cable (J1772)
outputs at most {socket:type1:output}", - "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal {socket:type1:output}" - }, - "freeform": { - "key": "socket:type1:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:output=3.7 kw", - "then": { - "en": "
Type 1 without cable (J1772)
outputs at most 3.7 kw", - "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 3.7 kw" - } - }, - { - "if": "socket:socket:type1:output=6.6 kw", - "then": { - "en": "
Type 1 without cable (J1772)
outputs at most 6.6 kw", - "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 6.6 kw" - } - }, - { - "if": "socket:socket:type1:output=7 kw", - "then": { - "en": "
Type 1 without cable (J1772)
outputs at most 7 kw", - "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 7 kw" - } - }, - { - "if": "socket:socket:type1:output=7.2 kw", - "then": { - "en": "
Type 1 without cable (J1772)
outputs at most 7.2 kw", - "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 7.2 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "plugs-5", - "question": { - "en": "How much plugs of type
Type 1 CCS (aka Type 1 Combo)
are available here?", - "nl": "Hoeveel stekkers van type
Type 1 CCS (ook gekend als Type 1 Combo)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:type1_combo} plugs of type
Type 1 CCS (aka Type 1 Combo)
available here", - "nl": "Hier zijn {socket:type1_combo} stekkers van het type
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "freeform": { - "key": "socket:type1_combo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "voltage-5", - "question": { - "en": "What voltage do the plugs with
Type 1 CCS (aka Type 1 Combo)
offer?", - "nl": "Welke spanning levert de stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "render": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs {socket:type1_combo:voltage} volt", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van {socket:type1_combo:voltage} volt" - }, - "freeform": { - "key": "socket:type1_combo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:voltage=400 V", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs 400 volt", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van 400 volt" - } - }, - { - "if": "socket:socket:type1_combo:voltage=1000 V", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs 1000 volt", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van 1000 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "current-5", - "question": { - "en": "What current do the plugs with
Type 1 CCS (aka Type 1 Combo)
offer?", - "nl": "Welke stroom levert de stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
?" - }, - "render": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most {socket:type1_combo:current}A", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal {socket:type1_combo:current}A" - }, - "freeform": { - "key": "socket:type1_combo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:current=50 A", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 A", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal 50 A" - } - }, - { - "if": "socket:socket:type1_combo:current=125 A", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 125 A", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal 125 A" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "power-output-5", - "question": { - "en": "What power output does a single plug of type
Type 1 CCS (aka Type 1 Combo)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
?" - }, - "render": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most {socket:type1_combo:output}", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal {socket:type1_combo:output}" - }, - "freeform": { - "key": "socket:type1_combo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:output=50 kw", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 kw", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 50 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=62.5 kw", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 62.5 kw", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 62.5 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=150 kw", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 150 kw", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=350 kw", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 350 kw", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 350 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "plugs-6", - "question": { - "en": "How much plugs of type
Tesla Supercharger
are available here?", - "nl": "Hoeveel stekkers van type
Tesla Supercharger
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:tesla_supercharger} plugs of type
Tesla Supercharger
available here", - "nl": "Hier zijn {socket:tesla_supercharger} stekkers van het type
Tesla Supercharger
" - }, - "freeform": { - "key": "socket:tesla_supercharger", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "voltage-6", - "question": { - "en": "What voltage do the plugs with
Tesla Supercharger
offer?", - "nl": "Welke spanning levert de stekker van type
Tesla Supercharger
" - }, - "render": { - "en": "
Tesla Supercharger
outputs {socket:tesla_supercharger:voltage} volt", - "nl": "
Tesla Supercharger
heeft een spanning van {socket:tesla_supercharger:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_supercharger:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:voltage=480 V", - "then": { - "en": "
Tesla Supercharger
outputs 480 volt", - "nl": "
Tesla Supercharger
heeft een spanning van 480 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "current-6", - "question": { - "en": "What current do the plugs with
Tesla Supercharger
offer?", - "nl": "Welke stroom levert de stekker van type
Tesla Supercharger
?" - }, - "render": { - "en": "
Tesla Supercharger
outputs at most {socket:tesla_supercharger:current}A", - "nl": "
Tesla Supercharger
levert een stroom van maximaal {socket:tesla_supercharger:current}A" - }, - "freeform": { - "key": "socket:tesla_supercharger:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:current=125 A", - "then": { - "en": "
Tesla Supercharger
outputs at most 125 A", - "nl": "
Tesla Supercharger
levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_supercharger:current=350 A", - "then": { - "en": "
Tesla Supercharger
outputs at most 350 A", - "nl": "
Tesla Supercharger
levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "power-output-6", - "question": { - "en": "What power output does a single plug of type
Tesla Supercharger
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger
?" - }, - "render": { - "en": "
Tesla Supercharger
outputs at most {socket:tesla_supercharger:output}", - "nl": "
Tesla Supercharger
levert een vermogen van maximaal {socket:tesla_supercharger:output}" - }, - "freeform": { - "key": "socket:tesla_supercharger:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:output=120 kw", - "then": { - "en": "
Tesla Supercharger
outputs at most 120 kw", - "nl": "
Tesla Supercharger
levert een vermogen van maximaal 120 kw" - } - }, - { - "if": "socket:socket:tesla_supercharger:output=150 kw", - "then": { - "en": "
Tesla Supercharger
outputs at most 150 kw", - "nl": "
Tesla Supercharger
levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:tesla_supercharger:output=250 kw", - "then": { - "en": "
Tesla Supercharger
outputs at most 250 kw", - "nl": "
Tesla Supercharger
levert een vermogen van maximaal 250 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "plugs-7", - "question": { - "en": "How much plugs of type
Type 2 (mennekes)
are available here?", - "nl": "Hoeveel stekkers van type
Type 2 (mennekes)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:type2} plugs of type
Type 2 (mennekes)
available here", - "nl": "Hier zijn {socket:type2} stekkers van het type
Type 2 (mennekes)
" - }, - "freeform": { - "key": "socket:type2", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "voltage-7", - "question": { - "en": "What voltage do the plugs with
Type 2 (mennekes)
offer?", - "nl": "Welke spanning levert de stekker van type
Type 2 (mennekes)
" - }, - "render": { - "en": "
Type 2 (mennekes)
outputs {socket:type2:voltage} volt", - "nl": "
Type 2 (mennekes)
heeft een spanning van {socket:type2:voltage} volt" - }, - "freeform": { - "key": "socket:type2:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:voltage=230 V", - "then": { - "en": "
Type 2 (mennekes)
outputs 230 volt", - "nl": "
Type 2 (mennekes)
heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:type2:voltage=400 V", - "then": { - "en": "
Type 2 (mennekes)
outputs 400 volt", - "nl": "
Type 2 (mennekes)
heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "current-7", - "question": { - "en": "What current do the plugs with
Type 2 (mennekes)
offer?", - "nl": "Welke stroom levert de stekker van type
Type 2 (mennekes)
?" - }, - "render": { - "en": "
Type 2 (mennekes)
outputs at most {socket:type2:current}A", - "nl": "
Type 2 (mennekes)
levert een stroom van maximaal {socket:type2:current}A" - }, - "freeform": { - "key": "socket:type2:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:current=16 A", - "then": { - "en": "
Type 2 (mennekes)
outputs at most 16 A", - "nl": "
Type 2 (mennekes)
levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:type2:current=32 A", - "then": { - "en": "
Type 2 (mennekes)
outputs at most 32 A", - "nl": "
Type 2 (mennekes)
levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "power-output-7", - "question": { - "en": "What power output does a single plug of type
Type 2 (mennekes)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Type 2 (mennekes)
?" - }, - "render": { - "en": "
Type 2 (mennekes)
outputs at most {socket:type2:output}", - "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal {socket:type2:output}" - }, - "freeform": { - "key": "socket:type2:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:output=11 kw", - "then": { - "en": "
Type 2 (mennekes)
outputs at most 11 kw", - "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:type2:output=22 kw", - "then": { - "en": "
Type 2 (mennekes)
outputs at most 22 kw", - "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "plugs-8", - "question": { - "en": "How much plugs of type
Type 2 CCS (mennekes)
are available here?", - "nl": "Hoeveel stekkers van type
Type 2 CCS (mennekes)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:type2_combo} plugs of type
Type 2 CCS (mennekes)
available here", - "nl": "Hier zijn {socket:type2_combo} stekkers van het type
Type 2 CCS (mennekes)
" - }, - "freeform": { - "key": "socket:type2_combo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "voltage-8", - "question": { - "en": "What voltage do the plugs with
Type 2 CCS (mennekes)
offer?", - "nl": "Welke spanning levert de stekker van type
Type 2 CCS (mennekes)
" - }, - "render": { - "en": "
Type 2 CCS (mennekes)
outputs {socket:type2_combo:voltage} volt", - "nl": "
Type 2 CCS (mennekes)
heeft een spanning van {socket:type2_combo:voltage} volt" - }, - "freeform": { - "key": "socket:type2_combo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:voltage=500 V", - "then": { - "en": "
Type 2 CCS (mennekes)
outputs 500 volt", - "nl": "
Type 2 CCS (mennekes)
heeft een spanning van 500 volt" - } - }, - { - "if": "socket:socket:type2_combo:voltage=920 V", - "then": { - "en": "
Type 2 CCS (mennekes)
outputs 920 volt", - "nl": "
Type 2 CCS (mennekes)
heeft een spanning van 920 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "current-8", - "question": { - "en": "What current do the plugs with
Type 2 CCS (mennekes)
offer?", - "nl": "Welke stroom levert de stekker van type
Type 2 CCS (mennekes)
?" - }, - "render": { - "en": "
Type 2 CCS (mennekes)
outputs at most {socket:type2_combo:current}A", - "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal {socket:type2_combo:current}A" - }, - "freeform": { - "key": "socket:type2_combo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:current=125 A", - "then": { - "en": "
Type 2 CCS (mennekes)
outputs at most 125 A", - "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:type2_combo:current=350 A", - "then": { - "en": "
Type 2 CCS (mennekes)
outputs at most 350 A", - "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "power-output-8", - "question": { - "en": "What power output does a single plug of type
Type 2 CCS (mennekes)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Type 2 CCS (mennekes)
?" - }, - "render": { - "en": "
Type 2 CCS (mennekes)
outputs at most {socket:type2_combo:output}", - "nl": "
Type 2 CCS (mennekes)
levert een vermogen van maximaal {socket:type2_combo:output}" - }, - "freeform": { - "key": "socket:type2_combo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:output=50 kw", - "then": { - "en": "
Type 2 CCS (mennekes)
outputs at most 50 kw", - "nl": "
Type 2 CCS (mennekes)
levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "plugs-9", - "question": { - "en": "How much plugs of type
Type 2 with cable (mennekes)
are available here?", - "nl": "Hoeveel stekkers van type
Type 2 met kabel (J1772)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:type2_cable} plugs of type
Type 2 with cable (mennekes)
available here", - "nl": "Hier zijn {socket:type2_cable} stekkers van het type
Type 2 met kabel (J1772)
" - }, - "freeform": { - "key": "socket:type2_cable", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "voltage-9", - "question": { - "en": "What voltage do the plugs with
Type 2 with cable (mennekes)
offer?", - "nl": "Welke spanning levert de stekker van type
Type 2 met kabel (J1772)
" - }, - "render": { - "en": "
Type 2 with cable (mennekes)
outputs {socket:type2_cable:voltage} volt", - "nl": "
Type 2 met kabel (J1772)
heeft een spanning van {socket:type2_cable:voltage} volt" - }, - "freeform": { - "key": "socket:type2_cable:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:voltage=230 V", - "then": { - "en": "
Type 2 with cable (mennekes)
outputs 230 volt", - "nl": "
Type 2 met kabel (J1772)
heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:type2_cable:voltage=400 V", - "then": { - "en": "
Type 2 with cable (mennekes)
outputs 400 volt", - "nl": "
Type 2 met kabel (J1772)
heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "current-9", - "question": { - "en": "What current do the plugs with
Type 2 with cable (mennekes)
offer?", - "nl": "Welke stroom levert de stekker van type
Type 2 met kabel (J1772)
?" - }, - "render": { - "en": "
Type 2 with cable (mennekes)
outputs at most {socket:type2_cable:current}A", - "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal {socket:type2_cable:current}A" - }, - "freeform": { - "key": "socket:type2_cable:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:current=16 A", - "then": { - "en": "
Type 2 with cable (mennekes)
outputs at most 16 A", - "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:type2_cable:current=32 A", - "then": { - "en": "
Type 2 with cable (mennekes)
outputs at most 32 A", - "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "power-output-9", - "question": { - "en": "What power output does a single plug of type
Type 2 with cable (mennekes)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Type 2 met kabel (J1772)
?" - }, - "render": { - "en": "
Type 2 with cable (mennekes)
outputs at most {socket:type2_cable:output}", - "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal {socket:type2_cable:output}" - }, - "freeform": { - "key": "socket:type2_cable:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:output=11 kw", - "then": { - "en": "
Type 2 with cable (mennekes)
outputs at most 11 kw", - "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:type2_cable:output=22 kw", - "then": { - "en": "
Type 2 with cable (mennekes)
outputs at most 22 kw", - "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "plugs-10", - "question": { - "en": "How much plugs of type
Tesla Supercharger CCS (a branded type2_css)
are available here?", - "nl": "Hoeveel stekkers van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:tesla_supercharger_ccs} plugs of type
Tesla Supercharger CCS (a branded type2_css)
available here", - "nl": "Hier zijn {socket:tesla_supercharger_ccs} stekkers van het type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "voltage-10", - "question": { - "en": "What voltage do the plugs with
Tesla Supercharger CCS (a branded type2_css)
offer?", - "nl": "Welke spanning levert de stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "render": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs {socket:tesla_supercharger_ccs:voltage} volt", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:voltage=500 V", - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs 500 volt", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van 500 volt" - } - }, - { - "if": "socket:socket:tesla_supercharger_ccs:voltage=920 V", - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs 920 volt", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van 920 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "current-10", - "question": { - "en": "What current do the plugs with
Tesla Supercharger CCS (a branded type2_css)
offer?", - "nl": "Welke stroom levert de stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
?" - }, - "render": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most {socket:tesla_supercharger_ccs:current}A", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:current=125 A", - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 125 A", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_supercharger_ccs:current=350 A", - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 350 A", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "power-output-10", - "question": { - "en": "What power output does a single plug of type
Tesla Supercharger CCS (a branded type2_css)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
?" - }, - "render": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most {socket:tesla_supercharger_ccs:output}", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:output=50 kw", - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 50 kw", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "plugs-11", - "question": { - "en": "How much plugs of type
Tesla Supercharger (destination)
are available here?", - "nl": "Hoeveel stekkers van type
Tesla Supercharger (destination)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:tesla_destination} plugs of type
Tesla Supercharger (destination)
available here", - "nl": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla Supercharger (destination)
" - }, - "freeform": { - "key": "socket:tesla_destination", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "voltage-11", - "question": { - "en": "What voltage do the plugs with
Tesla Supercharger (destination)
offer?", - "nl": "Welke spanning levert de stekker van type
Tesla Supercharger (destination)
" - }, - "render": { - "en": "
Tesla Supercharger (destination)
outputs {socket:tesla_destination:voltage} volt", - "nl": "
Tesla Supercharger (destination)
heeft een spanning van {socket:tesla_destination:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_destination:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:voltage=480 V", - "then": { - "en": "
Tesla Supercharger (destination)
outputs 480 volt", - "nl": "
Tesla Supercharger (destination)
heeft een spanning van 480 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "current-11", - "question": { - "en": "What current do the plugs with
Tesla Supercharger (destination)
offer?", - "nl": "Welke stroom levert de stekker van type
Tesla Supercharger (destination)
?" - }, - "render": { - "en": "
Tesla Supercharger (destination)
outputs at most {socket:tesla_destination:current}A", - "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal {socket:tesla_destination:current}A" - }, - "freeform": { - "key": "socket:tesla_destination:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:current=125 A", - "then": { - "en": "
Tesla Supercharger (destination)
outputs at most 125 A", - "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_destination:current=350 A", - "then": { - "en": "
Tesla Supercharger (destination)
outputs at most 350 A", - "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "power-output-11", - "question": { - "en": "What power output does a single plug of type
Tesla Supercharger (destination)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger (destination)
?" - }, - "render": { - "en": "
Tesla Supercharger (destination)
outputs at most {socket:tesla_destination:output}", - "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal {socket:tesla_destination:output}" - }, - "freeform": { - "key": "socket:tesla_destination:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:output=120 kw", - "then": { - "en": "
Tesla Supercharger (destination)
outputs at most 120 kw", - "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 120 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=150 kw", - "then": { - "en": "
Tesla Supercharger (destination)
outputs at most 150 kw", - "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=250 kw", - "then": { - "en": "
Tesla Supercharger (destination)
outputs at most 250 kw", - "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 250 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "plugs-12", - "question": { - "en": "How much plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
are available here?", - "nl": "Hoeveel stekkers van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:tesla_destination} plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
available here", - "nl": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "freeform": { - "key": "socket:tesla_destination", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "voltage-12", - "question": { - "en": "What voltage do the plugs with
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", - "nl": "Welke spanning levert de stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "render": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs {socket:tesla_destination:voltage} volt", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van {socket:tesla_destination:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_destination:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:voltage=230 V", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 230 volt", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:tesla_destination:voltage=400 V", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 400 volt", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "current-12", - "question": { - "en": "What current do the plugs with
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", - "nl": "Welke stroom levert de stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
?" - }, - "render": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most {socket:tesla_destination:current}A", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal {socket:tesla_destination:current}A" - }, - "freeform": { - "key": "socket:tesla_destination:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:current=16 A", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 16 A", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:tesla_destination:current=32 A", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 32 A", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "power-output-12", - "question": { - "en": "What power output does a single plug of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
?" - }, - "render": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most {socket:tesla_destination:output}", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal {socket:tesla_destination:output}" - }, - "freeform": { - "key": "socket:tesla_destination:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:output=11 kw", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 11 kw", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=22 kw", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 22 kw", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "plugs-13", - "question": { - "en": "How much plugs of type
USB to charge phones and small electronics
are available here?", - "nl": "Hoeveel stekkers van type
USB om GSMs en kleine electronica op te laden
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:USB-A} plugs of type
USB to charge phones and small electronics
available here", - "nl": "Hier zijn {socket:USB-A} stekkers van het type
USB om GSMs en kleine electronica op te laden
" - }, - "freeform": { - "key": "socket:USB-A", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "voltage-13", - "question": { - "en": "What voltage do the plugs with
USB to charge phones and small electronics
offer?", - "nl": "Welke spanning levert de stekker van type
USB om GSMs en kleine electronica op te laden
" - }, - "render": { - "en": "
USB to charge phones and small electronics
outputs {socket:USB-A:voltage} volt", - "nl": "
USB om GSMs en kleine electronica op te laden
heeft een spanning van {socket:USB-A:voltage} volt" - }, - "freeform": { - "key": "socket:USB-A:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:USB-A:voltage=5 V", - "then": { - "en": "
USB to charge phones and small electronics
outputs 5 volt", - "nl": "
USB om GSMs en kleine electronica op te laden
heeft een spanning van 5 volt" - } - } - ], - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "current-13", - "question": { - "en": "What current do the plugs with
USB to charge phones and small electronics
offer?", - "nl": "Welke stroom levert de stekker van type
USB om GSMs en kleine electronica op te laden
?" - }, - "render": { - "en": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:current}A", - "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal {socket:USB-A:current}A" - }, - "freeform": { - "key": "socket:USB-A:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:USB-A:current=1 A", - "then": { - "en": "
USB to charge phones and small electronics
outputs at most 1 A", - "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal 1 A" - } - }, - { - "if": "socket:socket:USB-A:current=2 A", - "then": { - "en": "
USB to charge phones and small electronics
outputs at most 2 A", - "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal 2 A" - } - } - ], - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "power-output-13", - "question": { - "en": "What power output does a single plug of type
USB to charge phones and small electronics
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
USB om GSMs en kleine electronica op te laden
?" - }, - "render": { - "en": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:output}", - "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal {socket:USB-A:output}" - }, - "freeform": { - "key": "socket:USB-A:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:USB-A:output=5w", - "then": { - "en": "
USB to charge phones and small electronics
outputs at most 5w", - "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal 5w" - } - }, - { - "if": "socket:socket:USB-A:output=10w", - "then": { - "en": "
USB to charge phones and small electronics
outputs at most 10w", - "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal 10w" - } - } - ], - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "plugs-14", - "question": { - "en": "How much plugs of type
Bosch Active Connect with 3 pins and cable
are available here?", - "nl": "Hoeveel stekkers van type
Bosch Active Connect met 3 pinnen aan een kabel
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:bosch_3pin} plugs of type
Bosch Active Connect with 3 pins and cable
available here", - "nl": "Hier zijn {socket:bosch_3pin} stekkers van het type
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "freeform": { - "key": "socket:bosch_3pin", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:bosch_3pin~*", - "socket:bosch_3pin!=0" - ] - } - }, - { - "id": "voltage-14", - "question": { - "en": "What voltage do the plugs with
Bosch Active Connect with 3 pins and cable
offer?", - "nl": "Welke spanning levert de stekker van type
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "render": { - "en": "
Bosch Active Connect with 3 pins and cable
outputs {socket:bosch_3pin:voltage} volt", - "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
heeft een spanning van {socket:bosch_3pin:voltage} volt" - }, - "freeform": { - "key": "socket:bosch_3pin:voltage", - "type": "pfloat" - }, - "mappings": [], - "condition": { - "and": [ - "socket:bosch_3pin~*", - "socket:bosch_3pin!=0" - ] - } - }, - { - "id": "current-14", - "question": { - "en": "What current do the plugs with
Bosch Active Connect with 3 pins and cable
offer?", - "nl": "Welke stroom levert de stekker van type
Bosch Active Connect met 3 pinnen aan een kabel
?" - }, - "render": { - "en": "
Bosch Active Connect with 3 pins and cable
outputs at most {socket:bosch_3pin:current}A", - "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
levert een stroom van maximaal {socket:bosch_3pin:current}A" - }, - "freeform": { - "key": "socket:bosch_3pin:current", - "type": "pfloat" - }, - "mappings": [], - "condition": { - "and": [ - "socket:bosch_3pin~*", - "socket:bosch_3pin!=0" - ] - } - }, - { - "id": "power-output-14", - "question": { - "en": "What power output does a single plug of type
Bosch Active Connect with 3 pins and cable
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Bosch Active Connect met 3 pinnen aan een kabel
?" - }, - "render": { - "en": "
Bosch Active Connect with 3 pins and cable
outputs at most {socket:bosch_3pin:output}", - "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
levert een vermogen van maximaal {socket:bosch_3pin:output}" - }, - "freeform": { - "key": "socket:bosch_3pin:output", - "type": "pfloat" - }, - "mappings": [], - "condition": { - "and": [ - "socket:bosch_3pin~*", - "socket:bosch_3pin!=0" - ] - } - }, - { - "id": "plugs-15", - "question": { - "en": "How much plugs of type
Bosch Active Connect with 5 pins and cable
are available here?", - "nl": "Hoeveel stekkers van type
Bosch Active Connect met 5 pinnen aan een kabel
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:bosch_5pin} plugs of type
Bosch Active Connect with 5 pins and cable
available here", - "nl": "Hier zijn {socket:bosch_5pin} stekkers van het type
Bosch Active Connect met 5 pinnen aan een kabel
" - }, - "freeform": { - "key": "socket:bosch_5pin", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:bosch_5pin~*", - "socket:bosch_5pin!=0" - ] - } - }, - { - "id": "voltage-15", - "question": { - "en": "What voltage do the plugs with
Bosch Active Connect with 5 pins and cable
offer?", - "nl": "Welke spanning levert de stekker van type
Bosch Active Connect met 5 pinnen aan een kabel
" - }, - "render": { - "en": "
Bosch Active Connect with 5 pins and cable
outputs {socket:bosch_5pin:voltage} volt", - "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
heeft een spanning van {socket:bosch_5pin:voltage} volt" - }, - "freeform": { - "key": "socket:bosch_5pin:voltage", - "type": "pfloat" - }, - "mappings": [], - "condition": { - "and": [ - "socket:bosch_5pin~*", - "socket:bosch_5pin!=0" - ] - } - }, - { - "id": "current-15", - "question": { - "en": "What current do the plugs with
Bosch Active Connect with 5 pins and cable
offer?", - "nl": "Welke stroom levert de stekker van type
Bosch Active Connect met 5 pinnen aan een kabel
?" - }, - "render": { - "en": "
Bosch Active Connect with 5 pins and cable
outputs at most {socket:bosch_5pin:current}A", - "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
levert een stroom van maximaal {socket:bosch_5pin:current}A" - }, - "freeform": { - "key": "socket:bosch_5pin:current", - "type": "pfloat" - }, - "mappings": [], - "condition": { - "and": [ - "socket:bosch_5pin~*", - "socket:bosch_5pin!=0" - ] - } - }, - { - "id": "power-output-15", - "question": { - "en": "What power output does a single plug of type
Bosch Active Connect with 5 pins and cable
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Bosch Active Connect met 5 pinnen aan een kabel
?" - }, - "render": { - "en": "
Bosch Active Connect with 5 pins and cable
outputs at most {socket:bosch_5pin:output}", - "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
levert een vermogen van maximaal {socket:bosch_5pin:output}" - }, - "freeform": { - "key": "socket:bosch_5pin:output", - "type": "pfloat" - }, - "mappings": [], - "condition": { - "and": [ - "socket:bosch_5pin~*", - "socket:bosch_5pin!=0" - ] - } - }, - { - "id": "Authentication", - "question": { - "en": "What kind of authentication is available at the charging station?", - "it": "Quali sono gli orari di apertura di questa stazione di ricarica?", - "ja": "この充電ステーションはいつオープンしますか?", - "nb_NO": "Når åpnet denne ladestasjonen?", - "ru": "В какое время работает эта зарядная станция?", - "zh_Hant": "何時是充電站開放使用的時間?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "authentication:membership_card=yes", - "ifnot": "authentication:membership_card=no", - "then": { - "en": "Authentication by a membership card" - } - }, - { - "if": "authentication:app=yes", - "ifnot": "authentication:app=no", - "then": { - "en": "Authentication by an app" - } - }, - { - "if": "authentication:phone_call=yes", - "ifnot": "authentication:phone_call=no", - "then": { - "en": "Authentication via phone call is available" - } - }, - { - "if": "authentication:short_message=yes", - "ifnot": "authentication:short_message=no", - "then": { - "en": "Authentication via phone call is available" - } - }, - { - "if": "authentication:nfc=yes", - "ifnot": "authentication:nfc=no", - "then": { - "en": "Authentication via NFC is available" - } - }, - { - "if": "authentication:money_card=yes", - "ifnot": "authentication:money_card=no", - "then": { - "en": "Authentication via Money Card is available" - } - }, - { - "if": "authentication:debit_card=yes", - "ifnot": "authentication:debit_card=no", - "then": { - "en": "Authentication via debit card is available" - } - }, - { - "if": "authentication:none=yes", - "ifnot": "authentication:none=no", - "then": { - "en": "Charging here is (also) possible without authentication" - } - } - ] - }, - { - "id": "Auth phone", - "render": { - "en": "Authenticate by calling or SMS'ing to {authentication:phone_call:number}", - "it": "{network}", - "ja": "{network}", - "nb_NO": "{network}", - "ru": "{network}", - "zh_Hant": "{network}" - }, - "question": { - "en": "What's the phone number for authentication call or SMS?", - "it": "A quale rete appartiene questa stazione di ricarica?", - "ja": "この充電ステーションの運営チェーンはどこですか?", - "ru": "К какой сети относится эта станция?", - "zh_Hant": "充電站所屬的網路是?" - }, - "freeform": { - "key": "authentication:phone_call:number", - "type": "phone" - }, - "condition": { - "or": [ - "authentication:phone_call=yes", - "authentication:short_message=yes" - ] - }, - "it": { - "0": { - "then": "Non appartiene a una rete" - } - }, - "ja": { - "0": { - "then": "大規模な運営チェーンの一部ではない" - } - }, - "ru": { - "0": { - "then": "Не является частью более крупной сети" - } - }, - "zh_Hant": { - "0": { - "then": "不屬於大型網路" - } - } - }, - { - "id": "OH", - "render": "{opening_hours_table(opening_hours)}", - "freeform": { - "key": "opening_hours", - "type": "opening_hours" - }, - "question": { - "en": "When is this charging station opened?" - }, - "mappings": [ - { - "if": "opening_hours=24/7", - "then": { - "en": "24/7 opened (including holidays)" - } - } - ] - }, - { - "id": "fee/charge", - "question": { - "en": "How much does one have to pay to use this charging station?", - "nl": "Hoeveel kost het gebruik van dit oplaadpunt?" - }, - "freeform": { - "key": "charge", - "addExtraTags": [ - "fee=yes" - ] - }, - "render": { - "en": "Using this charging station costs {charge}", - "nl": "Dit oplaadpunt gebruiken kost {charge}" - }, - "mappings": [ - { - "if": { - "and": [ - "fee=no", - "charge=" - ] - }, - "then": { - "nl": "Gratis te gebruiken", - "en": "Free to use" - } - } - ] - }, - { - "id": "payment-options", - "builtin": "payment-options", - "override": { - "condition": { - "or": [ - "fee=yes", - "charge~*" - ] - }, - "mappings+": [ - { - "if": "payment:app=yes", - "ifnot": "payment:app=no", - "then": { - "en": "Payment is done using a dedicated app", - "nl": "Betalen via een app van het netwerk" - } - }, - { - "if": "payment:membership_card=yes", - "ifnot": "payment:membership_card=no", - "then": { - "en": "Payment is done using a membership card", - "nl": "Betalen via een lidkaart van het netwerk" - } - } - ] - } - }, - { - "id": "maxstay", - "question": { - "en": "What is the maximum amount of time one is allowed to stay here?", - "nl": "Hoelang mag een voertuig hier blijven staan?" - }, - "freeform": { - "key": "maxstay" - }, - "render": { - "en": "One can stay at most {canonical(maxstay)}", - "nl": "De maximale parkeertijd hier is {canonical(maxstay)}" - }, - "mappings": [ - { - "if": "maxstay=unlimited", - "then": { - "en": "No timelimit on leaving your vehicle here", - "nl": "Geen maximum parkeertijd" - } - } - ] - }, - { - "id": "Network", - "render": { - "en": "Part of the network {network}" - }, - "question": { - "en": "Is this charging station part of a network?" - }, - "freeform": { - "key": "network" - }, - "mappings": [ - { - "if": "no:network=yes", - "then": { - "en": "Not part of a bigger network" - } - }, - { - "if": "network=none", - "then": { - "en": "Not part of a bigger network" - }, - "hideInAnswer": true - }, - { - "if": "network=AeroVironment", - "then": "AeroVironment" - }, - { - "if": "network=Blink", - "then": "Blink" - }, - { - "if": "network=eVgo", - "then": "eVgo" - } - ] - }, - { - "id": "Operator", - "question": { - "en": "Who is the operator of this charging station?" - }, - "render": { - "en": "This charging station is operated by {operator}" - }, - "freeform": { - "key": "operator" - }, - "mappings": [ - { - "if": { - "and": [ - "network:={operator}" - ] - }, - "then": { - "en": "Actually, {operator} is the network" - }, - "addExtraTags": [ - "operator=" - ], - "hideInAnswer": "operator=" - } - ] - }, - { - "id": "phone", - "question": { - "en": "What number can one call if there is a problem with this charging station?" - }, - "render": { - "en": "In case of problems, call {phone}" - }, - "freeform": { - "key": "phone", - "type": "phone" - } - }, - { - "id": "email", - "question": { - "en": "What is the email address of the operator?" - }, - "render": { - "en": "In case of problems, send an email to {email}" - }, - "freeform": { - "key": "email", - "type": "email" - } - }, - { - "id": "website", - "question": { - "en": "What is the website of the operator?" - }, - "render": { - "en": "More info on {website}" - }, - "freeform": { - "key": "website", - "type": "url" - } - }, - "level", - { - "id": "ref", - "question": { - "en": "What is the reference number of this charging station?" - }, - "render": { - "en": "Reference number is {ref}" - }, - "freeform": { - "key": "ref" - } - }, - { - "id": "Operational status", - "question": { - "en": "Is this charging point in use?", - "nl": "Is dit oplaadpunt operationeel?" - }, - "mappings": [ - { - "if": "operational_status=broken", - "then": { - "en": "This charging station is broken", - "nl": "Dit oplaadpunt is kapot" - } - }, - { - "if": { - "and": [ - "planned:amenity=charging_station", - "amenity=" - ] - }, - "then": { - "en": "A charging station is planned here", - "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden" - } - }, - { - "if": { - "and": [ - "construction:amenity=charging_station", - "amenity=" - ] - }, - "then": { - "en": "A charging station is constructed here", - "nl": "Hier wordt op dit moment een oplaadpunt gebouwd" - } - }, - { - "if": { - "and": [ - "disused:amenity=charging_station", - "amenity=" - ] - }, - "then": { - "en": "This charging station has beed permanently disabled and is not in use anymore but is still visible", - "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig" - } - }, - { - "if": { - "and": [ - "amenity=charging_station", - "operational_status=" - ] - }, - "then": { - "en": "This charging station works", - "nl": "Dit oplaadpunt werkt" - } - } - ] - }, - { - "id": "Parking:fee", - "question": { - "en": "Does one have to pay a parking fee while charging?" - }, - "mappings": [ - { - "if": "parking:fee=no", - "then": { - "en": "No additional parking cost while charging" - } - }, - { - "if": "parking:fee=yes", - "then": { - "en": "An additional parking fee should be paid while charging" - } - } - ] - } - ], - "mapRendering": [ - { - "icon": { - "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", - "mappings": [ - { - "if": "bicycle=yes", - "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" - }, - { - "if": { - "or": [ - "car=yes", - "motorcar=yes" - ] + "id": "Type", + "question": { + "en": "Which vehicles are allowed to charge here?" }, - "then": "pin:#fff;./assets/themes/charging_stations/car.svg" - } - ] - }, - "iconOverlays": [ - { - "if": { - "or": [ - "disused:amenity=charging_station", - "operational_status=broken" + "multiAnswer": true, + "mappings": [ + { + "if": "bicycle=yes", + "ifnot": "bicycle=no", + "then": { + "en": "bicycles can be charged here" + } + }, + { + "if": "motorcar=yes", + "ifnot": "motorcar=no", + "then": { + "en": "Cars can be charged here" + } + }, + { + "if": "scooter=yes", + "ifnot": "scooter=no", + "then": { + "en": "Scooters can be charged here" + } + }, + { + "if": "hgv=yes", + "ifnot": "hgv=no", + "then": { + "en": "Heavy good vehicles (such as trucks) can be charged here" + } + }, + { + "if": "bus=yes", + "ifnot": "bus=no", + "then": { + "en": "Buses can be charged here" + } + } ] - }, - "then": "cross_bottom_right:#c22;" }, { - "if": { - "or": [ - "proposed:amenity=charging_station", - "planned:amenity=charging_station" - ] - }, - "then": "./assets/layers/charging_station/under_construction.svg", - "badge": true - }, - { - "if": { - "and": [ - "bicycle=yes", - { - "or": [ - "motorcar=yes", - "car=yes" + "id": "access", + "question": { + "en": "Who is allowed to use this charging station?" + }, + "render": { + "en": "Access is {access}" + }, + "freeform": { + "key": "access", + "addExtraTags": [ + "fixme=Freeform field used for access - doublecheck the value" ] - } + }, + "mappings": [ + { + "if": "access=yes", + "then": "Anyone can use this charging station (payment might be needed)" + }, + { + "if": { + "or": [ + "access=permissive", + "access=public" + ] + }, + "then": "Anyone can use this charging station (payment might be needed)", + "hideInAnswer": true + }, + { + "if": "access=customers", + "then": "Only customers of the place this station belongs to can use this charging station
E.g. a charging station operated by hotel which is only usable by their guests " + }, + { + "if": "access=private", + "then": "Not accessible to the general public (e.g. only accessible to the owners, employees, ...)" + } ] - }, - "then": "circle:#fff;./assets/themes/charging_stations/car.svg", - "badge": true - } - ], - "iconSize": { - "render": "50,50,bottom" - } - } - ], - "presets": [ - { - "tags": [ - "amenity=charging_station" - ], - "title": { - "en": "Charging station" - }, - "preciseInput": { - "preferredBackground": "map" - } - } - ], - "wayHandling": 1, - "filter": [ - { - "id": "vehicle-type", - "options": [ - { - "question": { - "en": "All vehicle types", - "nl": "Alle voertuigen" - } }, { - "question": { - "en": "Charging station for bicycles", - "nl": "Oplaadpunten voor fietsen" - }, - "osmTags": "bicycle=yes" + "id": "capacity", + "render": { + "en": "{capacity} vehicles can be charged here at the same time", + "nl": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden" + }, + "question": { + "en": "How much vehicles can be charged here at the same time?", + "nl": "Hoeveel voertuigen kunnen hier opgeladen worden?" + }, + "freeform": { + "key": "capacity", + "type": "pnat" + } }, { - "question": { - "en": "Charging station for cars", - "nl": "Oplaadpunten voor auto's" - }, - "osmTags": { - "or": [ - "car=yes", - "motorcar=yes" + "id": "Available_charging_stations (generated)", + "question": { + "en": "Which charging stations are available here?" + }, + "multiAnswer": true, + "mappings": [ + { + "if": "socket:schuko=1", + "ifnot": "socket:schuko=", + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "hideInAnswer": { + "or": [ + "_country!=be", + "_country!=fr", + "_country!=ma", + "_country!=tn", + "_country!=pl", + "_country!=cs", + "_country!=sk", + "_country!=mo" + ] + } + }, + { + "if": { + "and": [ + "socket:schuko~*", + "socket:schuko!=1" + ] + }, + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:typee=1", + "ifnot": "socket:typee=", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" + } + }, + { + "if": { + "and": [ + "socket:typee~*", + "socket:typee!=1" + ] + }, + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:chademo=1", + "ifnot": "socket:chademo=", + "then": { + "en": "
Chademo
", + "nl": "
Chademo
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:chademo~*", + "socket:chademo!=1" + ] + }, + "then": { + "en": "
Chademo
", + "nl": "
Chademo
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1_cable=1", + "ifnot": "socket:type1_cable=", + "then": { + "en": "
Type 1 with cable (J1772)
", + "nl": "
Type 1 met kabel (J1772)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=1" + ] + }, + "then": { + "en": "
Type 1 with cable (J1772)
", + "nl": "
Type 1 met kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1=1", + "ifnot": "socket:type1=", + "then": { + "en": "
Type 1 without cable (J1772)
", + "nl": "
Type 1 zonder kabel (J1772)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type1~*", + "socket:type1!=1" + ] + }, + "then": { + "en": "
Type 1 without cable (J1772)
", + "nl": "
Type 1 zonder kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1_combo=1", + "ifnot": "socket:type1_combo=", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=1" + ] + }, + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_supercharger=1", + "ifnot": "socket:tesla_supercharger=", + "then": { + "en": "
Tesla Supercharger
", + "nl": "
Tesla Supercharger
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger
", + "nl": "
Tesla Supercharger
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2=1", + "ifnot": "socket:type2=", + "then": { + "en": "
Type 2 (mennekes)
", + "nl": "
Type 2 (mennekes)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type2~*", + "socket:type2!=1" + ] + }, + "then": { + "en": "
Type 2 (mennekes)
", + "nl": "
Type 2 (mennekes)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2_combo=1", + "ifnot": "socket:type2_combo=", + "then": { + "en": "
Type 2 CCS (mennekes)
", + "nl": "
Type 2 CCS (mennekes)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=1" + ] + }, + "then": { + "en": "
Type 2 CCS (mennekes)
", + "nl": "
Type 2 CCS (mennekes)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2_cable=1", + "ifnot": "socket:type2_cable=", + "then": { + "en": "
Type 2 with cable (mennekes)
", + "nl": "
Type 2 met kabel (J1772)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=1" + ] + }, + "then": { + "en": "
Type 2 with cable (mennekes)
", + "nl": "
Type 2 met kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_supercharger_ccs=1", + "ifnot": "socket:tesla_supercharger_ccs=", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_destination=1", + "ifnot": "socket:tesla_destination=", + "then": { + "en": "
Tesla Supercharger (destination)
", + "nl": "
Tesla Supercharger (destination)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + }, + { + "or": [ + "_country!=us" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger (destination)
", + "nl": "
Tesla Supercharger (destination)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_destination=1", + "ifnot": "socket:tesla_destination=", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + }, + { + "or": [ + "_country=us" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=1" + ] + }, + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:USB-A=1", + "ifnot": "socket:USB-A=", + "then": { + "en": "
USB to charge phones and small electronics
", + "nl": "
USB om GSMs en kleine electronica op te laden
" + } + }, + { + "if": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=1" + ] + }, + "then": { + "en": "
USB to charge phones and small electronics
", + "nl": "
USB om GSMs en kleine electronica op te laden
" + }, + "hideInAnswer": true + }, + { + "if": "socket:bosch_3pin=1", + "ifnot": "socket:bosch_3pin=", + "then": { + "en": "
Bosch Active Connect with 3 pins and cable
", + "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "bicycle=no" + ] + }, + { + "and": [ + { + "or": [ + "car=yes", + "motorcar=yes", + "hgv=yes", + "bus=yes" + ] + }, + "bicycle!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:bosch_3pin~*", + "socket:bosch_3pin!=1" + ] + }, + "then": { + "en": "
Bosch Active Connect with 3 pins and cable
", + "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
" + }, + "hideInAnswer": true + }, + { + "if": "socket:bosch_5pin=1", + "ifnot": "socket:bosch_5pin=", + "then": { + "en": "
Bosch Active Connect with 5 pins and cable
", + "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "bicycle=no" + ] + }, + { + "and": [ + { + "or": [ + "car=yes", + "motorcar=yes", + "hgv=yes", + "bus=yes" + ] + }, + "bicycle!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:bosch_5pin~*", + "socket:bosch_5pin!=1" + ] + }, + "then": { + "en": "
Bosch Active Connect with 5 pins and cable
", + "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
" + }, + "hideInAnswer": true + } ] - } - } - ] - }, - { - "id": "working", - "options": [ + }, { - "question": { - "en": "Only working charging stations" - }, - "osmTags": { - "and": [ - "operational_status!=broken", - "amenity=charging_station" + "id": "plugs-0", + "question": { + "en": "How much plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
are available here?", + "nl": "Hoeveel stekkers van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:schuko} plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
available here", + "nl": "Hier zijn {socket:schuko} stekkers van het type
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "freeform": { + "key": "socket:schuko", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "voltage-0", + "question": { + "en": "What voltage do the plugs with
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", + "nl": "Welke spanning levert de stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "render": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs {socket:schuko:voltage} volt", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft een spanning van {socket:schuko:voltage} volt" + }, + "freeform": { + "key": "socket:schuko:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:schuko:voltage=230 V", + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs 230 volt", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft een spanning van 230 volt" + } + } + ], + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "current-0", + "question": { + "en": "What current do the plugs with
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", + "nl": "Welke stroom levert de stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
?" + }, + "render": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most {socket:schuko:current}A", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een stroom van maximaal {socket:schuko:current}A" + }, + "freeform": { + "key": "socket:schuko:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:schuko:current=16 A", + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 16 A", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een stroom van maximaal 16 A" + } + } + ], + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "power-output-0", + "question": { + "en": "What power output does a single plug of type
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
?" + }, + "render": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most {socket:schuko:output}", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een vermogen van maximaal {socket:schuko:output}" + }, + "freeform": { + "key": "socket:schuko:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:schuko:output=3.6 kw", + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 3.6 kw", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een vermogen van maximaal 3.6 kw" + } + } + ], + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "plugs-1", + "question": { + "en": "How much plugs of type
European wall plug with ground pin (CEE7/4 type E)
are available here?", + "nl": "Hoeveel stekkers van type
Europese stekker met aardingspin (CEE7/4 type E)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:typee} plugs of type
European wall plug with ground pin (CEE7/4 type E)
available here", + "nl": "Hier zijn {socket:typee} stekkers van het type
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "freeform": { + "key": "socket:typee", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "voltage-1", + "question": { + "en": "What voltage do the plugs with
European wall plug with ground pin (CEE7/4 type E)
offer?", + "nl": "Welke spanning levert de stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "render": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs {socket:typee:voltage} volt", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
heeft een spanning van {socket:typee:voltage} volt" + }, + "freeform": { + "key": "socket:typee:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:typee:voltage=230 V", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs 230 volt", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
heeft een spanning van 230 volt" + } + } + ], + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "current-1", + "question": { + "en": "What current do the plugs with
European wall plug with ground pin (CEE7/4 type E)
offer?", + "nl": "Welke stroom levert de stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
?" + }, + "render": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most {socket:typee:current}A", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een stroom van maximaal {socket:typee:current}A" + }, + "freeform": { + "key": "socket:typee:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:typee:current=16 A", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 16 A", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een stroom van maximaal 16 A" + } + } + ], + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "power-output-1", + "question": { + "en": "What power output does a single plug of type
European wall plug with ground pin (CEE7/4 type E)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
?" + }, + "render": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most {socket:typee:output}", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal {socket:typee:output}" + }, + "freeform": { + "key": "socket:typee:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:typee:output=3 kw", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 3 kw", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal 3 kw" + } + }, + { + "if": "socket:socket:typee:output=22 kw", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 22 kw", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "plugs-2", + "question": { + "en": "How much plugs of type
Chademo
are available here?", + "nl": "Hoeveel stekkers van type
Chademo
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:chademo} plugs of type
Chademo
available here", + "nl": "Hier zijn {socket:chademo} stekkers van het type
Chademo
" + }, + "freeform": { + "key": "socket:chademo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "voltage-2", + "question": { + "en": "What voltage do the plugs with
Chademo
offer?", + "nl": "Welke spanning levert de stekker van type
Chademo
" + }, + "render": { + "en": "
Chademo
outputs {socket:chademo:voltage} volt", + "nl": "
Chademo
heeft een spanning van {socket:chademo:voltage} volt" + }, + "freeform": { + "key": "socket:chademo:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:chademo:voltage=500 V", + "then": { + "en": "
Chademo
outputs 500 volt", + "nl": "
Chademo
heeft een spanning van 500 volt" + } + } + ], + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "current-2", + "question": { + "en": "What current do the plugs with
Chademo
offer?", + "nl": "Welke stroom levert de stekker van type
Chademo
?" + }, + "render": { + "en": "
Chademo
outputs at most {socket:chademo:current}A", + "nl": "
Chademo
levert een stroom van maximaal {socket:chademo:current}A" + }, + "freeform": { + "key": "socket:chademo:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:chademo:current=120 A", + "then": { + "en": "
Chademo
outputs at most 120 A", + "nl": "
Chademo
levert een stroom van maximaal 120 A" + } + } + ], + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "power-output-2", + "question": { + "en": "What power output does a single plug of type
Chademo
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Chademo
?" + }, + "render": { + "en": "
Chademo
outputs at most {socket:chademo:output}", + "nl": "
Chademo
levert een vermogen van maximaal {socket:chademo:output}" + }, + "freeform": { + "key": "socket:chademo:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:chademo:output=50 kw", + "then": { + "en": "
Chademo
outputs at most 50 kw", + "nl": "
Chademo
levert een vermogen van maximaal 50 kw" + } + } + ], + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "plugs-3", + "question": { + "en": "How much plugs of type
Type 1 with cable (J1772)
are available here?", + "nl": "Hoeveel stekkers van type
Type 1 met kabel (J1772)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type1_cable} plugs of type
Type 1 with cable (J1772)
available here", + "nl": "Hier zijn {socket:type1_cable} stekkers van het type
Type 1 met kabel (J1772)
" + }, + "freeform": { + "key": "socket:type1_cable", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "voltage-3", + "question": { + "en": "What voltage do the plugs with
Type 1 with cable (J1772)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 1 met kabel (J1772)
" + }, + "render": { + "en": "
Type 1 with cable (J1772)
outputs {socket:type1_cable:voltage} volt", + "nl": "
Type 1 met kabel (J1772)
heeft een spanning van {socket:type1_cable:voltage} volt" + }, + "freeform": { + "key": "socket:type1_cable:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_cable:voltage=200 V", + "then": { + "en": "
Type 1 with cable (J1772)
outputs 200 volt", + "nl": "
Type 1 met kabel (J1772)
heeft een spanning van 200 volt" + } + }, + { + "if": "socket:socket:type1_cable:voltage=240 V", + "then": { + "en": "
Type 1 with cable (J1772)
outputs 240 volt", + "nl": "
Type 1 met kabel (J1772)
heeft een spanning van 240 volt" + } + } + ], + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "current-3", + "question": { + "en": "What current do the plugs with
Type 1 with cable (J1772)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 1 met kabel (J1772)
?" + }, + "render": { + "en": "
Type 1 with cable (J1772)
outputs at most {socket:type1_cable:current}A", + "nl": "
Type 1 met kabel (J1772)
levert een stroom van maximaal {socket:type1_cable:current}A" + }, + "freeform": { + "key": "socket:type1_cable:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_cable:current=32 A", + "then": { + "en": "
Type 1 with cable (J1772)
outputs at most 32 A", + "nl": "
Type 1 met kabel (J1772)
levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "power-output-3", + "question": { + "en": "What power output does a single plug of type
Type 1 with cable (J1772)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 1 met kabel (J1772)
?" + }, + "render": { + "en": "
Type 1 with cable (J1772)
outputs at most {socket:type1_cable:output}", + "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal {socket:type1_cable:output}" + }, + "freeform": { + "key": "socket:type1_cable:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_cable:output=3.7 kw", + "then": { + "en": "
Type 1 with cable (J1772)
outputs at most 3.7 kw", + "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal 3.7 kw" + } + }, + { + "if": "socket:socket:type1_cable:output=7 kw", + "then": { + "en": "
Type 1 with cable (J1772)
outputs at most 7 kw", + "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal 7 kw" + } + } + ], + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "plugs-4", + "question": { + "en": "How much plugs of type
Type 1 without cable (J1772)
are available here?", + "nl": "Hoeveel stekkers van type
Type 1 zonder kabel (J1772)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type1} plugs of type
Type 1 without cable (J1772)
available here", + "nl": "Hier zijn {socket:type1} stekkers van het type
Type 1 zonder kabel (J1772)
" + }, + "freeform": { + "key": "socket:type1", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "voltage-4", + "question": { + "en": "What voltage do the plugs with
Type 1 without cable (J1772)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 1 zonder kabel (J1772)
" + }, + "render": { + "en": "
Type 1 without cable (J1772)
outputs {socket:type1:voltage} volt", + "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van {socket:type1:voltage} volt" + }, + "freeform": { + "key": "socket:type1:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1:voltage=200 V", + "then": { + "en": "
Type 1 without cable (J1772)
outputs 200 volt", + "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van 200 volt" + } + }, + { + "if": "socket:socket:type1:voltage=240 V", + "then": { + "en": "
Type 1 without cable (J1772)
outputs 240 volt", + "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van 240 volt" + } + } + ], + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "current-4", + "question": { + "en": "What current do the plugs with
Type 1 without cable (J1772)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 1 zonder kabel (J1772)
?" + }, + "render": { + "en": "
Type 1 without cable (J1772)
outputs at most {socket:type1:current}A", + "nl": "
Type 1 zonder kabel (J1772)
levert een stroom van maximaal {socket:type1:current}A" + }, + "freeform": { + "key": "socket:type1:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1:current=32 A", + "then": { + "en": "
Type 1 without cable (J1772)
outputs at most 32 A", + "nl": "
Type 1 zonder kabel (J1772)
levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "power-output-4", + "question": { + "en": "What power output does a single plug of type
Type 1 without cable (J1772)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 1 zonder kabel (J1772)
?" + }, + "render": { + "en": "
Type 1 without cable (J1772)
outputs at most {socket:type1:output}", + "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal {socket:type1:output}" + }, + "freeform": { + "key": "socket:type1:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1:output=3.7 kw", + "then": { + "en": "
Type 1 without cable (J1772)
outputs at most 3.7 kw", + "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 3.7 kw" + } + }, + { + "if": "socket:socket:type1:output=6.6 kw", + "then": { + "en": "
Type 1 without cable (J1772)
outputs at most 6.6 kw", + "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 6.6 kw" + } + }, + { + "if": "socket:socket:type1:output=7 kw", + "then": { + "en": "
Type 1 without cable (J1772)
outputs at most 7 kw", + "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 7 kw" + } + }, + { + "if": "socket:socket:type1:output=7.2 kw", + "then": { + "en": "
Type 1 without cable (J1772)
outputs at most 7.2 kw", + "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 7.2 kw" + } + } + ], + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "plugs-5", + "question": { + "en": "How much plugs of type
Type 1 CCS (aka Type 1 Combo)
are available here?", + "nl": "Hoeveel stekkers van type
Type 1 CCS (ook gekend als Type 1 Combo)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type1_combo} plugs of type
Type 1 CCS (aka Type 1 Combo)
available here", + "nl": "Hier zijn {socket:type1_combo} stekkers van het type
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "freeform": { + "key": "socket:type1_combo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "voltage-5", + "question": { + "en": "What voltage do the plugs with
Type 1 CCS (aka Type 1 Combo)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "render": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs {socket:type1_combo:voltage} volt", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van {socket:type1_combo:voltage} volt" + }, + "freeform": { + "key": "socket:type1_combo:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_combo:voltage=400 V", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs 400 volt", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van 400 volt" + } + }, + { + "if": "socket:socket:type1_combo:voltage=1000 V", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs 1000 volt", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van 1000 volt" + } + } + ], + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "current-5", + "question": { + "en": "What current do the plugs with
Type 1 CCS (aka Type 1 Combo)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
?" + }, + "render": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most {socket:type1_combo:current}A", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal {socket:type1_combo:current}A" + }, + "freeform": { + "key": "socket:type1_combo:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_combo:current=50 A", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 A", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal 50 A" + } + }, + { + "if": "socket:socket:type1_combo:current=125 A", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 125 A", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal 125 A" + } + } + ], + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "power-output-5", + "question": { + "en": "What power output does a single plug of type
Type 1 CCS (aka Type 1 Combo)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
?" + }, + "render": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most {socket:type1_combo:output}", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal {socket:type1_combo:output}" + }, + "freeform": { + "key": "socket:type1_combo:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_combo:output=50 kw", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 kw", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 50 kw" + } + }, + { + "if": "socket:socket:type1_combo:output=62.5 kw", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 62.5 kw", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 62.5 kw" + } + }, + { + "if": "socket:socket:type1_combo:output=150 kw", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 150 kw", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 150 kw" + } + }, + { + "if": "socket:socket:type1_combo:output=350 kw", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 350 kw", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 350 kw" + } + } + ], + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "plugs-6", + "question": { + "en": "How much plugs of type
Tesla Supercharger
are available here?", + "nl": "Hoeveel stekkers van type
Tesla Supercharger
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:tesla_supercharger} plugs of type
Tesla Supercharger
available here", + "nl": "Hier zijn {socket:tesla_supercharger} stekkers van het type
Tesla Supercharger
" + }, + "freeform": { + "key": "socket:tesla_supercharger", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "voltage-6", + "question": { + "en": "What voltage do the plugs with
Tesla Supercharger
offer?", + "nl": "Welke spanning levert de stekker van type
Tesla Supercharger
" + }, + "render": { + "en": "
Tesla Supercharger
outputs {socket:tesla_supercharger:voltage} volt", + "nl": "
Tesla Supercharger
heeft een spanning van {socket:tesla_supercharger:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_supercharger:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger:voltage=480 V", + "then": { + "en": "
Tesla Supercharger
outputs 480 volt", + "nl": "
Tesla Supercharger
heeft een spanning van 480 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "current-6", + "question": { + "en": "What current do the plugs with
Tesla Supercharger
offer?", + "nl": "Welke stroom levert de stekker van type
Tesla Supercharger
?" + }, + "render": { + "en": "
Tesla Supercharger
outputs at most {socket:tesla_supercharger:current}A", + "nl": "
Tesla Supercharger
levert een stroom van maximaal {socket:tesla_supercharger:current}A" + }, + "freeform": { + "key": "socket:tesla_supercharger:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger:current=125 A", + "then": { + "en": "
Tesla Supercharger
outputs at most 125 A", + "nl": "
Tesla Supercharger
levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:tesla_supercharger:current=350 A", + "then": { + "en": "
Tesla Supercharger
outputs at most 350 A", + "nl": "
Tesla Supercharger
levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "power-output-6", + "question": { + "en": "What power output does a single plug of type
Tesla Supercharger
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger
?" + }, + "render": { + "en": "
Tesla Supercharger
outputs at most {socket:tesla_supercharger:output}", + "nl": "
Tesla Supercharger
levert een vermogen van maximaal {socket:tesla_supercharger:output}" + }, + "freeform": { + "key": "socket:tesla_supercharger:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger:output=120 kw", + "then": { + "en": "
Tesla Supercharger
outputs at most 120 kw", + "nl": "
Tesla Supercharger
levert een vermogen van maximaal 120 kw" + } + }, + { + "if": "socket:socket:tesla_supercharger:output=150 kw", + "then": { + "en": "
Tesla Supercharger
outputs at most 150 kw", + "nl": "
Tesla Supercharger
levert een vermogen van maximaal 150 kw" + } + }, + { + "if": "socket:socket:tesla_supercharger:output=250 kw", + "then": { + "en": "
Tesla Supercharger
outputs at most 250 kw", + "nl": "
Tesla Supercharger
levert een vermogen van maximaal 250 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "plugs-7", + "question": { + "en": "How much plugs of type
Type 2 (mennekes)
are available here?", + "nl": "Hoeveel stekkers van type
Type 2 (mennekes)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type2} plugs of type
Type 2 (mennekes)
available here", + "nl": "Hier zijn {socket:type2} stekkers van het type
Type 2 (mennekes)
" + }, + "freeform": { + "key": "socket:type2", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "voltage-7", + "question": { + "en": "What voltage do the plugs with
Type 2 (mennekes)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 2 (mennekes)
" + }, + "render": { + "en": "
Type 2 (mennekes)
outputs {socket:type2:voltage} volt", + "nl": "
Type 2 (mennekes)
heeft een spanning van {socket:type2:voltage} volt" + }, + "freeform": { + "key": "socket:type2:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2:voltage=230 V", + "then": { + "en": "
Type 2 (mennekes)
outputs 230 volt", + "nl": "
Type 2 (mennekes)
heeft een spanning van 230 volt" + } + }, + { + "if": "socket:socket:type2:voltage=400 V", + "then": { + "en": "
Type 2 (mennekes)
outputs 400 volt", + "nl": "
Type 2 (mennekes)
heeft een spanning van 400 volt" + } + } + ], + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "current-7", + "question": { + "en": "What current do the plugs with
Type 2 (mennekes)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 2 (mennekes)
?" + }, + "render": { + "en": "
Type 2 (mennekes)
outputs at most {socket:type2:current}A", + "nl": "
Type 2 (mennekes)
levert een stroom van maximaal {socket:type2:current}A" + }, + "freeform": { + "key": "socket:type2:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2:current=16 A", + "then": { + "en": "
Type 2 (mennekes)
outputs at most 16 A", + "nl": "
Type 2 (mennekes)
levert een stroom van maximaal 16 A" + } + }, + { + "if": "socket:socket:type2:current=32 A", + "then": { + "en": "
Type 2 (mennekes)
outputs at most 32 A", + "nl": "
Type 2 (mennekes)
levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "power-output-7", + "question": { + "en": "What power output does a single plug of type
Type 2 (mennekes)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 2 (mennekes)
?" + }, + "render": { + "en": "
Type 2 (mennekes)
outputs at most {socket:type2:output}", + "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal {socket:type2:output}" + }, + "freeform": { + "key": "socket:type2:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2:output=11 kw", + "then": { + "en": "
Type 2 (mennekes)
outputs at most 11 kw", + "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal 11 kw" + } + }, + { + "if": "socket:socket:type2:output=22 kw", + "then": { + "en": "
Type 2 (mennekes)
outputs at most 22 kw", + "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "plugs-8", + "question": { + "en": "How much plugs of type
Type 2 CCS (mennekes)
are available here?", + "nl": "Hoeveel stekkers van type
Type 2 CCS (mennekes)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type2_combo} plugs of type
Type 2 CCS (mennekes)
available here", + "nl": "Hier zijn {socket:type2_combo} stekkers van het type
Type 2 CCS (mennekes)
" + }, + "freeform": { + "key": "socket:type2_combo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "voltage-8", + "question": { + "en": "What voltage do the plugs with
Type 2 CCS (mennekes)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 2 CCS (mennekes)
" + }, + "render": { + "en": "
Type 2 CCS (mennekes)
outputs {socket:type2_combo:voltage} volt", + "nl": "
Type 2 CCS (mennekes)
heeft een spanning van {socket:type2_combo:voltage} volt" + }, + "freeform": { + "key": "socket:type2_combo:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_combo:voltage=500 V", + "then": { + "en": "
Type 2 CCS (mennekes)
outputs 500 volt", + "nl": "
Type 2 CCS (mennekes)
heeft een spanning van 500 volt" + } + }, + { + "if": "socket:socket:type2_combo:voltage=920 V", + "then": { + "en": "
Type 2 CCS (mennekes)
outputs 920 volt", + "nl": "
Type 2 CCS (mennekes)
heeft een spanning van 920 volt" + } + } + ], + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "current-8", + "question": { + "en": "What current do the plugs with
Type 2 CCS (mennekes)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 2 CCS (mennekes)
?" + }, + "render": { + "en": "
Type 2 CCS (mennekes)
outputs at most {socket:type2_combo:current}A", + "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal {socket:type2_combo:current}A" + }, + "freeform": { + "key": "socket:type2_combo:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_combo:current=125 A", + "then": { + "en": "
Type 2 CCS (mennekes)
outputs at most 125 A", + "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:type2_combo:current=350 A", + "then": { + "en": "
Type 2 CCS (mennekes)
outputs at most 350 A", + "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "power-output-8", + "question": { + "en": "What power output does a single plug of type
Type 2 CCS (mennekes)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 2 CCS (mennekes)
?" + }, + "render": { + "en": "
Type 2 CCS (mennekes)
outputs at most {socket:type2_combo:output}", + "nl": "
Type 2 CCS (mennekes)
levert een vermogen van maximaal {socket:type2_combo:output}" + }, + "freeform": { + "key": "socket:type2_combo:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_combo:output=50 kw", + "then": { + "en": "
Type 2 CCS (mennekes)
outputs at most 50 kw", + "nl": "
Type 2 CCS (mennekes)
levert een vermogen van maximaal 50 kw" + } + } + ], + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "plugs-9", + "question": { + "en": "How much plugs of type
Type 2 with cable (mennekes)
are available here?", + "nl": "Hoeveel stekkers van type
Type 2 met kabel (J1772)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type2_cable} plugs of type
Type 2 with cable (mennekes)
available here", + "nl": "Hier zijn {socket:type2_cable} stekkers van het type
Type 2 met kabel (J1772)
" + }, + "freeform": { + "key": "socket:type2_cable", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "voltage-9", + "question": { + "en": "What voltage do the plugs with
Type 2 with cable (mennekes)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 2 met kabel (J1772)
" + }, + "render": { + "en": "
Type 2 with cable (mennekes)
outputs {socket:type2_cable:voltage} volt", + "nl": "
Type 2 met kabel (J1772)
heeft een spanning van {socket:type2_cable:voltage} volt" + }, + "freeform": { + "key": "socket:type2_cable:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_cable:voltage=230 V", + "then": { + "en": "
Type 2 with cable (mennekes)
outputs 230 volt", + "nl": "
Type 2 met kabel (J1772)
heeft een spanning van 230 volt" + } + }, + { + "if": "socket:socket:type2_cable:voltage=400 V", + "then": { + "en": "
Type 2 with cable (mennekes)
outputs 400 volt", + "nl": "
Type 2 met kabel (J1772)
heeft een spanning van 400 volt" + } + } + ], + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "current-9", + "question": { + "en": "What current do the plugs with
Type 2 with cable (mennekes)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 2 met kabel (J1772)
?" + }, + "render": { + "en": "
Type 2 with cable (mennekes)
outputs at most {socket:type2_cable:current}A", + "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal {socket:type2_cable:current}A" + }, + "freeform": { + "key": "socket:type2_cable:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_cable:current=16 A", + "then": { + "en": "
Type 2 with cable (mennekes)
outputs at most 16 A", + "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal 16 A" + } + }, + { + "if": "socket:socket:type2_cable:current=32 A", + "then": { + "en": "
Type 2 with cable (mennekes)
outputs at most 32 A", + "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "power-output-9", + "question": { + "en": "What power output does a single plug of type
Type 2 with cable (mennekes)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 2 met kabel (J1772)
?" + }, + "render": { + "en": "
Type 2 with cable (mennekes)
outputs at most {socket:type2_cable:output}", + "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal {socket:type2_cable:output}" + }, + "freeform": { + "key": "socket:type2_cable:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_cable:output=11 kw", + "then": { + "en": "
Type 2 with cable (mennekes)
outputs at most 11 kw", + "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal 11 kw" + } + }, + { + "if": "socket:socket:type2_cable:output=22 kw", + "then": { + "en": "
Type 2 with cable (mennekes)
outputs at most 22 kw", + "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "plugs-10", + "question": { + "en": "How much plugs of type
Tesla Supercharger CCS (a branded type2_css)
are available here?", + "nl": "Hoeveel stekkers van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:tesla_supercharger_ccs} plugs of type
Tesla Supercharger CCS (a branded type2_css)
available here", + "nl": "Hier zijn {socket:tesla_supercharger_ccs} stekkers van het type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "voltage-10", + "question": { + "en": "What voltage do the plugs with
Tesla Supercharger CCS (a branded type2_css)
offer?", + "nl": "Welke spanning levert de stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "render": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs {socket:tesla_supercharger_ccs:voltage} volt", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger_ccs:voltage=500 V", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs 500 volt", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van 500 volt" + } + }, + { + "if": "socket:socket:tesla_supercharger_ccs:voltage=920 V", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs 920 volt", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van 920 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "current-10", + "question": { + "en": "What current do the plugs with
Tesla Supercharger CCS (a branded type2_css)
offer?", + "nl": "Welke stroom levert de stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
?" + }, + "render": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most {socket:tesla_supercharger_ccs:current}A", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger_ccs:current=125 A", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 125 A", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:tesla_supercharger_ccs:current=350 A", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 350 A", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "power-output-10", + "question": { + "en": "What power output does a single plug of type
Tesla Supercharger CCS (a branded type2_css)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
?" + }, + "render": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most {socket:tesla_supercharger_ccs:output}", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger_ccs:output=50 kw", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 50 kw", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een vermogen van maximaal 50 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "plugs-11", + "question": { + "en": "How much plugs of type
Tesla Supercharger (destination)
are available here?", + "nl": "Hoeveel stekkers van type
Tesla Supercharger (destination)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:tesla_destination} plugs of type
Tesla Supercharger (destination)
available here", + "nl": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla Supercharger (destination)
" + }, + "freeform": { + "key": "socket:tesla_destination", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "voltage-11", + "question": { + "en": "What voltage do the plugs with
Tesla Supercharger (destination)
offer?", + "nl": "Welke spanning levert de stekker van type
Tesla Supercharger (destination)
" + }, + "render": { + "en": "
Tesla Supercharger (destination)
outputs {socket:tesla_destination:voltage} volt", + "nl": "
Tesla Supercharger (destination)
heeft een spanning van {socket:tesla_destination:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_destination:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:voltage=480 V", + "then": { + "en": "
Tesla Supercharger (destination)
outputs 480 volt", + "nl": "
Tesla Supercharger (destination)
heeft een spanning van 480 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "current-11", + "question": { + "en": "What current do the plugs with
Tesla Supercharger (destination)
offer?", + "nl": "Welke stroom levert de stekker van type
Tesla Supercharger (destination)
?" + }, + "render": { + "en": "
Tesla Supercharger (destination)
outputs at most {socket:tesla_destination:current}A", + "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal {socket:tesla_destination:current}A" + }, + "freeform": { + "key": "socket:tesla_destination:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:current=125 A", + "then": { + "en": "
Tesla Supercharger (destination)
outputs at most 125 A", + "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:tesla_destination:current=350 A", + "then": { + "en": "
Tesla Supercharger (destination)
outputs at most 350 A", + "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "power-output-11", + "question": { + "en": "What power output does a single plug of type
Tesla Supercharger (destination)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger (destination)
?" + }, + "render": { + "en": "
Tesla Supercharger (destination)
outputs at most {socket:tesla_destination:output}", + "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal {socket:tesla_destination:output}" + }, + "freeform": { + "key": "socket:tesla_destination:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:output=120 kw", + "then": { + "en": "
Tesla Supercharger (destination)
outputs at most 120 kw", + "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 120 kw" + } + }, + { + "if": "socket:socket:tesla_destination:output=150 kw", + "then": { + "en": "
Tesla Supercharger (destination)
outputs at most 150 kw", + "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 150 kw" + } + }, + { + "if": "socket:socket:tesla_destination:output=250 kw", + "then": { + "en": "
Tesla Supercharger (destination)
outputs at most 250 kw", + "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 250 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "plugs-12", + "question": { + "en": "How much plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
are available here?", + "nl": "Hoeveel stekkers van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:tesla_destination} plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
available here", + "nl": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "freeform": { + "key": "socket:tesla_destination", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "voltage-12", + "question": { + "en": "What voltage do the plugs with
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", + "nl": "Welke spanning levert de stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "render": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs {socket:tesla_destination:voltage} volt", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van {socket:tesla_destination:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_destination:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:voltage=230 V", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 230 volt", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van 230 volt" + } + }, + { + "if": "socket:socket:tesla_destination:voltage=400 V", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 400 volt", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van 400 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "current-12", + "question": { + "en": "What current do the plugs with
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", + "nl": "Welke stroom levert de stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
?" + }, + "render": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most {socket:tesla_destination:current}A", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal {socket:tesla_destination:current}A" + }, + "freeform": { + "key": "socket:tesla_destination:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:current=16 A", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 16 A", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal 16 A" + } + }, + { + "if": "socket:socket:tesla_destination:current=32 A", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 32 A", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "power-output-12", + "question": { + "en": "What power output does a single plug of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
?" + }, + "render": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most {socket:tesla_destination:output}", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal {socket:tesla_destination:output}" + }, + "freeform": { + "key": "socket:tesla_destination:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:output=11 kw", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 11 kw", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal 11 kw" + } + }, + { + "if": "socket:socket:tesla_destination:output=22 kw", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 22 kw", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "plugs-13", + "question": { + "en": "How much plugs of type
USB to charge phones and small electronics
are available here?", + "nl": "Hoeveel stekkers van type
USB om GSMs en kleine electronica op te laden
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:USB-A} plugs of type
USB to charge phones and small electronics
available here", + "nl": "Hier zijn {socket:USB-A} stekkers van het type
USB om GSMs en kleine electronica op te laden
" + }, + "freeform": { + "key": "socket:USB-A", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "voltage-13", + "question": { + "en": "What voltage do the plugs with
USB to charge phones and small electronics
offer?", + "nl": "Welke spanning levert de stekker van type
USB om GSMs en kleine electronica op te laden
" + }, + "render": { + "en": "
USB to charge phones and small electronics
outputs {socket:USB-A:voltage} volt", + "nl": "
USB om GSMs en kleine electronica op te laden
heeft een spanning van {socket:USB-A:voltage} volt" + }, + "freeform": { + "key": "socket:USB-A:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:voltage=5 V", + "then": { + "en": "
USB to charge phones and small electronics
outputs 5 volt", + "nl": "
USB om GSMs en kleine electronica op te laden
heeft een spanning van 5 volt" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "current-13", + "question": { + "en": "What current do the plugs with
USB to charge phones and small electronics
offer?", + "nl": "Welke stroom levert de stekker van type
USB om GSMs en kleine electronica op te laden
?" + }, + "render": { + "en": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:current}A", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal {socket:USB-A:current}A" + }, + "freeform": { + "key": "socket:USB-A:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:current=1 A", + "then": { + "en": "
USB to charge phones and small electronics
outputs at most 1 A", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal 1 A" + } + }, + { + "if": "socket:socket:USB-A:current=2 A", + "then": { + "en": "
USB to charge phones and small electronics
outputs at most 2 A", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal 2 A" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "power-output-13", + "question": { + "en": "What power output does a single plug of type
USB to charge phones and small electronics
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
USB om GSMs en kleine electronica op te laden
?" + }, + "render": { + "en": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:output}", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal {socket:USB-A:output}" + }, + "freeform": { + "key": "socket:USB-A:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:output=5w", + "then": { + "en": "
USB to charge phones and small electronics
outputs at most 5w", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal 5w" + } + }, + { + "if": "socket:socket:USB-A:output=10w", + "then": { + "en": "
USB to charge phones and small electronics
outputs at most 10w", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal 10w" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "plugs-14", + "question": { + "en": "How much plugs of type
Bosch Active Connect with 3 pins and cable
are available here?", + "nl": "Hoeveel stekkers van type
Bosch Active Connect met 3 pinnen aan een kabel
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:bosch_3pin} plugs of type
Bosch Active Connect with 3 pins and cable
available here", + "nl": "Hier zijn {socket:bosch_3pin} stekkers van het type
Bosch Active Connect met 3 pinnen aan een kabel
" + }, + "freeform": { + "key": "socket:bosch_3pin", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:bosch_3pin~*", + "socket:bosch_3pin!=0" + ] + } + }, + { + "id": "voltage-14", + "question": { + "en": "What voltage do the plugs with
Bosch Active Connect with 3 pins and cable
offer?", + "nl": "Welke spanning levert de stekker van type
Bosch Active Connect met 3 pinnen aan een kabel
" + }, + "render": { + "en": "
Bosch Active Connect with 3 pins and cable
outputs {socket:bosch_3pin:voltage} volt", + "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
heeft een spanning van {socket:bosch_3pin:voltage} volt" + }, + "freeform": { + "key": "socket:bosch_3pin:voltage", + "type": "pfloat" + }, + "mappings": [], + "condition": { + "and": [ + "socket:bosch_3pin~*", + "socket:bosch_3pin!=0" + ] + } + }, + { + "id": "current-14", + "question": { + "en": "What current do the plugs with
Bosch Active Connect with 3 pins and cable
offer?", + "nl": "Welke stroom levert de stekker van type
Bosch Active Connect met 3 pinnen aan een kabel
?" + }, + "render": { + "en": "
Bosch Active Connect with 3 pins and cable
outputs at most {socket:bosch_3pin:current}A", + "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
levert een stroom van maximaal {socket:bosch_3pin:current}A" + }, + "freeform": { + "key": "socket:bosch_3pin:current", + "type": "pfloat" + }, + "mappings": [], + "condition": { + "and": [ + "socket:bosch_3pin~*", + "socket:bosch_3pin!=0" + ] + } + }, + { + "id": "power-output-14", + "question": { + "en": "What power output does a single plug of type
Bosch Active Connect with 3 pins and cable
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Bosch Active Connect met 3 pinnen aan een kabel
?" + }, + "render": { + "en": "
Bosch Active Connect with 3 pins and cable
outputs at most {socket:bosch_3pin:output}", + "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
levert een vermogen van maximaal {socket:bosch_3pin:output}" + }, + "freeform": { + "key": "socket:bosch_3pin:output", + "type": "pfloat" + }, + "mappings": [], + "condition": { + "and": [ + "socket:bosch_3pin~*", + "socket:bosch_3pin!=0" + ] + } + }, + { + "id": "plugs-15", + "question": { + "en": "How much plugs of type
Bosch Active Connect with 5 pins and cable
are available here?", + "nl": "Hoeveel stekkers van type
Bosch Active Connect met 5 pinnen aan een kabel
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:bosch_5pin} plugs of type
Bosch Active Connect with 5 pins and cable
available here", + "nl": "Hier zijn {socket:bosch_5pin} stekkers van het type
Bosch Active Connect met 5 pinnen aan een kabel
" + }, + "freeform": { + "key": "socket:bosch_5pin", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:bosch_5pin~*", + "socket:bosch_5pin!=0" + ] + } + }, + { + "id": "voltage-15", + "question": { + "en": "What voltage do the plugs with
Bosch Active Connect with 5 pins and cable
offer?", + "nl": "Welke spanning levert de stekker van type
Bosch Active Connect met 5 pinnen aan een kabel
" + }, + "render": { + "en": "
Bosch Active Connect with 5 pins and cable
outputs {socket:bosch_5pin:voltage} volt", + "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
heeft een spanning van {socket:bosch_5pin:voltage} volt" + }, + "freeform": { + "key": "socket:bosch_5pin:voltage", + "type": "pfloat" + }, + "mappings": [], + "condition": { + "and": [ + "socket:bosch_5pin~*", + "socket:bosch_5pin!=0" + ] + } + }, + { + "id": "current-15", + "question": { + "en": "What current do the plugs with
Bosch Active Connect with 5 pins and cable
offer?", + "nl": "Welke stroom levert de stekker van type
Bosch Active Connect met 5 pinnen aan een kabel
?" + }, + "render": { + "en": "
Bosch Active Connect with 5 pins and cable
outputs at most {socket:bosch_5pin:current}A", + "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
levert een stroom van maximaal {socket:bosch_5pin:current}A" + }, + "freeform": { + "key": "socket:bosch_5pin:current", + "type": "pfloat" + }, + "mappings": [], + "condition": { + "and": [ + "socket:bosch_5pin~*", + "socket:bosch_5pin!=0" + ] + } + }, + { + "id": "power-output-15", + "question": { + "en": "What power output does a single plug of type
Bosch Active Connect with 5 pins and cable
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Bosch Active Connect met 5 pinnen aan een kabel
?" + }, + "render": { + "en": "
Bosch Active Connect with 5 pins and cable
outputs at most {socket:bosch_5pin:output}", + "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
levert een vermogen van maximaal {socket:bosch_5pin:output}" + }, + "freeform": { + "key": "socket:bosch_5pin:output", + "type": "pfloat" + }, + "mappings": [], + "condition": { + "and": [ + "socket:bosch_5pin~*", + "socket:bosch_5pin!=0" + ] + } + }, + { + "id": "Authentication", + "question": { + "en": "What kind of authentication is available at the charging station?", + "it": "Quali sono gli orari di apertura di questa stazione di ricarica?", + "ja": "この充電ステーションはいつオープンしますか?", + "nb_NO": "Når åpnet denne ladestasjonen?", + "ru": "В какое время работает эта зарядная станция?", + "zh_Hant": "何時是充電站開放使用的時間?" + }, + "multiAnswer": true, + "mappings": [ + { + "if": "authentication:membership_card=yes", + "ifnot": "authentication:membership_card=no", + "then": { + "en": "Authentication by a membership card" + } + }, + { + "if": "authentication:app=yes", + "ifnot": "authentication:app=no", + "then": { + "en": "Authentication by an app" + } + }, + { + "if": "authentication:phone_call=yes", + "ifnot": "authentication:phone_call=no", + "then": { + "en": "Authentication via phone call is available" + } + }, + { + "if": "authentication:short_message=yes", + "ifnot": "authentication:short_message=no", + "then": { + "en": "Authentication via phone call is available" + } + }, + { + "if": "authentication:nfc=yes", + "ifnot": "authentication:nfc=no", + "then": { + "en": "Authentication via NFC is available" + } + }, + { + "if": "authentication:money_card=yes", + "ifnot": "authentication:money_card=no", + "then": { + "en": "Authentication via Money Card is available" + } + }, + { + "if": "authentication:debit_card=yes", + "ifnot": "authentication:debit_card=no", + "then": { + "en": "Authentication via debit card is available" + } + }, + { + "if": "authentication:none=yes", + "ifnot": "authentication:none=no", + "then": { + "en": "Charging here is (also) possible without authentication" + } + } + ] + }, + { + "id": "Auth phone", + "render": { + "en": "Authenticate by calling or SMS'ing to {authentication:phone_call:number}", + "it": "{network}", + "ja": "{network}", + "nb_NO": "{network}", + "ru": "{network}", + "zh_Hant": "{network}" + }, + "question": { + "en": "What's the phone number for authentication call or SMS?", + "it": "A quale rete appartiene questa stazione di ricarica?", + "ja": "この充電ステーションの運営チェーンはどこですか?", + "ru": "К какой сети относится эта станция?", + "zh_Hant": "充電站所屬的網路是?" + }, + "freeform": { + "key": "authentication:phone_call:number", + "type": "phone" + }, + "condition": { + "or": [ + "authentication:phone_call=yes", + "authentication:short_message=yes" + ] + }, + "it": { + "0": { + "then": "Non appartiene a una rete" + } + }, + "ja": { + "0": { + "then": "大規模な運営チェーンの一部ではない" + } + }, + "ru": { + "0": { + "then": "Не является частью более крупной сети" + } + }, + "zh_Hant": { + "0": { + "then": "不屬於大型網路" + } + } + }, + { + "id": "OH", + "render": "{opening_hours_table(opening_hours)}", + "freeform": { + "key": "opening_hours", + "type": "opening_hours" + }, + "question": { + "en": "When is this charging station opened?" + }, + "mappings": [ + { + "if": "opening_hours=24/7", + "then": { + "en": "24/7 opened (including holidays)" + } + } + ] + }, + { + "id": "fee/charge", + "question": { + "en": "How much does one have to pay to use this charging station?", + "nl": "Hoeveel kost het gebruik van dit oplaadpunt?" + }, + "freeform": { + "key": "charge", + "addExtraTags": [ + "fee=yes" + ] + }, + "render": { + "en": "Using this charging station costs {charge}", + "nl": "Dit oplaadpunt gebruiken kost {charge}" + }, + "mappings": [ + { + "if": { + "and": [ + "fee=no", + "charge=" + ] + }, + "then": { + "nl": "Gratis te gebruiken", + "en": "Free to use" + } + } + ] + }, + { + "id": "payment-options", + "builtin": "payment-options", + "override": { + "condition": { + "or": [ + "fee=yes", + "charge~*" + ] + }, + "mappings+": [ + { + "if": "payment:app=yes", + "ifnot": "payment:app=no", + "then": { + "en": "Payment is done using a dedicated app", + "nl": "Betalen via een app van het netwerk" + } + }, + { + "if": "payment:membership_card=yes", + "ifnot": "payment:membership_card=no", + "then": { + "en": "Payment is done using a membership card", + "nl": "Betalen via een lidkaart van het netwerk" + } + } + ] + } + }, + { + "id": "maxstay", + "question": { + "en": "What is the maximum amount of time one is allowed to stay here?", + "nl": "Hoelang mag een voertuig hier blijven staan?" + }, + "freeform": { + "key": "maxstay" + }, + "render": { + "en": "One can stay at most {canonical(maxstay)}", + "nl": "De maximale parkeertijd hier is {canonical(maxstay)}" + }, + "mappings": [ + { + "if": "maxstay=unlimited", + "then": { + "en": "No timelimit on leaving your vehicle here", + "nl": "Geen maximum parkeertijd" + } + } + ] + }, + { + "id": "Network", + "render": { + "en": "Part of the network {network}" + }, + "question": { + "en": "Is this charging station part of a network?" + }, + "freeform": { + "key": "network" + }, + "mappings": [ + { + "if": "no:network=yes", + "then": { + "en": "Not part of a bigger network" + } + }, + { + "if": "network=none", + "then": { + "en": "Not part of a bigger network" + }, + "hideInAnswer": true + }, + { + "if": "network=AeroVironment", + "then": "AeroVironment" + }, + { + "if": "network=Blink", + "then": "Blink" + }, + { + "if": "network=eVgo", + "then": "eVgo" + } + ] + }, + { + "id": "Operator", + "question": { + "en": "Who is the operator of this charging station?" + }, + "render": { + "en": "This charging station is operated by {operator}" + }, + "freeform": { + "key": "operator" + }, + "mappings": [ + { + "if": { + "and": [ + "network:={operator}" + ] + }, + "then": { + "en": "Actually, {operator} is the network" + }, + "addExtraTags": [ + "operator=" + ], + "hideInAnswer": "operator=" + } + ] + }, + { + "id": "phone", + "question": { + "en": "What number can one call if there is a problem with this charging station?" + }, + "render": { + "en": "In case of problems, call {phone}" + }, + "freeform": { + "key": "phone", + "type": "phone" + } + }, + { + "id": "email", + "question": { + "en": "What is the email address of the operator?" + }, + "render": { + "en": "In case of problems, send an email to {email}" + }, + "freeform": { + "key": "email", + "type": "email" + } + }, + { + "id": "website", + "question": { + "en": "What is the website of the operator?" + }, + "render": { + "en": "More info on {website}" + }, + "freeform": { + "key": "website", + "type": "url" + } + }, + "level", + { + "id": "ref", + "question": { + "en": "What is the reference number of this charging station?" + }, + "render": { + "en": "Reference number is {ref}" + }, + "freeform": { + "key": "ref" + } + }, + { + "id": "Operational status", + "question": { + "en": "Is this charging point in use?", + "nl": "Is dit oplaadpunt operationeel?" + }, + "mappings": [ + { + "if": "operational_status=broken", + "then": { + "en": "This charging station is broken", + "nl": "Dit oplaadpunt is kapot" + } + }, + { + "if": { + "and": [ + "planned:amenity=charging_station", + "amenity=" + ] + }, + "then": { + "en": "A charging station is planned here", + "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden" + } + }, + { + "if": { + "and": [ + "construction:amenity=charging_station", + "amenity=" + ] + }, + "then": { + "en": "A charging station is constructed here", + "nl": "Hier wordt op dit moment een oplaadpunt gebouwd" + } + }, + { + "if": { + "and": [ + "disused:amenity=charging_station", + "amenity=" + ] + }, + "then": { + "en": "This charging station has beed permanently disabled and is not in use anymore but is still visible", + "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig" + } + }, + { + "if": { + "and": [ + "amenity=charging_station", + "operational_status=" + ] + }, + "then": { + "en": "This charging station works", + "nl": "Dit oplaadpunt werkt" + } + } + ] + }, + { + "id": "Parking:fee", + "question": { + "en": "Does one have to pay a parking fee while charging?" + }, + "mappings": [ + { + "if": "parking:fee=no", + "then": { + "en": "No additional parking cost while charging" + } + }, + { + "if": "parking:fee=yes", + "then": { + "en": "An additional parking fee should be paid while charging" + } + } ] - } } - ] - }, - { - "id": "connection_type", - "options": [ + ], + "mapRendering": [ { - "question": { - "en": "All connectors", - "nl": "Alle types" - } - }, - { - "question": { - "en": "Has a
Schuko wall plug without ground pin (CEE7/4 type F)
connector", - "nl": "Heeft een
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "osmTags": "socket:schuko~*" - }, - { - "question": { - "en": "Has a
European wall plug with ground pin (CEE7/4 type E)
connector", - "nl": "Heeft een
Europese stekker met aardingspin (CEE7/4 type E)
" - }, - "osmTags": "socket:typee~*" - }, - { - "question": { - "en": "Has a
Chademo
connector", - "nl": "Heeft een
Chademo
" - }, - "osmTags": "socket:chademo~*" - }, - { - "question": { - "en": "Has a
Type 1 with cable (J1772)
connector", - "nl": "Heeft een
Type 1 met kabel (J1772)
" - }, - "osmTags": "socket:type1_cable~*" - }, - { - "question": { - "en": "Has a
Type 1 without cable (J1772)
connector", - "nl": "Heeft een
Type 1 zonder kabel (J1772)
" - }, - "osmTags": "socket:type1~*" - }, - { - "question": { - "en": "Has a
Type 1 CCS (aka Type 1 Combo)
connector", - "nl": "Heeft een
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "osmTags": "socket:type1_combo~*" - }, - { - "question": { - "en": "Has a
Tesla Supercharger
connector", - "nl": "Heeft een
Tesla Supercharger
" - }, - "osmTags": "socket:tesla_supercharger~*" - }, - { - "question": { - "en": "Has a
Type 2 (mennekes)
connector", - "nl": "Heeft een
Type 2 (mennekes)
" - }, - "osmTags": "socket:type2~*" - }, - { - "question": { - "en": "Has a
Type 2 CCS (mennekes)
connector", - "nl": "Heeft een
Type 2 CCS (mennekes)
" - }, - "osmTags": "socket:type2_combo~*" - }, - { - "question": { - "en": "Has a
Type 2 with cable (mennekes)
connector", - "nl": "Heeft een
Type 2 met kabel (J1772)
" - }, - "osmTags": "socket:type2_cable~*" - }, - { - "question": { - "en": "Has a
Tesla Supercharger CCS (a branded type2_css)
connector", - "nl": "Heeft een
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "osmTags": "socket:tesla_supercharger_ccs~*" - }, - { - "question": { - "en": "Has a
Tesla Supercharger (destination)
connector", - "nl": "Heeft een
Tesla Supercharger (destination)
" - }, - "osmTags": "socket:tesla_destination~*" - }, - { - "question": { - "en": "Has a
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
connector", - "nl": "Heeft een
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "osmTags": "socket:tesla_destination~*" - }, - { - "question": { - "en": "Has a
USB to charge phones and small electronics
connector", - "nl": "Heeft een
USB om GSMs en kleine electronica op te laden
" - }, - "osmTags": "socket:USB-A~*" - }, - { - "question": { - "en": "Has a
Bosch Active Connect with 3 pins and cable
connector", - "nl": "Heeft een
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "osmTags": "socket:bosch_3pin~*" - }, - { - "question": { - "en": "Has a
Bosch Active Connect with 5 pins and cable
connector", - "nl": "Heeft een
Bosch Active Connect met 5 pinnen aan een kabel
" - }, - "osmTags": "socket:bosch_5pin~*" + "location": [ + "point" + ] } - ] - } - ], - "units": [ - { - "appliesToKey": [ - "maxstay" - ], - "applicableUnits": [ + ], + "presets": [ { - "canonicalDenomination": "minutes", - "canonicalDenominationSingular": "minute", - "alternativeDenomination": [ - "m", - "min", - "mins", - "minuten", - "mns" - ], - "human": { - "en": " minutes", - "nl": " minuten" - }, - "humanSingular": { - "en": " minute", - "nl": " minuut" - } + "tags": [ + "amenity=charging_station" + ], + "title": { + "en": "Charging station" + }, + "preciseInput": { + "preferredBackground": "map" + } + } + ], + "wayHandling": 1, + "filter": [ + { + "id": "vehicle-type", + "options": [ + { + "question": { + "en": "All vehicle types", + "nl": "Alle voertuigen" + } + }, + { + "question": { + "en": "Charging station for bicycles", + "nl": "Oplaadpunten voor fietsen" + }, + "osmTags": "bicycle=yes" + }, + { + "question": { + "en": "Charging station for cars", + "nl": "Oplaadpunten voor auto's" + }, + "osmTags": { + "or": [ + "car=yes", + "motorcar=yes" + ] + } + } + ] }, { - "canonicalDenomination": "hours", - "canonicalDenominationSingular": "hour", - "alternativeDenomination": [ - "h", - "hrs", - "hours", - "u", - "uur", - "uren" - ], - "human": { - "en": " hours", - "nl": " uren" - }, - "humanSingular": { - "en": " hour", - "nl": " uur" - } + "id": "working", + "options": [ + { + "question": { + "en": "Only working charging stations" + }, + "osmTags": { + "and": [ + "operational_status!=broken", + "amenity=charging_station" + ] + } + } + ] }, { - "canonicalDenomination": "days", - "canonicalDenominationSingular": "day", - "alternativeDenomination": [ - "dys", - "dagen", - "dag" - ], - "human": { - "en": " days", - "nl": " day" - }, - "humanSingular": { - "en": " day", - "nl": " dag" - } + "id": "connection_type", + "options": [ + { + "question": { + "en": "All connectors", + "nl": "Alle types" + } + }, + { + "question": { + "en": "Has a
Schuko wall plug without ground pin (CEE7/4 type F)
connector", + "nl": "Heeft een
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "osmTags": "socket:schuko~*" + }, + { + "question": { + "en": "Has a
European wall plug with ground pin (CEE7/4 type E)
connector", + "nl": "Heeft een
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "osmTags": "socket:typee~*" + }, + { + "question": { + "en": "Has a
Chademo
connector", + "nl": "Heeft een
Chademo
" + }, + "osmTags": "socket:chademo~*" + }, + { + "question": { + "en": "Has a
Type 1 with cable (J1772)
connector", + "nl": "Heeft een
Type 1 met kabel (J1772)
" + }, + "osmTags": "socket:type1_cable~*" + }, + { + "question": { + "en": "Has a
Type 1 without cable (J1772)
connector", + "nl": "Heeft een
Type 1 zonder kabel (J1772)
" + }, + "osmTags": "socket:type1~*" + }, + { + "question": { + "en": "Has a
Type 1 CCS (aka Type 1 Combo)
connector", + "nl": "Heeft een
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "osmTags": "socket:type1_combo~*" + }, + { + "question": { + "en": "Has a
Tesla Supercharger
connector", + "nl": "Heeft een
Tesla Supercharger
" + }, + "osmTags": "socket:tesla_supercharger~*" + }, + { + "question": { + "en": "Has a
Type 2 (mennekes)
connector", + "nl": "Heeft een
Type 2 (mennekes)
" + }, + "osmTags": "socket:type2~*" + }, + { + "question": { + "en": "Has a
Type 2 CCS (mennekes)
connector", + "nl": "Heeft een
Type 2 CCS (mennekes)
" + }, + "osmTags": "socket:type2_combo~*" + }, + { + "question": { + "en": "Has a
Type 2 with cable (mennekes)
connector", + "nl": "Heeft een
Type 2 met kabel (J1772)
" + }, + "osmTags": "socket:type2_cable~*" + }, + { + "question": { + "en": "Has a
Tesla Supercharger CCS (a branded type2_css)
connector", + "nl": "Heeft een
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "osmTags": "socket:tesla_supercharger_ccs~*" + }, + { + "question": { + "en": "Has a
Tesla Supercharger (destination)
connector", + "nl": "Heeft een
Tesla Supercharger (destination)
" + }, + "osmTags": "socket:tesla_destination~*" + }, + { + "question": { + "en": "Has a
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
connector", + "nl": "Heeft een
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "osmTags": "socket:tesla_destination~*" + }, + { + "question": { + "en": "Has a
USB to charge phones and small electronics
connector", + "nl": "Heeft een
USB om GSMs en kleine electronica op te laden
" + }, + "osmTags": "socket:USB-A~*" + }, + { + "question": { + "en": "Has a
Bosch Active Connect with 3 pins and cable
connector", + "nl": "Heeft een
Bosch Active Connect met 3 pinnen aan een kabel
" + }, + "osmTags": "socket:bosch_3pin~*" + }, + { + "question": { + "en": "Has a
Bosch Active Connect with 5 pins and cable
connector", + "nl": "Heeft een
Bosch Active Connect met 5 pinnen aan een kabel
" + }, + "osmTags": "socket:bosch_5pin~*" + } + ] } - ] - }, - { - "appliesToKey": [ - "socket:schuko:voltage", - "socket:typee:voltage", - "socket:chademo:voltage", - "socket:type1_cable:voltage", - "socket:type1:voltage", - "socket:type1_combo:voltage", - "socket:tesla_supercharger:voltage", - "socket:type2:voltage", - "socket:type2_combo:voltage", - "socket:type2_cable:voltage", - "socket:tesla_supercharger_ccs:voltage", - "socket:tesla_destination:voltage", - "socket:tesla_destination:voltage", - "socket:USB-A:voltage", - "socket:bosch_3pin:voltage", - "socket:bosch_5pin:voltage" - ], - "applicableUnits": [ + ], + "units": [ { - "canonicalDenomination": "V", - "alternativeDenomination": [ - "v", - "volt", - "voltage", - "V", - "Volt" - ], - "human": { - "en": "Volts", - "nl": "volt" - } - } - ], - "eraseInvalidValues": true - }, - { - "appliesToKey": [ - "socket:schuko:current", - "socket:typee:current", - "socket:chademo:current", - "socket:type1_cable:current", - "socket:type1:current", - "socket:type1_combo:current", - "socket:tesla_supercharger:current", - "socket:type2:current", - "socket:type2_combo:current", - "socket:type2_cable:current", - "socket:tesla_supercharger_ccs:current", - "socket:tesla_destination:current", - "socket:tesla_destination:current", - "socket:USB-A:current", - "socket:bosch_3pin:current", - "socket:bosch_5pin:current" - ], - "applicableUnits": [ - { - "canonicalDenomination": "A", - "alternativeDenomination": [ - "a", - "amp", - "amperage", - "A" - ], - "human": { - "en": "A", - "nl": "A" - } - } - ], - "eraseInvalidValues": true - }, - { - "appliesToKey": [ - "socket:schuko:output", - "socket:typee:output", - "socket:chademo:output", - "socket:type1_cable:output", - "socket:type1:output", - "socket:type1_combo:output", - "socket:tesla_supercharger:output", - "socket:type2:output", - "socket:type2_combo:output", - "socket:type2_cable:output", - "socket:tesla_supercharger_ccs:output", - "socket:tesla_destination:output", - "socket:tesla_destination:output", - "socket:USB-A:output", - "socket:bosch_3pin:output", - "socket:bosch_5pin:output" - ], - "applicableUnits": [ - { - "canonicalDenomination": "kW", - "alternativeDenomination": [ - "kilowatt" - ], - "human": { - "en": "kilowatt", - "nl": "kilowatt" - } + "appliesToKey": [ + "maxstay" + ], + "applicableUnits": [ + { + "canonicalDenomination": "minutes", + "canonicalDenominationSingular": "minute", + "alternativeDenomination": [ + "m", + "min", + "mins", + "minuten", + "mns" + ], + "human": { + "en": " minutes", + "nl": " minuten" + }, + "humanSingular": { + "en": " minute", + "nl": " minuut" + } + }, + { + "canonicalDenomination": "hours", + "canonicalDenominationSingular": "hour", + "alternativeDenomination": [ + "h", + "hrs", + "hours", + "u", + "uur", + "uren" + ], + "human": { + "en": " hours", + "nl": " uren" + }, + "humanSingular": { + "en": " hour", + "nl": " uur" + } + }, + { + "canonicalDenomination": "days", + "canonicalDenominationSingular": "day", + "alternativeDenomination": [ + "dys", + "dagen", + "dag" + ], + "human": { + "en": " days", + "nl": " day" + }, + "humanSingular": { + "en": " day", + "nl": " dag" + } + } + ] }, { - "canonicalDenomination": "mW", - "alternativeDenomination": [ - "megawatt" - ], - "human": { - "en": "megawatt", - "nl": "megawatt" - } + "appliesToKey": [ + "socket:schuko:voltage", + "socket:typee:voltage", + "socket:chademo:voltage", + "socket:type1_cable:voltage", + "socket:type1:voltage", + "socket:type1_combo:voltage", + "socket:tesla_supercharger:voltage", + "socket:type2:voltage", + "socket:type2_combo:voltage", + "socket:type2_cable:voltage", + "socket:tesla_supercharger_ccs:voltage", + "socket:tesla_destination:voltage", + "socket:tesla_destination:voltage", + "socket:USB-A:voltage", + "socket:bosch_3pin:voltage", + "socket:bosch_5pin:voltage" + ], + "applicableUnits": [ + { + "canonicalDenomination": "V", + "alternativeDenomination": [ + "v", + "volt", + "voltage", + "V", + "Volt" + ], + "human": { + "en": "Volts", + "nl": "volt" + } + } + ], + "eraseInvalidValues": true + }, + { + "appliesToKey": [ + "socket:schuko:current", + "socket:typee:current", + "socket:chademo:current", + "socket:type1_cable:current", + "socket:type1:current", + "socket:type1_combo:current", + "socket:tesla_supercharger:current", + "socket:type2:current", + "socket:type2_combo:current", + "socket:type2_cable:current", + "socket:tesla_supercharger_ccs:current", + "socket:tesla_destination:current", + "socket:tesla_destination:current", + "socket:USB-A:current", + "socket:bosch_3pin:current", + "socket:bosch_5pin:current" + ], + "applicableUnits": [ + { + "canonicalDenomination": "A", + "alternativeDenomination": [ + "a", + "amp", + "amperage", + "A" + ], + "human": { + "en": "A", + "nl": "A" + } + } + ], + "eraseInvalidValues": true + }, + { + "appliesToKey": [ + "socket:schuko:output", + "socket:typee:output", + "socket:chademo:output", + "socket:type1_cable:output", + "socket:type1:output", + "socket:type1_combo:output", + "socket:tesla_supercharger:output", + "socket:type2:output", + "socket:type2_combo:output", + "socket:type2_cable:output", + "socket:tesla_supercharger_ccs:output", + "socket:tesla_destination:output", + "socket:tesla_destination:output", + "socket:USB-A:output", + "socket:bosch_3pin:output", + "socket:bosch_5pin:output" + ], + "applicableUnits": [ + { + "canonicalDenomination": "kW", + "alternativeDenomination": [ + "kilowatt" + ], + "human": { + "en": "kilowatt", + "nl": "kilowatt" + } + }, + { + "canonicalDenomination": "mW", + "alternativeDenomination": [ + "megawatt" + ], + "human": { + "en": "megawatt", + "nl": "megawatt" + } + } + ], + "eraseInvalidValues": true } - ], - "eraseInvalidValues": true - } - ] + ] } \ No newline at end of file diff --git a/assets/layers/drinking_water/drinking_water.json b/assets/layers/drinking_water/drinking_water.json index 76130921f..439579473 100644 --- a/assets/layers/drinking_water/drinking_water.json +++ b/assets/layers/drinking_water/drinking_water.json @@ -33,8 +33,7 @@ "operational_status=closed" ] }, - "then": "close:#c33", - "badge": true + "then": "close:#c33" } ], "iconSize": "40,40,bottom", @@ -185,7 +184,7 @@ "icon": { "render": "pin:#6BC4F7;./assets/layers/drinking_water/drips.svg" }, - "iconOverlays": [ + "iconBadges": [ { "if": { "or": [ @@ -193,8 +192,7 @@ "operational_status=closed" ] }, - "then": "close:#c33", - "badge": true + "then": "close:#c33" } ], "iconSize": "40,40,bottom", diff --git a/assets/layers/food/food.json b/assets/layers/food/food.json index 3386e1b79..b0df677b3 100644 --- a/assets/layers/food/food.json +++ b/assets/layers/food/food.json @@ -35,8 +35,7 @@ "iconOverlays": [ { "if": "opening_hours~*", - "then": "isOpen", - "badge": true + "then": "isOpen" }, { "if": { @@ -47,8 +46,7 @@ }, "then": { "render": "circle:white;./assets/themes/fritures/Vegetarian-mark.svg" - }, - "badge": true + } } ], "label": { @@ -656,11 +654,10 @@ } ] }, - "iconOverlays": [ + "iconBadges": [ { "if": "opening_hours~*", - "then": "isOpen", - "badge": true + "then": "isOpen" }, { "if": { @@ -671,8 +668,7 @@ }, "then": { "render": "circle:white;./assets/themes/fritures/Vegetarian-mark.svg" - }, - "badge": true + } } ], "label": { diff --git a/assets/layers/playground/playground.json b/assets/layers/playground/playground.json index 87dc9b3ca..cf44f3723 100644 --- a/assets/layers/playground/playground.json +++ b/assets/layers/playground/playground.json @@ -482,8 +482,7 @@ "opening_hours~*" ] }, - "then": "isOpen", - "badge": true + "then": "isOpen" } ], "width": { @@ -541,7 +540,7 @@ "icon": { "render": "./assets/themes/playgrounds/playground.svg" }, - "iconOverlays": [ + "iconBadges": [ { "if": { "and": [ @@ -549,8 +548,7 @@ "opening_hours~*" ] }, - "then": "isOpen", - "badge": true + "then": "isOpen" } ], "iconSize": { diff --git a/assets/layers/public_bookcase/public_bookcase.json b/assets/layers/public_bookcase/public_bookcase.json index 7011728b0..a3bb5fcd4 100644 --- a/assets/layers/public_bookcase/public_bookcase.json +++ b/assets/layers/public_bookcase/public_bookcase.json @@ -45,7 +45,7 @@ ] }, "icon": { - "render": "./assets/themes/bookcases/bookcase.svg;" + "render": "./assets/themes/bookcases/bookcase.svg" }, "label": { "mappings": [ diff --git a/assets/layers/shops/shops.json b/assets/layers/shops/shops.json index c0761f84b..8233ce910 100644 --- a/assets/layers/shops/shops.json +++ b/assets/layers/shops/shops.json @@ -279,8 +279,7 @@ "iconOverlays": [ { "if": "opening_hours~*", - "then": "isOpen", - "badge": true + "then": "isOpen" } ], "width": { @@ -328,11 +327,10 @@ "icon": { "render": "./assets/themes/shops/shop.svg" }, - "iconOverlays": [ + "iconBadges": [ { "if": "opening_hours~*", - "then": "isOpen", - "badge": true + "then": "isOpen" } ], "iconSize": { diff --git a/assets/layers/sport_pitch/sport_pitch.json b/assets/layers/sport_pitch/sport_pitch.json index cc39de256..f51e63c6f 100644 --- a/assets/layers/sport_pitch/sport_pitch.json +++ b/assets/layers/sport_pitch/sport_pitch.json @@ -420,8 +420,7 @@ "opening_hours~*" ] }, - "then": "isOpen", - "badge": true + "then": "isOpen" }, { "if": { @@ -431,8 +430,7 @@ "access=no" ] }, - "then": "circle:white;./assets/layers/sport_pitch/lock.svg", - "badge": true + "then": "circle:white;./assets/layers/sport_pitch/lock.svg" } ], "width": { @@ -514,7 +512,7 @@ } ] }, - "iconOverlays": [ + "iconBadges": [ { "if": { "and": [ @@ -522,8 +520,7 @@ "opening_hours~*" ] }, - "then": "isOpen", - "badge": true + "then": "isOpen" }, { "if": { @@ -533,8 +530,7 @@ "access=no" ] }, - "then": "circle:white;./assets/layers/sport_pitch/lock.svg", - "badge": true + "then": "circle:white;./assets/layers/sport_pitch/lock.svg" } ], "iconSize": { diff --git a/assets/svg/license_info.json b/assets/svg/license_info.json index 4ac61d303..8e1eaa626 100644 --- a/assets/svg/license_info.json +++ b/assets/svg/license_info.json @@ -1411,6 +1411,26 @@ "https://www.onlinewebfonts.com/icon/197818" ] }, + { + "path": "teardrop_with_hole_green.svg", + "license": "CC-BY-SA 3.0 Unported", + "authors": [ + "Mono, derivated work User:Benoit Rochon" + ], + "sources": [ + "https://commons.wikimedia.org/wiki/File:Map_pin_icon_green.svg" + ] + }, + { + "path": "teardrop.svg", + "license": "CC-BY-SA 3.0 Unported", + "authors": [ + "Mono, derivated work User:Benoit Rochon" + ], + "sources": [ + "https://commons.wikimedia.org/wiki/File:Map_pin_icon_green.svg" + ] + }, { "path": "translate.svg", "license": "CC-BY-SA 3.0", @@ -1503,4 +1523,4 @@ "https://www.wikipedia.org/" ] } -] \ No newline at end of file +] diff --git a/assets/svg/teardrop.svg b/assets/svg/teardrop.svg new file mode 100644 index 000000000..1cc113c57 --- /dev/null +++ b/assets/svg/teardrop.svg @@ -0,0 +1,104 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/assets/svg/teardrop_with_hole_green.svg b/assets/svg/teardrop_with_hole_green.svg new file mode 100644 index 000000000..cc32242cd --- /dev/null +++ b/assets/svg/teardrop_with_hole_green.svg @@ -0,0 +1,130 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/assets/themes/climbing/climbing.json b/assets/themes/climbing/climbing.json index 7ebb37693..cd73e5e4b 100644 --- a/assets/themes/climbing/climbing.json +++ b/assets/themes/climbing/climbing.json @@ -156,8 +156,7 @@ "iconOverlays": [ { "if": "opening_hours~*", - "then": "isOpen", - "badge": true + "then": "isOpen" } ], "width": { @@ -221,11 +220,10 @@ "icon": { "render": "./assets/themes/climbing/club.svg" }, - "iconOverlays": [ + "iconBadges": [ { "if": "opening_hours~*", - "then": "isOpen", - "badge": true + "then": "isOpen" } ], "iconSize": { @@ -325,8 +323,7 @@ "iconOverlays": [ { "if": "opening_hours~*", - "then": "isOpen", - "badge": true + "then": "isOpen" } ], "width": "0", @@ -339,11 +336,10 @@ "icon": { "render": "./assets/themes/climbing/climbing_gym.svg" }, - "iconOverlays": [ + "iconBadges": [ { "if": "opening_hours~*", - "then": "isOpen", - "badge": true + "then": "isOpen" } ], "iconSize": { diff --git a/assets/themes/facadegardens/facadegardens.json b/assets/themes/facadegardens/facadegardens.json index 295c3e03e..c5fb32ff6 100644 --- a/assets/themes/facadegardens/facadegardens.json +++ b/assets/themes/facadegardens/facadegardens.json @@ -84,33 +84,27 @@ "iconOverlays": [ { "if": "plant~.*vine.*", - "then": "circle:white;./assets/themes/facadegardens/klimplant.svg", - "badge": true + "then": "circle:white;./assets/themes/facadegardens/klimplant.svg" }, { "if": "plant~.*groundcover.*", - "then": "circle:white;./assets/themes/facadegardens/bodembedekker.svg", - "badge": true + "then": "circle:white;./assets/themes/facadegardens/bodembedekker.svg" }, { "if": "edible=true", - "then": "circle:white;./assets/themes/facadegardens/eetbaar.svg", - "badge": true + "then": "circle:white;./assets/themes/facadegardens/eetbaar.svg" }, { "if": "rain_barel=yes", - "then": "circle:white;./assets/themes/facadegardens/gevelton.svg", - "badge": true + "then": "circle:white;./assets/themes/facadegardens/gevelton.svg" }, { "if": "plant~.*shrub.*", - "then": "circle:white;./assets/themes/facadegardens/struik.svg", - "badge": true + "then": "circle:white;./assets/themes/facadegardens/struik.svg" }, { "if": "plant~.*flower.*", - "then": "circle:white;./assets/themes/facadegardens/bloei.svg", - "badge": true + "then": "circle:white;./assets/themes/facadegardens/bloei.svg" } ], "tagRenderings": [ @@ -486,36 +480,30 @@ } ] }, - "iconOverlays": [ + "iconBadges": [ { "if": "plant~.*vine.*", - "then": "circle:white;./assets/themes/facadegardens/klimplant.svg", - "badge": true + "then": "circle:white;./assets/themes/facadegardens/klimplant.svg" }, { "if": "plant~.*groundcover.*", - "then": "circle:white;./assets/themes/facadegardens/bodembedekker.svg", - "badge": true + "then": "circle:white;./assets/themes/facadegardens/bodembedekker.svg" }, { "if": "edible=true", - "then": "circle:white;./assets/themes/facadegardens/eetbaar.svg", - "badge": true + "then": "circle:white;./assets/themes/facadegardens/eetbaar.svg" }, { "if": "rain_barel=yes", - "then": "circle:white;./assets/themes/facadegardens/gevelton.svg", - "badge": true + "then": "circle:white;./assets/themes/facadegardens/gevelton.svg" }, { "if": "plant~.*shrub.*", - "then": "circle:white;./assets/themes/facadegardens/struik.svg", - "badge": true + "then": "circle:white;./assets/themes/facadegardens/struik.svg" }, { "if": "plant~.*flower.*", - "then": "circle:white;./assets/themes/facadegardens/bloei.svg", - "badge": true + "then": "circle:white;./assets/themes/facadegardens/bloei.svg" } ], "iconSize": { diff --git a/assets/themes/postboxes/postboxes.json b/assets/themes/postboxes/postboxes.json index d8ca6024a..b871943ad 100644 --- a/assets/themes/postboxes/postboxes.json +++ b/assets/themes/postboxes/postboxes.json @@ -154,8 +154,7 @@ "iconOverlays": [ { "if": "opening_hours~*", - "then": "isOpen", - "badge": true + "then": "isOpen" } ], "width": { @@ -196,11 +195,10 @@ "icon": { "render": "square:white;./assets/themes/postboxes/post_office.svg" }, - "iconOverlays": [ + "iconBadges": [ { "if": "opening_hours~*", - "then": "isOpen", - "badge": true + "then": "isOpen" } ], "iconSize": { diff --git a/css/index-tailwind-output.css b/css/index-tailwind-output.css index 7ba52d19b..463cd5c3a 100644 --- a/css/index-tailwind-output.css +++ b/css/index-tailwind-output.css @@ -748,6 +748,14 @@ video { right: 0.75rem; } +.bottom-0 { + bottom: 0px; +} + +.right-1\/3 { + right: 33.333333%; +} + .top-0 { top: 0px; } @@ -760,10 +768,6 @@ video { right: 0px; } -.bottom-0 { - bottom: 0px; -} - .isolate { isolation: isolate; } @@ -856,6 +860,10 @@ video { margin-left: 0.75rem; } +.mt-1 { + margin-top: 0.25rem; +} + .mb-2 { margin-bottom: 0.5rem; } @@ -884,10 +892,6 @@ video { margin-bottom: 2.5rem; } -.mt-1 { - margin-top: 0.25rem; -} - .mt-0 { margin-top: 0px; } @@ -988,6 +992,10 @@ video { height: 2rem; } +.h-1\/2 { + height: 50%; +} + .h-12 { height: 3rem; } @@ -1048,6 +1056,10 @@ video { width: 2rem; } +.w-0 { + width: 0px; +} + .w-12 { width: 3rem; } @@ -1060,10 +1072,6 @@ video { width: 2.75rem; } -.w-0 { - width: 0px; -} - .w-16 { width: 4rem; } @@ -1810,6 +1818,15 @@ html, body { display: block ruby; } +.badge { +} + +.badge svg { + /*Workaround for leaflet*/ + width: unset !important; + height: 100% !important; +} + svg, img { box-sizing: content-box; width: 100%; diff --git a/index.css b/index.css index 6eb113abc..8ee0c0e10 100644 --- a/index.css +++ b/index.css @@ -91,6 +91,15 @@ html, body { display: block ruby; } +.badge { +} + +.badge svg { + /*Workaround for leaflet*/ + width: unset !important; + height: 100% !important; +} + svg, img { box-sizing: content-box; width: 100%; diff --git a/scripts/lint.ts b/scripts/lint.ts index 50b1d9e25..8323218fe 100644 --- a/scripts/lint.ts +++ b/scripts/lint.ts @@ -26,35 +26,37 @@ function fixLayerConfig(config: LayerConfigJson): void { } } } - - if(config.mapRendering === undefined || true){ + + if (config.mapRendering === undefined || true) { // This is a legacy format, lets create a pointRendering - let location: ("point"|"centroid")[] = ["point"] - let wayHandling: number = config.wayHandling - if(wayHandling === 2){ + let location: ("point" | "centroid")[] = ["point"] + let wayHandling: number = config["wayHandling"] ?? 0 + if (wayHandling === 2) { location = ["point", "centroid"] } config.mapRendering = [ { icon: config["icon"], - iconOverlays: config["iconOverlays"], + iconBadges: config["iconOverlays"], label: config["label"], iconSize: config["iconSize"], location, rotation: config["rotation"] } ] - - if(wayHandling !== 1){ + + if (wayHandling !== 1) { const lineRenderConfig = { color: config["color"], width: config["width"], dashArray: config["dashArray"] } - if(Object.keys(lineRenderConfig).length > 0){ + if (Object.keys(lineRenderConfig).length > 0) { config.mapRendering.push(lineRenderConfig) } } + + /*delete config["color"] delete config["width"] delete config["dashArray"] @@ -64,11 +66,23 @@ function fixLayerConfig(config: LayerConfigJson): void { delete config["label"] delete config["iconSize"] delete config["rotation"] + delete config["wayHandling"] */ - - + } - + + for (const mapRenderingElement of config.mapRendering) { + if (mapRenderingElement["iconOverlays"] !== undefined) { + mapRenderingElement["iconBadges"] = mapRenderingElement["iconOverlays"] + } + for (const overlay of mapRenderingElement["iconBadges"] ?? []) { + if (overlay["badge"] !== true) { + console.log("Warning: non-overlay element for ", config.id) + } + delete overlay["badge"] + } + } + } const layerFiles = ScriptUtils.getLayerFiles(); @@ -86,11 +100,11 @@ for (const themeFile of themeFiles) { // @ts-ignore fixLayerConfig(layerConfig) } - - if(themeFile.parsed["roamingRenderings"] !== undefined && themeFile.parsed["roamingRenderings"].length == 0){ + + if (themeFile.parsed["roamingRenderings"] !== undefined && themeFile.parsed["roamingRenderings"].length == 0) { delete themeFile.parsed["roamingRenderings"] } - + writeFileSync(themeFile.path, JSON.stringify(themeFile.parsed, null, " ")) } //*/ From 0c0ef48a96c6baba6037554456dabf118bfe66b1 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 22 Oct 2021 01:07:32 +0200 Subject: [PATCH 15/95] First working version with multi-rendering --- Logic/FeatureSource/FeaturePipeline.ts | 2 - .../WayHandlingApplyingFeatureSource.ts | 78 ++++++++++--------- .../Json/LineRenderingConfigJson.ts | 7 +- Models/ThemeConfig/LayerConfig.ts | 33 +------- Models/ThemeConfig/LineRenderingConfig.ts | 74 +++++++++--------- Models/ThemeConfig/PointRenderingConfig.ts | 11 ++- Models/ThemeConfig/TagRenderingConfig.ts | 6 ++ UI/BigComponents/AddNewMarker.ts | 4 +- UI/BigComponents/SimpleAddUI.ts | 2 +- UI/ShowDataLayer/ShowDataLayer.ts | 58 +++++++++----- Utils.ts | 8 +- .../public_bookcase/public_bookcase.json | 2 +- assets/themes/sidewalks/sidewalks.json | 61 +++++++++++++++ package-lock.json | 11 +++ package.json | 1 + scripts/lint.ts | 5 ++ 16 files changed, 228 insertions(+), 135 deletions(-) create mode 100644 assets/themes/sidewalks/sidewalks.json diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index e8b77bb77..9aa8937c3 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -114,9 +114,7 @@ export default class FeaturePipeline { // This will already contain the merged features for this tile. In other words, this will only be triggered once for every tile const srcFiltered = new FilteringFeatureSource(state, src.tileIndex, - new WayHandlingApplyingFeatureSource( new ChangeGeometryApplicator(src, state.changes) - ) ) handleFeatureSource(srcFiltered) diff --git a/Logic/FeatureSource/Sources/WayHandlingApplyingFeatureSource.ts b/Logic/FeatureSource/Sources/WayHandlingApplyingFeatureSource.ts index cb36c4b21..f996ad9a0 100644 --- a/Logic/FeatureSource/Sources/WayHandlingApplyingFeatureSource.ts +++ b/Logic/FeatureSource/Sources/WayHandlingApplyingFeatureSource.ts @@ -1,58 +1,62 @@ /** - * This is the part of the pipeline which introduces extra points at the center of an area (but only if this is demanded by the wayhandling) + * This feature source helps the ShowDataLayer class: it introduces the necessary extra features and indiciates with what renderConfig it should be rendered. */ import {UIEventSource} from "../../UIEventSource"; -import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"; import {GeoOperations} from "../../GeoOperations"; -import {FeatureSourceForLayer} from "../FeatureSource"; +import FeatureSource from "../FeatureSource"; +import PointRenderingConfig from "../../../Models/ThemeConfig/PointRenderingConfig"; +import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"; -export default class WayHandlingApplyingFeatureSource implements FeatureSourceForLayer { - public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; - public readonly name; - public readonly layer; - constructor(upstream: FeatureSourceForLayer) { - - this.name = "Wayhandling(" + upstream.name + ")"; - this.layer = upstream.layer - const layer = upstream.layer.layerDef; - - if (layer.wayHandling === LayerConfig.WAYHANDLING_DEFAULT) { - // We don't have to do anything fancy - // lets just wire up the upstream - this.features = upstream.features; - return; - } +export default class RenderingMultiPlexerFeatureSource { + public readonly features: UIEventSource<(any & {pointRenderingIndex: number | undefined, lineRenderingIndex: number | undefined})[]>; + constructor(upstream: FeatureSource, layer: LayerConfig) { this.features = upstream.features.map( features => { if (features === undefined) { return; } - const newFeatures: { feature: any, freshness: Date }[] = []; + + const pointRenderObjects: { rendering: PointRenderingConfig, index: number }[] = layer.mapRendering.map((r, i) => ({rendering: r, index: i})) + const pointRenderings = pointRenderObjects.filter(r => r.rendering.location.has("point")) + const centroidRenderings = pointRenderObjects.filter(r => r.rendering.location.has("centroid")) + + const lineRenderObjects = layer.lineRendering + + const withIndex : (any & {pointRenderingIndex: number | undefined, lineRenderingIndex: number | undefined})[] = []; + for (const f of features) { const feat = f.feature; + + if(feat.geometry.type === "Point"){ - if (layer.wayHandling === LayerConfig.WAYHANDLING_DEFAULT) { - newFeatures.push(f); - continue; - } + for (const rendering of pointRenderings) { + withIndex.push({ + ...feat, + pointRenderingIndex: rendering.index + }) + } + }else{ + // This is a a line + for (const rendering of centroidRenderings) { + withIndex.push({ + ...GeoOperations.centerpoint(feat), + pointRenderingIndex: rendering.index + }) + } + + + for (let i = 0; i < lineRenderObjects.length; i++){ + withIndex.push({ + ...feat, + lineRenderingIndex:i + }) + } - if (feat.geometry.type === "Point") { - newFeatures.push(f); - // feature is a point, nothing to do here - continue; - } - - // Create the copy - const centerPoint = GeoOperations.centerpoint(feat); - - newFeatures.push({feature: centerPoint, freshness: f.freshness}); - if (layer.wayHandling === LayerConfig.WAYHANDLING_CENTER_AND_WAY) { - newFeatures.push(f); } } - return newFeatures; + return withIndex; } ); diff --git a/Models/ThemeConfig/Json/LineRenderingConfigJson.ts b/Models/ThemeConfig/Json/LineRenderingConfigJson.ts index 5330373c9..80ee19009 100644 --- a/Models/ThemeConfig/Json/LineRenderingConfigJson.ts +++ b/Models/ThemeConfig/Json/LineRenderingConfigJson.ts @@ -1,5 +1,4 @@ import {TagRenderingConfigJson} from "./TagRenderingConfigJson"; -import {AndOrTagConfigJson} from "./TagConfigJson"; /** * The LineRenderingConfig gives all details onto how to render a single line of a feature. @@ -27,4 +26,10 @@ export default interface LineRenderingConfigJson { * Default value: "" (empty string == full line) */ dashArray?: string | TagRenderingConfigJson + + /** + * The number of pixels this line should be moved. + * Use a positive numbe to move to the right, a negative to move to the left (left/right as defined by the drawing direction of the line) + */ + offset?: number | TagRenderingConfigJson } \ No newline at end of file diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts index 7bb4db470..566c55dfa 100644 --- a/Models/ThemeConfig/LayerConfig.ts +++ b/Models/ThemeConfig/LayerConfig.ts @@ -6,9 +6,6 @@ import PresetConfig from "./PresetConfig"; import {LayerConfigJson} from "./Json/LayerConfigJson"; import Translations from "../../UI/i18n/Translations"; import {TagUtils} from "../../Logic/Tags/TagUtils"; -import {Utils} from "../../Utils"; -import {UIEventSource} from "../../Logic/UIEventSource"; -import BaseUIElement from "../../UI/BaseUIElement"; import FilterConfig from "./FilterConfig"; import {Unit} from "../Unit"; import DeleteConfig from "./DeleteConfig"; @@ -207,10 +204,6 @@ export default class LayerConfig extends WithContextLoader{ .map((r, i) => new LineRenderingConfig(r, context+".mapRendering["+i+"]")) - if(this.mapRendering.length > 1){ - throw "Invalid maprendering for "+this.id+", currently only one mapRendering is supported!" - } - this.tagRenderings = this.trs(json.tagRenderings, false); const missingIds = json.tagRenderings?.filter(tr => typeof tr !== "string" && tr["builtin"] === undefined && tr["id"] === undefined) ?? []; @@ -284,31 +277,7 @@ export default class LayerConfig extends WithContextLoader{ } - public GenerateLeafletStyle( - tags: UIEventSource, - clickable: boolean - ): { - icon: { - html: BaseUIElement; - iconSize: [number, number]; - iconAnchor: [number, number]; - popupAnchor: [number, number]; - iconUrl: string; - className: string; - }; - color: string; - weight: number; - dashArray: number[]; - } { - - - const icon = this.mapRendering[0].GenerateLeafletStyle(tags, clickable) - const lineStyle = (this.lineRendering[0] ?? new LineRenderingConfig({}, "default"))?.GenerateLeafletStyle(tags) - return { - icon: icon, - ...lineStyle - }; - } + public ExtractImages(): Set { const parts: Set[] = []; diff --git a/Models/ThemeConfig/LineRenderingConfig.ts b/Models/ThemeConfig/LineRenderingConfig.ts index f1656c725..04e5bf2ec 100644 --- a/Models/ThemeConfig/LineRenderingConfig.ts +++ b/Models/ThemeConfig/LineRenderingConfig.ts @@ -8,16 +8,17 @@ import LineRenderingConfigJson from "./Json/LineRenderingConfigJson"; export default class LineRenderingConfig extends WithContextLoader { - public readonly color: TagRenderingConfig; - public readonly width: TagRenderingConfig; - public readonly dashArray: TagRenderingConfig; + public readonly color: TagRenderingConfig; + public readonly width: TagRenderingConfig; + public readonly dashArray: TagRenderingConfig; + public readonly offset: TagRenderingConfig; constructor(json: LineRenderingConfigJson, context: string) { super(json, context) this.color = this.tr("color", "#0000ff"); this.width = this.tr("width", "7"); this.dashArray = this.tr("dashArray", ""); - + this.offset = this.tr("offset", "0"); } @@ -27,40 +28,43 @@ export default class LineRenderingConfig extends WithContextLoader { { color: string, weight: number, - dashArray: number[] + dashArray: number[], + offset: number + } { + function rendernum(tr: TagRenderingConfig, deflt: number) { + const str = Number(render(tr, "" + deflt)); + const n = Number(str); + if (isNaN(n)) { + return deflt; + } + return n; } - { - function rendernum(tr: TagRenderingConfig, deflt: number) { - const str = Number(render(tr, "" + deflt)); - const n = Number(str); - if (isNaN(n)) { - return deflt; - } - return n; - } - function render(tr: TagRenderingConfig, deflt?: string) { - if (tags === undefined) { - return deflt - } - const str = tr?.GetRenderValue(tags.data)?.txt ?? deflt; - return Utils.SubstituteKeys(str, tags.data).replace(/{.*}/g, ""); - } - const dashArray = render(this.dashArray)?.split(" ")?.map(Number); - let color = render(this.color, "#00f"); + function render(tr: TagRenderingConfig, deflt?: string) { + if (tags === undefined) { + return deflt + } + const str = tr?.GetRenderValue(tags.data)?.txt ?? deflt; + return Utils.SubstituteKeys(str, tags.data)?.replace(/{.*}/g, ""); + } - if (color.startsWith("--")) { - color = getComputedStyle(document.body).getPropertyValue( - "--catch-detail-color" - ); - } + const dashArray = render(this.dashArray)?.split(" ")?.map(Number); + let color = render(this.color, "#00f"); + if (color.startsWith("--")) { + color = getComputedStyle(document.body).getPropertyValue( + "--catch-detail-color" + ); + } - const weight = rendernum(this.width, 5); - return { - color, - weight, - dashArray - } + const weight = rendernum(this.width, 5); + const offset = rendernum(this.offset, 0) + console.log("Calculated offset:", offset, "for", this.offset) + return { + color, + weight, + dashArray, + offset + } } - + } \ No newline at end of file diff --git a/Models/ThemeConfig/PointRenderingConfig.ts b/Models/ThemeConfig/PointRenderingConfig.ts index 4da8ae32f..e50891c8e 100644 --- a/Models/ThemeConfig/PointRenderingConfig.ts +++ b/Models/ThemeConfig/PointRenderingConfig.ts @@ -15,6 +15,8 @@ import {VariableUiElement} from "../../UI/Base/VariableUIElement"; export default class PointRenderingConfig extends WithContextLoader { + public readonly location: Set<"point" | "centroid"> + public readonly icon: TagRenderingConfig; public readonly iconBadges: { if: TagsFilter; then: TagRenderingConfig }[]; public readonly iconSize: TagRenderingConfig; @@ -23,6 +25,11 @@ export default class PointRenderingConfig extends WithContextLoader { constructor(json: PointRenderingConfigJson, context: string) { super(json, context) + this.location = new Set(json.location) + + if(this.location.size == 0){ + throw "A pointRendering should have at least one 'location' to defined where it should be rendered. (At "+context+".location)" + } this.icon = this.tr("icon", ""); this.iconBadges = (json.iconBadges ?? []).map((overlay, i) => { let tr : TagRenderingConfig; @@ -114,7 +121,7 @@ export default class PointRenderingConfig extends WithContextLoader { return new VariableUiElement(tags.map(tags => { const rotation = self.rotation?.GetRenderValue(tags)?.txt ?? "0deg" - const htmlDefs = self.icon.GetRenderValue(tags)?.txt + const htmlDefs = Utils.SubstituteKeys(self.icon.GetRenderValue(tags)?.txt, tags) let defaultPin : BaseUIElement = undefined if(self.label === undefined){ defaultPin = Svg.teardrop_with_hole_green_svg() @@ -137,7 +144,7 @@ export default class PointRenderingConfig extends WithContextLoader { return undefined } - const htmlDefs = badge.then.GetRenderValue(tags)?.txt + const htmlDefs = Utils.SubstituteKeys(badge.then.GetRenderValue(tags)?.txt, tags) const badgeElement= PointRenderingConfig.FromHtmlMulti(htmlDefs, "0", true)?.SetClass("block relative") if(badgeElement === undefined){ return undefined; diff --git a/Models/ThemeConfig/TagRenderingConfig.ts b/Models/ThemeConfig/TagRenderingConfig.ts index a4499f46c..e2b634839 100644 --- a/Models/ThemeConfig/TagRenderingConfig.ts +++ b/Models/ThemeConfig/TagRenderingConfig.ts @@ -47,6 +47,12 @@ export default class TagRenderingConfig { this.condition = null; } + if(typeof json === "number"){ + this.render =Translations.WT( ""+json) + return; + } + + if (json === undefined) { throw "Initing a TagRenderingConfig with undefined in " + context; } diff --git a/UI/BigComponents/AddNewMarker.ts b/UI/BigComponents/AddNewMarker.ts index 09210bb52..e51152c7d 100644 --- a/UI/BigComponents/AddNewMarker.ts +++ b/UI/BigComponents/AddNewMarker.ts @@ -16,12 +16,12 @@ export default class AddNewMarker extends Combine { const layer = filteredLayer.layerDef; for (const preset of filteredLayer.layerDef.presets) { const tags = TagUtils.KVtoProperties(preset.tags) - const icon = layer.GenerateLeafletStyle(new UIEventSource(tags), false).icon.html + const icon = layer.mapRendering[0].GenerateLeafletStyle(new UIEventSource(tags), false).html .SetClass("block relative") .SetStyle("width: 42px; height: 42px;"); icons.push(icon) if (last === undefined) { - last = layer.GenerateLeafletStyle(new UIEventSource(tags), false).icon.html + last = layer.mapRendering[0].GenerateLeafletStyle(new UIEventSource(tags), false).html .SetClass("block relative") .SetStyle("width: 42px; height: 42px;"); } diff --git a/UI/BigComponents/SimpleAddUI.ts b/UI/BigComponents/SimpleAddUI.ts index 84e14b682..6a5b37cb9 100644 --- a/UI/BigComponents/SimpleAddUI.ts +++ b/UI/BigComponents/SimpleAddUI.ts @@ -368,7 +368,7 @@ export default class SimpleAddUI extends Toggle { for (const preset of presets) { const tags = TagUtils.KVtoProperties(preset.tags ?? []); - let icon: () => BaseUIElement = () => layer.layerDef.GenerateLeafletStyle(new UIEventSource(tags), false).icon.html + let icon: () => BaseUIElement = () => layer.layerDef.mapRendering[0]. GenerateLeafletStyle(new UIEventSource(tags), false).html .SetClass("w-12 h-12 block relative"); const presetInfo: PresetInfo = { tags: preset.tags, diff --git a/UI/ShowDataLayer/ShowDataLayer.ts b/UI/ShowDataLayer/ShowDataLayer.ts index 2f957ed15..8c0b885ed 100644 --- a/UI/ShowDataLayer/ShowDataLayer.ts +++ b/UI/ShowDataLayer/ShowDataLayer.ts @@ -6,12 +6,14 @@ import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; import FeatureInfoBox from "../Popup/FeatureInfoBox"; import {ShowDataLayerOptions} from "./ShowDataLayerOptions"; import {ElementStorage} from "../../Logic/ElementStorage"; +import RenderingMultiPlexerFeatureSource from "../../Logic/FeatureSource/Sources/WayHandlingApplyingFeatureSource"; +import 'leaflet-polylineoffset'; export default class ShowDataLayer { private readonly _leafletMap: UIEventSource; private readonly _enablePopups: boolean; - private readonly _features: UIEventSource<{ feature: any }[]> + private readonly _features: RenderingMultiPlexerFeatureSource private readonly _layerToShow: LayerConfig; private readonly _selectedElement: UIEventSource private readonly allElements : ElementStorage @@ -41,8 +43,7 @@ export default class ShowDataLayer { console.error("Invalid ShowDataLayer invocation: options.features is undefed") throw "Invalid ShowDataLayer invocation: options.features is undefed" } - const features = options.features.features.map(featFreshes => featFreshes.map(ff => ff.feature)); - this._features = features; + this._features = new RenderingMultiPlexerFeatureSource(options.features, options.layerToShow); this._layerToShow = options.layerToShow; this._selectedElement = options.selectedElement this.allElements = options.allElements; @@ -53,7 +54,7 @@ export default class ShowDataLayer { } ); - features.addCallback(_ => self.update(options)); + this._features.features.addCallback(_ => self.update(options)); options.doShowLayer?.addCallback(doShow => { const mp = options.leafletMap.data; if (mp == undefined) { @@ -109,7 +110,7 @@ export default class ShowDataLayer { } private update(options: ShowDataLayerOptions) { - if (this._features.data === undefined) { + if (this._features.features.data === undefined) { return; } this.isDirty = true; @@ -139,13 +140,25 @@ export default class ShowDataLayer { onEachFeature: (feature, leafletLayer) => self.postProcessFeature(feature, leafletLayer) }); - const allFeats = this._features.data; + const allFeats = this._features.features.data; for (const feat of allFeats) { if (feat === undefined) { continue } try { - this.geoLayer.addData(feat); + + if((feat.geometry.type === "LineString" || feat.geometry.type === "MultiLineString")) { + const coords = L.GeoJSON.coordsToLatLngs(feat.geometry.coordinates) + const tagsSource = this.allElements?.addOrGetElement(feat) ?? new UIEventSource(feat.properties); + const lineStyle = this._layerToShow.lineRendering[feat.lineRenderingIndex].GenerateLeafletStyle(tagsSource) + const offsettedLine = L.polyline(coords, lineStyle); + + this.postProcessFeature(feat, offsettedLine) + + offsettedLine.addTo(this.geoLayer) + }else{ + this.geoLayer.addData(feat); + } } catch (e) { console.error("Could not add ", feat, "to the geojson layer in leaflet due to", e, e.stack) } @@ -170,7 +183,20 @@ export default class ShowDataLayer { const tagsSource = this.allElements?.addOrGetElement(feature) ?? new UIEventSource(feature.properties); // Every object is tied to exactly one layer const layer = this._layerToShow - return layer?.GenerateLeafletStyle(tagsSource, true); + + const pointRenderingIndex = feature.pointRenderingIndex + const lineRenderingIndex = feature.lineRenderingIndex + + if(pointRenderingIndex !== undefined){ + return { + icon: layer.mapRendering[pointRenderingIndex].GenerateLeafletStyle(tagsSource, this._enablePopups) + } + } + if(lineRenderingIndex !== undefined){ + return layer.lineRendering[lineRenderingIndex].GenerateLeafletStyle(tagsSource) + } + + throw "Neither lineRendering nor mapRendering defined for "+feature } private pointToLayer(feature, latLng): L.Layer { @@ -185,20 +211,14 @@ export default class ShowDataLayer { let tagSource = this.allElements?.getEventSourceById(feature.properties.id) ?? new UIEventSource(feature.properties) const clickable = !(layer.title === undefined && (layer.tagRenderings ?? []).length === 0) - const style = layer.GenerateLeafletStyle(tagSource, clickable); - const baseElement = style.icon.html; + let style : any = layer.mapRendering[feature.pointRenderingIndex].GenerateLeafletStyle(tagSource, clickable); + const baseElement = style.html; if (!this._enablePopups) { baseElement.SetStyle("cursor: initial !important") } + style.html = style.html.ConstructElement() return L.marker(latLng, { - icon: L.divIcon({ - html: baseElement.ConstructElement(), - className: style.icon.className, - iconAnchor: style.icon.iconAnchor, - iconUrl: style.icon.iconUrl ?? "./assets/svg/bug.svg", - popupAnchor: style.icon.popupAnchor, - iconSize: style.icon.iconSize - }) + icon: L.divIcon(style) }); } @@ -228,7 +248,7 @@ export default class ShowDataLayer { let infobox: FeatureInfoBox = undefined; - const id = `popup-${feature.properties.id}-${feature.geometry.type}-${this.showDataLayerid}-${this._cleanCount}` + const id = `popup-${feature.properties.id}-${feature.geometry.type}-${this.showDataLayerid}-${this._cleanCount}-${feature.pointerRenderingIndex ?? feature.lineRenderingIndex}` popup.setContent(`
Popup for ${feature.properties.id} ${feature.geometry.type} ${id} is loading
`) leafletLayer.on("popupopen", () => { if (infobox === undefined) { diff --git a/Utils.ts b/Utils.ts index a732f1ab8..4c80de5b0 100644 --- a/Utils.ts +++ b/Utils.ts @@ -165,8 +165,10 @@ export class Utils { return [a.substr(0, index), a.substr(index + sep.length)]; } - public static SubstituteKeys(txt: string, tags: any) { - + public static SubstituteKeys(txt: string | undefined, tags: any): string | undefined { + if (txt === undefined) { + return undefined + } const regex = /.*{([^}]*)}.*/ let match = txt.match(regex) @@ -176,7 +178,7 @@ export class Utils { txt = txt.replace("{" + key + "}", tags[key] ?? "") match = txt.match(regex) } - + return txt; } diff --git a/assets/layers/public_bookcase/public_bookcase.json b/assets/layers/public_bookcase/public_bookcase.json index a3bb5fcd4..65f017ee3 100644 --- a/assets/layers/public_bookcase/public_bookcase.json +++ b/assets/layers/public_bookcase/public_bookcase.json @@ -489,7 +489,7 @@ "mapRendering": [ { "icon": { - "render": "./assets/themes/bookcases/bookcase.svg;" + "render": "./assets/themes/bookcases/bookcase.svg" }, "label": { "mappings": [ diff --git a/assets/themes/sidewalks/sidewalks.json b/assets/themes/sidewalks/sidewalks.json new file mode 100644 index 000000000..8890b70f6 --- /dev/null +++ b/assets/themes/sidewalks/sidewalks.json @@ -0,0 +1,61 @@ +{ + "id": "sidewalks", + "title": { + "en": "Sidewalks" + }, + "shortDescription": { + "en": "Sidewalk mapping" + }, + "description": { + "en": "Experimental theme" + }, + "language": [ + "en" + ], + "maintainer": "", + "icon": "./assets/svg/bug.svg", + "version": "0", + "startLat": 0, + "startLon": 0, + "startZoom": 1, + "widenFactor": 0.05, + "socialImage": "", + "layers": [ + { + "id": "sidewalks", + "name": { + "en": "Sidewalks" + }, + "minzoom": 12, + "source": { + "osmTags": "highway=residential" + }, + "title": { + "render": { + "en": "Street {name}" + } + }, + "description": { + "en": "Layer showing sidewalks of highways" + }, + "tagRenderings": [], + "mapRendering": [ + { + "color": "#ddd", + "width": 8 + }, + { + "color": "#888", + "width": 8, + "offset": -8 + }, + { + "color": "#888", + "width": 8, + "offset": 8 + } + ], + "allowSplit": true + } + ] +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index ca84f2e65..fc0315a4a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "jspdf": "^2.3.1", "latlon2country": "^1.1.3", "leaflet": "^1.7.1", + "leaflet-polylineoffset": "^1.1.1", "leaflet-providers": "^1.13.0", "leaflet-simple-map-screenshoter": "^0.4.4", "leaflet.markercluster": "^1.4.1", @@ -10034,6 +10035,11 @@ "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.7.1.tgz", "integrity": "sha512-/xwPEBidtg69Q3HlqPdU3DnrXQOvQU/CCHA1tcDQVzOwm91YMYaILjNp7L4Eaw5Z4sOYdbBz6koWyibppd8Zqw==" }, + "node_modules/leaflet-polylineoffset": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/leaflet-polylineoffset/-/leaflet-polylineoffset-1.1.1.tgz", + "integrity": "sha512-WcEjAROx9IhIVwSMoFy9p2QBCG9YeuGtJl4ZdunIgj4xbCdTrUkBj8JdonUeCyLPnD2/Vrem/raOPHm5LvebSw==" + }, "node_modules/leaflet-providers": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/leaflet-providers/-/leaflet-providers-1.13.0.tgz", @@ -25964,6 +25970,11 @@ "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.7.1.tgz", "integrity": "sha512-/xwPEBidtg69Q3HlqPdU3DnrXQOvQU/CCHA1tcDQVzOwm91YMYaILjNp7L4Eaw5Z4sOYdbBz6koWyibppd8Zqw==" }, + "leaflet-polylineoffset": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/leaflet-polylineoffset/-/leaflet-polylineoffset-1.1.1.tgz", + "integrity": "sha512-WcEjAROx9IhIVwSMoFy9p2QBCG9YeuGtJl4ZdunIgj4xbCdTrUkBj8JdonUeCyLPnD2/Vrem/raOPHm5LvebSw==" + }, "leaflet-providers": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/leaflet-providers/-/leaflet-providers-1.13.0.tgz", diff --git a/package.json b/package.json index ea7d04f0f..21a1a7e8e 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ "jspdf": "^2.3.1", "latlon2country": "^1.1.3", "leaflet": "^1.7.1", + "leaflet-polylineoffset": "^1.1.1", "leaflet-providers": "^1.13.0", "leaflet-simple-map-screenshoter": "^0.4.4", "leaflet.markercluster": "^1.4.1", diff --git a/scripts/lint.ts b/scripts/lint.ts index 8323218fe..633c4a98b 100644 --- a/scripts/lint.ts +++ b/scripts/lint.ts @@ -13,6 +13,11 @@ import LineRenderingConfigJson from "../Models/ThemeConfig/Json/LineRenderingCon * In place fix */ function fixLayerConfig(config: LayerConfigJson): void { + if(config["overpassTags"]){ + config.source.osmTags = config["overpassTags"] + delete config["overpassTags"] + } + if (config.tagRenderings !== undefined) { for (const tagRendering of config.tagRenderings) { if (tagRendering["#"] !== undefined) { From 20fa5028d9a77094af79e9400cfa32774bafa30c Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 22 Oct 2021 01:42:44 +0200 Subject: [PATCH 16/95] First working version with multi-rendering --- Logic/SimpleMetaTagger.ts | 116 +++++++++++++++--- .../Json/LineRenderingConfigJson.ts | 5 +- Models/ThemeConfig/LineRenderingConfig.ts | 6 +- Models/ThemeConfig/TagRenderingConfig.ts | 2 +- assets/themes/sidewalks/sidewalks.json | 21 +++- 5 files changed, 129 insertions(+), 21 deletions(-) diff --git a/Logic/SimpleMetaTagger.ts b/Logic/SimpleMetaTagger.ts index 7d42e9d01..2934f93e2 100644 --- a/Logic/SimpleMetaTagger.ts +++ b/Logic/SimpleMetaTagger.ts @@ -26,7 +26,7 @@ export default class SimpleMetaTagger { "_last_edit:changeset", "_last_edit:timestamp", "_version_number", - "_backend"], + "_backend"], doc: "Information about the last edit of this object." }, (feature) => {/*Note: also called by 'UpdateTagsFromOsmAPI'*/ @@ -67,17 +67,100 @@ export default class SimpleMetaTagger { private static layerInfo = new SimpleMetaTagger( { doc: "The layer-id to which this feature belongs. Note that this might be return any applicable if `passAllFeatures` is defined.", - keys:["_layer"], + keys: ["_layer"], includesDates: false, }, (feature, freshness, layer) => { - if(feature.properties._layer === layer.id){ + if (feature.properties._layer === layer.id) { return false; } feature.properties._layer = layer.id return true; } ) + private static noBothButLeftRight = new SimpleMetaTagger( + { + keys: ["sidewalk:left", "sidewalk:right", "generic_key:left:property", "generic_key:right:property"], + doc: "Rewrites tags from 'generic_key:both:property' as 'generic_key:left:property' and 'generic_key:right:property' (and similar for sidewalk tagging). Note that this rewritten tags _will be reuploaded on a change_. To prevent to much unrelated retagging, this is only enabled if the layer has at least some lineRenderings with offset defined", + includesDates: false, + cleanupRetagger: true + }, + ((feature, state, layer) => { + + if(!layer.lineRendering.some(lr => lr.leftRightSensitive)){ + return; + } + + const tgs = feature.properties; + let somethingChanged = false + + /** + * Sets the key onto the properties (but doesn't overwrite if already existing) + */ + function set(key, value) { + if (tgs[key] === undefined || tgs[key] === "") { + tgs[key] = value + somethingChanged = true + } + } + + if (tgs["sidewalk"]) { + + const v = tgs["sidewalk"] + switch (v) { + case "none": + case "no": + set("sidewalk:left", "no"); + set("sidewalk:right", "no"); + break + case "both": + set("sidewalk:left", "yes"); + set("sidewalk:right", "yes"); + break; + case "left": + set("sidewalk:left", "yes"); + set("sidewalk:right", "no"); + break; + case "right": + set("sidewalk:left", "no"); + set("sidewalk:right", "yes"); + break; + default: + set("sidewalk:left", v); + set("sidewalk:right", v); + break; + } + delete tgs["sidewalk"] + somethingChanged = true + } + + + const regex = /\([^:]*\):both:\(.*\)/ + for (const key in tgs) { + const v = tgs[key] + if (key.endsWith(":both")) { + const strippedKey = key.substring(0, key.length - ":both".length) + set(strippedKey + ":left", v) + set(strippedKey + ":right", v) + delete tgs[key] + continue + } + + const match = key.match(regex) + if (match !== null) { + const strippedKey = match[1] + const property = match[1] + set(strippedKey + ":left:" + property, v) + set(strippedKey + ":right:" + property, v) + console.log("Left-right rewritten " + key) + delete tgs[key] + } + } + + + return somethingChanged + }) + ) private static surfaceArea = new SimpleMetaTagger( { keys: ["_surface", "_surface:ha"], @@ -85,12 +168,12 @@ export default class SimpleMetaTagger { isLazy: true }, (feature => { - + Object.defineProperty(feature.properties, "_surface", { enumerable: false, configurable: true, get: () => { - const sqMeters = ""+ GeoOperations.surfaceAreaInSqMeters(feature); + const sqMeters = "" + GeoOperations.surfaceAreaInSqMeters(feature); delete feature.properties["_surface"] feature.properties["_surface"] = sqMeters; return sqMeters @@ -108,7 +191,7 @@ export default class SimpleMetaTagger { return sqMetersHa } }) - + return true; }) ); @@ -219,8 +302,8 @@ export default class SimpleMetaTagger { // isOpen is irrelevant return false } - - Object.defineProperty(feature.properties, "_isOpen",{ + + Object.defineProperty(feature.properties, "_isOpen", { enumerable: false, configurable: true, get: () => { @@ -247,7 +330,7 @@ export default class SimpleMetaTagger { if (oldNextChange > (new Date()).getTime() && tags["_isOpen:oldvalue"] === tags["opening_hours"] - && tags["_isOpen"] !== undefined) { + && tags["_isOpen"] !== undefined) { // Already calculated and should not yet be triggered return false; } @@ -354,7 +437,8 @@ export default class SimpleMetaTagger { SimpleMetaTagger.isOpen, SimpleMetaTagger.directionSimplified, SimpleMetaTagger.currentTime, - SimpleMetaTagger.objectMetaInfo + SimpleMetaTagger.objectMetaInfo, + SimpleMetaTagger.noBothButLeftRight ]; public static readonly lazyTags: string[] = [].concat(...SimpleMetaTagger.metatags.filter(tagger => tagger.isLazy) @@ -365,22 +449,24 @@ export default class SimpleMetaTagger { public readonly isLazy: boolean; public readonly includesDates: boolean public readonly applyMetaTagsOnFeature: (feature: any, freshness: Date, layer: LayerConfig) => boolean; - + /*** * A function that adds some extra data to a feature * @param docs: what does this extra data do? * @param f: apply the changes. Returns true if something changed */ - constructor(docs: { keys: string[], doc: string, includesDates?: boolean, isLazy?: boolean }, + constructor(docs: { keys: string[], doc: string, includesDates?: boolean, isLazy?: boolean, cleanupRetagger?: boolean }, f: ((feature: any, freshness: Date, layer: LayerConfig) => boolean)) { this.keys = docs.keys; this.doc = docs.doc; this.isLazy = docs.isLazy this.applyMetaTagsOnFeature = f; this.includesDates = docs.includesDates ?? false; - for (const key of docs.keys) { - if (!key.startsWith('_') && key.toLowerCase().indexOf("theme") < 0) { - throw `Incorrect metakey ${key}: it should start with underscore (_)` + if (!docs.cleanupRetagger) { + for (const key of docs.keys) { + if (!key.startsWith('_') && key.toLowerCase().indexOf("theme") < 0) { + throw `Incorrect metakey ${key}: it should start with underscore (_)` + } } } } diff --git a/Models/ThemeConfig/Json/LineRenderingConfigJson.ts b/Models/ThemeConfig/Json/LineRenderingConfigJson.ts index 80ee19009..708366767 100644 --- a/Models/ThemeConfig/Json/LineRenderingConfigJson.ts +++ b/Models/ThemeConfig/Json/LineRenderingConfigJson.ts @@ -29,7 +29,10 @@ export default interface LineRenderingConfigJson { /** * The number of pixels this line should be moved. - * Use a positive numbe to move to the right, a negative to move to the left (left/right as defined by the drawing direction of the line) + * Use a positive numbe to move to the right, a negative to move to the left (left/right as defined by the drawing direction of the line). + * + * IMPORTANT: MapComplete will already normalize 'key:both:property' and 'key:both' into the corresponding 'key:left' and 'key:right' tagging (same for 'sidewalk=left/right/both' which is rewritten to 'sidewalk:left' and 'sidewalk:right') + * This simplifies programming. Refer to the CalculatedTags.md-documentation for more details */ offset?: number | TagRenderingConfigJson } \ No newline at end of file diff --git a/Models/ThemeConfig/LineRenderingConfig.ts b/Models/ThemeConfig/LineRenderingConfig.ts index 04e5bf2ec..ae7e4cb60 100644 --- a/Models/ThemeConfig/LineRenderingConfig.ts +++ b/Models/ThemeConfig/LineRenderingConfig.ts @@ -12,12 +12,16 @@ export default class LineRenderingConfig extends WithContextLoader { public readonly width: TagRenderingConfig; public readonly dashArray: TagRenderingConfig; public readonly offset: TagRenderingConfig; - + public readonly leftRightSensitive: boolean + constructor(json: LineRenderingConfigJson, context: string) { super(json, context) this.color = this.tr("color", "#0000ff"); this.width = this.tr("width", "7"); this.dashArray = this.tr("dashArray", ""); + + this.leftRightSensitive = json.offset !== undefined && json.offset !== 0 && json.offset !== "0" + this.offset = this.tr("offset", "0"); } diff --git a/Models/ThemeConfig/TagRenderingConfig.ts b/Models/ThemeConfig/TagRenderingConfig.ts index e2b634839..9e44784e1 100644 --- a/Models/ThemeConfig/TagRenderingConfig.ts +++ b/Models/ThemeConfig/TagRenderingConfig.ts @@ -48,7 +48,7 @@ export default class TagRenderingConfig { } if(typeof json === "number"){ - this.render =Translations.WT( ""+json) + this.render = Translations.WT( ""+json) return; } diff --git a/assets/themes/sidewalks/sidewalks.json b/assets/themes/sidewalks/sidewalks.json index 8890b70f6..5248c97c0 100644 --- a/assets/themes/sidewalks/sidewalks.json +++ b/assets/themes/sidewalks/sidewalks.json @@ -45,8 +45,23 @@ "width": 8 }, { - "color": "#888", - "width": 8, + "color": { + "render": "#888" + }, + "width": { + "render": "8", + "mappings": [ + { + "if": { + "or": [ + "sidewalk:left=no", + "sidewalk:left=separate" + ] + }, + "then": 0 + } + ] + }, "offset": -8 }, { @@ -55,7 +70,7 @@ "offset": 8 } ], - "allowSplit": true + "allowSplit": true } ] } \ No newline at end of file From 72edc9bdcc971ddf11e33836022c85158d2ef593 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 22 Oct 2021 02:16:07 +0200 Subject: [PATCH 17/95] General cleanup, improve sidewalk rendering --- Logic/SimpleMetaTagger.ts | 6 ++-- Models/ThemeConfig/LineRenderingConfig.ts | 10 ++---- Models/ThemeConfig/TagRenderingConfig.ts | 2 +- UI/ShowDataLayer/ShowDataLayer.ts | 42 +++++++++++++---------- UI/i18n/Translations.ts | 3 ++ assets/themes/sidewalks/sidewalks.json | 25 +++++++++++--- 6 files changed, 53 insertions(+), 35 deletions(-) diff --git a/Logic/SimpleMetaTagger.ts b/Logic/SimpleMetaTagger.ts index 2934f93e2..f5c1b77e2 100644 --- a/Logic/SimpleMetaTagger.ts +++ b/Logic/SimpleMetaTagger.ts @@ -97,9 +97,9 @@ export default class SimpleMetaTagger { /** * Sets the key onto the properties (but doesn't overwrite if already existing) */ - function set(key, value) { - if (tgs[key] === undefined || tgs[key] === "") { - tgs[key] = value + function set(k, value) { + if (tgs[k] === undefined || tgs[k] === "") { + tgs[k] = value somethingChanged = true } } diff --git a/Models/ThemeConfig/LineRenderingConfig.ts b/Models/ThemeConfig/LineRenderingConfig.ts index ae7e4cb60..05da6604e 100644 --- a/Models/ThemeConfig/LineRenderingConfig.ts +++ b/Models/ThemeConfig/LineRenderingConfig.ts @@ -25,10 +25,7 @@ export default class LineRenderingConfig extends WithContextLoader { this.offset = this.tr("offset", "0"); } - - public GenerateLeafletStyle( - tags: UIEventSource - ): +public GenerateLeafletStyle( tags: {} ): { color: string, weight: number, @@ -48,8 +45,8 @@ export default class LineRenderingConfig extends WithContextLoader { if (tags === undefined) { return deflt } - const str = tr?.GetRenderValue(tags.data)?.txt ?? deflt; - return Utils.SubstituteKeys(str, tags.data)?.replace(/{.*}/g, ""); + const str = tr?.GetRenderValue(tags)?.txt ?? deflt; + return Utils.SubstituteKeys(str, tags)?.replace(/{.*}/g, ""); } const dashArray = render(this.dashArray)?.split(" ")?.map(Number); @@ -62,7 +59,6 @@ export default class LineRenderingConfig extends WithContextLoader { const weight = rendernum(this.width, 5); const offset = rendernum(this.offset, 0) - console.log("Calculated offset:", offset, "for", this.offset) return { color, weight, diff --git a/Models/ThemeConfig/TagRenderingConfig.ts b/Models/ThemeConfig/TagRenderingConfig.ts index 9e44784e1..51d3e9f53 100644 --- a/Models/ThemeConfig/TagRenderingConfig.ts +++ b/Models/ThemeConfig/TagRenderingConfig.ts @@ -141,7 +141,7 @@ export default class TagRenderingConfig { const mp = { if: TagUtils.Tag(mapping.if, `${mappingContext}.if`), ifnot: (mapping.ifnot !== undefined ? TagUtils.Tag(mapping.ifnot, `${mappingContext}.ifnot`) : undefined), - then: Translations.T(mapping.then, `{mappingContext}.then`), + then: Translations.T(mapping.then, `${mappingContext}.then`), hideInAnswer: hideInAnswer }; if (this.question) { diff --git a/UI/ShowDataLayer/ShowDataLayer.ts b/UI/ShowDataLayer/ShowDataLayer.ts index 8c0b885ed..980bcba96 100644 --- a/UI/ShowDataLayer/ShowDataLayer.ts +++ b/UI/ShowDataLayer/ShowDataLayer.ts @@ -16,7 +16,7 @@ export default class ShowDataLayer { private readonly _features: RenderingMultiPlexerFeatureSource private readonly _layerToShow: LayerConfig; private readonly _selectedElement: UIEventSource - private readonly allElements : ElementStorage + private readonly allElements: ElementStorage // Used to generate a fresh ID when needed private _cleanCount = 0; private geoLayer = undefined; @@ -31,7 +31,7 @@ export default class ShowDataLayer { */ private readonly leafletLayersPerId = new Map() - private readonly showDataLayerid : number; + private readonly showDataLayerid: number; private static dataLayerIds = 0 constructor(options: ShowDataLayerOptions & { layerToShow: LayerConfig }) { @@ -104,7 +104,7 @@ export default class ShowDataLayer { leafletLayer.openPopup() } }) - + this.update(options) } @@ -146,17 +146,21 @@ export default class ShowDataLayer { continue } try { - - if((feat.geometry.type === "LineString" || feat.geometry.type === "MultiLineString")) { + + if ((feat.geometry.type === "LineString" || feat.geometry.type === "MultiLineString")) { + const self = this; const coords = L.GeoJSON.coordsToLatLngs(feat.geometry.coordinates) const tagsSource = this.allElements?.addOrGetElement(feat) ?? new UIEventSource(feat.properties); - const lineStyle = this._layerToShow.lineRendering[feat.lineRenderingIndex].GenerateLeafletStyle(tagsSource) - const offsettedLine = L.polyline(coords, lineStyle); - - this.postProcessFeature(feat, offsettedLine) - - offsettedLine.addTo(this.geoLayer) - }else{ + let offsettedLine; + tagsSource.map(tags => this._layerToShow.lineRendering[feat.lineRenderingIndex].GenerateLeafletStyle(tags)).addCallbackAndRunD(lineStyle => { + if (offsettedLine !== undefined) { + self.geoLayer.removeLayer(offsettedLine) + } + offsettedLine = L.polyline(coords, lineStyle); + this.postProcessFeature(feat, offsettedLine) + offsettedLine.addTo(this.geoLayer) + }) + } else { this.geoLayer.addData(feat); } } catch (e) { @@ -183,20 +187,20 @@ export default class ShowDataLayer { const tagsSource = this.allElements?.addOrGetElement(feature) ?? new UIEventSource(feature.properties); // Every object is tied to exactly one layer const layer = this._layerToShow - + const pointRenderingIndex = feature.pointRenderingIndex const lineRenderingIndex = feature.lineRenderingIndex - - if(pointRenderingIndex !== undefined){ + + if (pointRenderingIndex !== undefined) { return { icon: layer.mapRendering[pointRenderingIndex].GenerateLeafletStyle(tagsSource, this._enablePopups) } } - if(lineRenderingIndex !== undefined){ + if (lineRenderingIndex !== undefined) { return layer.lineRendering[lineRenderingIndex].GenerateLeafletStyle(tagsSource) } - throw "Neither lineRendering nor mapRendering defined for "+feature + throw "Neither lineRendering nor mapRendering defined for " + feature } private pointToLayer(feature, latLng): L.Layer { @@ -211,7 +215,7 @@ export default class ShowDataLayer { let tagSource = this.allElements?.getEventSourceById(feature.properties.id) ?? new UIEventSource(feature.properties) const clickable = !(layer.title === undefined && (layer.tagRenderings ?? []).length === 0) - let style : any = layer.mapRendering[feature.pointRenderingIndex].GenerateLeafletStyle(tagSource, clickable); + let style: any = layer.mapRendering[feature.pointRenderingIndex].GenerateLeafletStyle(tagSource, clickable); const baseElement = style.html; if (!this._enablePopups) { baseElement.SetStyle("cursor: initial !important") @@ -276,7 +280,7 @@ export default class ShowDataLayer { feature: feature, leafletlayer: leafletLayer }) - + } diff --git a/UI/i18n/Translations.ts b/UI/i18n/Translations.ts index b1513d74c..ccf24f39b 100644 --- a/UI/i18n/Translations.ts +++ b/UI/i18n/Translations.ts @@ -26,6 +26,9 @@ export default class Translations { if (t === undefined || t === null) { return undefined; } + if(typeof t === "number"){ + t = ""+t + } if (typeof t === "string") { return new Translation({"*": t}, context); } diff --git a/assets/themes/sidewalks/sidewalks.json b/assets/themes/sidewalks/sidewalks.json index 5248c97c0..60a245ccc 100644 --- a/assets/themes/sidewalks/sidewalks.json +++ b/assets/themes/sidewalks/sidewalks.json @@ -41,7 +41,7 @@ "tagRenderings": [], "mapRendering": [ { - "color": "#ddd", + "color": "#ffffff55", "width": 8 }, { @@ -49,11 +49,12 @@ "render": "#888" }, "width": { - "render": "8", + "render": 6, "mappings": [ { "if": { "or": [ + "sidewalk:left=", "sidewalk:left=no", "sidewalk:left=separate" ] @@ -62,12 +63,26 @@ } ] }, - "offset": -8 + "offset": -6 }, { "color": "#888", - "width": 8, - "offset": 8 + "width": { + "render": 6, + "mappings": [ + { + "if": { + "or": [ + "sidewalk:right=", + "sidewalk:right=no", + "sidewalk:right=separate" + ] + }, + "then": 0 + } + ] + }, + "offset": 6 } ], "allowSplit": true From 45e8ee310bbbebbac2ff810ab4341315df18492e Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 22 Oct 2021 02:34:06 +0200 Subject: [PATCH 18/95] Fix build --- UI/Base/MinimapImplementation.ts | 1 + UI/ShowDataLayer/ShowDataLayer.ts | 17 +- .../charging_station/charging_station.json | 2888 +---------------- .../charging_station.protojson | 29 +- assets/svg/license_info.json | 8 +- langs/layers/de.json | 68 - langs/layers/en.json | 666 ---- langs/layers/nl.json | 665 ---- langs/themes/en.json | 14 + 9 files changed, 226 insertions(+), 4130 deletions(-) diff --git a/UI/Base/MinimapImplementation.ts b/UI/Base/MinimapImplementation.ts index 9ec90bd31..249d4df6d 100644 --- a/UI/Base/MinimapImplementation.ts +++ b/UI/Base/MinimapImplementation.ts @@ -8,6 +8,7 @@ import * as L from "leaflet"; import {Map} from "leaflet"; import Minimap, {MinimapObj, MinimapOptions} from "./Minimap"; import {BBox} from "../../Logic/BBox"; +import 'leaflet-polylineoffset' export default class MinimapImplementation extends BaseUIElement implements MinimapObj { private static _nextId = 0; diff --git a/UI/ShowDataLayer/ShowDataLayer.ts b/UI/ShowDataLayer/ShowDataLayer.ts index 980bcba96..00f04c199 100644 --- a/UI/ShowDataLayer/ShowDataLayer.ts +++ b/UI/ShowDataLayer/ShowDataLayer.ts @@ -1,14 +1,23 @@ -/** - * The data layer shows all the given geojson elements with the appropriate icon etc - */ + import {UIEventSource} from "../../Logic/UIEventSource"; import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; import FeatureInfoBox from "../Popup/FeatureInfoBox"; import {ShowDataLayerOptions} from "./ShowDataLayerOptions"; import {ElementStorage} from "../../Logic/ElementStorage"; import RenderingMultiPlexerFeatureSource from "../../Logic/FeatureSource/Sources/WayHandlingApplyingFeatureSource"; -import 'leaflet-polylineoffset'; +/* +// import 'leaflet-polylineoffset'; +We don't actually import it here. It is imported in the 'MinimapImplementation'-class, which'll result in a patched 'L' object. + Even though actually importing this here would seem cleaner, we don't do this as this breaks some scripts: + - Scripts are ran in ts-node + - ts-node doesn't define the 'window'-object + - Importing this will execute some code which needs the window object + */ + +/** + * The data layer shows all the given geojson elements with the appropriate icon etc + */ export default class ShowDataLayer { private readonly _leafletMap: UIEventSource; diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index fc738cdaa..7b44e0a11 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -6,7 +6,8 @@ "ja": "充電ステーション", "nb_NO": "Ladestasjoner", "ru": "Зарядные станции", - "zh_Hant": "充電站" + "zh_Hant": "充電站", + "de": "Ladestationen" }, "minzoom": 10, "source": { @@ -35,14 +36,16 @@ "ja": "充電ステーション", "nb_NO": "En ladestasjon", "ru": "Зарядная станция", - "zh_Hant": "充電站" + "zh_Hant": "充電站", + "de": "Eine Ladestation" }, "tagRenderings": [ "images", { "id": "Type", "question": { - "en": "Which vehicles are allowed to charge here?" + "en": "Which vehicles are allowed to charge here?", + "de": "Welche Fahrzeuge dürfen hier geladen werden?" }, "multiAnswer": true, "mappings": [ @@ -50,35 +53,40 @@ "if": "bicycle=yes", "ifnot": "bicycle=no", "then": { - "en": "bicycles can be charged here" + "en": "bicycles can be charged here", + "de": "Fahrräder können hier geladen werden" } }, { "if": "motorcar=yes", "ifnot": "motorcar=no", "then": { - "en": "Cars can be charged here" + "en": "Cars can be charged here", + "de": "Autos können hier geladen werden" } }, { "if": "scooter=yes", "ifnot": "scooter=no", "then": { - "en": "Scooters can be charged here" + "en": "Scooters can be charged here", + "de": " Roller können hier geladen werden" } }, { "if": "hgv=yes", "ifnot": "hgv=no", "then": { - "en": "Heavy good vehicles (such as trucks) can be charged here" + "en": "Heavy good vehicles (such as trucks) can be charged here", + "de": "Lastkraftwagen (LKW) können hier geladen werden" } }, { "if": "bus=yes", "ifnot": "bus=no", "then": { - "en": "Buses can be charged here" + "en": "Buses can be charged here", + "de": "Busse können hier geladen werden" } } ] @@ -86,10 +94,12 @@ { "id": "access", "question": { - "en": "Who is allowed to use this charging station?" + "en": "Who is allowed to use this charging station?", + "de": "Wer darf diese Ladestation benutzen?" }, "render": { - "en": "Access is {access}" + "en": "Access is {access}", + "de": "Zugang ist {access}" }, "freeform": { "key": "access", @@ -126,11 +136,13 @@ "id": "capacity", "render": { "en": "{capacity} vehicles can be charged here at the same time", - "nl": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden" + "nl": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden", + "de": "{capacity} Fahrzeuge können hier gleichzeitig geladen werden" }, "question": { "en": "How much vehicles can be charged here at the same time?", - "nl": "Hoeveel voertuigen kunnen hier opgeladen worden?" + "nl": "Hoeveel voertuigen kunnen hier opgeladen worden?", + "de": "Wie viele Fahrzeuge können hier gleichzeitig geladen werden?" }, "freeform": { "key": "capacity", @@ -138,2660 +150,7 @@ } }, { - "id": "Available_charging_stations (generated)", - "question": { - "en": "Which charging stations are available here?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "socket:schuko=1", - "ifnot": "socket:schuko=", - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "hideInAnswer": { - "or": [ - "_country!=be", - "_country!=fr", - "_country!=ma", - "_country!=tn", - "_country!=pl", - "_country!=cs", - "_country!=sk", - "_country!=mo" - ] - } - }, - { - "if": { - "and": [ - "socket:schuko~*", - "socket:schuko!=1" - ] - }, - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:typee=1", - "ifnot": "socket:typee=", - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" - } - }, - { - "if": { - "and": [ - "socket:typee~*", - "socket:typee!=1" - ] - }, - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:chademo=1", - "ifnot": "socket:chademo=", - "then": { - "en": "
Chademo
", - "nl": "
Chademo
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:chademo~*", - "socket:chademo!=1" - ] - }, - "then": { - "en": "
Chademo
", - "nl": "
Chademo
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1_cable=1", - "ifnot": "socket:type1_cable=", - "then": { - "en": "
Type 1 with cable (J1772)
", - "nl": "
Type 1 met kabel (J1772)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=1" - ] - }, - "then": { - "en": "
Type 1 with cable (J1772)
", - "nl": "
Type 1 met kabel (J1772)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1=1", - "ifnot": "socket:type1=", - "then": { - "en": "
Type 1 without cable (J1772)
", - "nl": "
Type 1 zonder kabel (J1772)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type1~*", - "socket:type1!=1" - ] - }, - "then": { - "en": "
Type 1 without cable (J1772)
", - "nl": "
Type 1 zonder kabel (J1772)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1_combo=1", - "ifnot": "socket:type1_combo=", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=1" - ] - }, - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_supercharger=1", - "ifnot": "socket:tesla_supercharger=", - "then": { - "en": "
Tesla Supercharger
", - "nl": "
Tesla Supercharger
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=1" - ] - }, - "then": { - "en": "
Tesla Supercharger
", - "nl": "
Tesla Supercharger
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type2=1", - "ifnot": "socket:type2=", - "then": { - "en": "
Type 2 (mennekes)
", - "nl": "
Type 2 (mennekes)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type2~*", - "socket:type2!=1" - ] - }, - "then": { - "en": "
Type 2 (mennekes)
", - "nl": "
Type 2 (mennekes)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type2_combo=1", - "ifnot": "socket:type2_combo=", - "then": { - "en": "
Type 2 CCS (mennekes)
", - "nl": "
Type 2 CCS (mennekes)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=1" - ] - }, - "then": { - "en": "
Type 2 CCS (mennekes)
", - "nl": "
Type 2 CCS (mennekes)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type2_cable=1", - "ifnot": "socket:type2_cable=", - "then": { - "en": "
Type 2 with cable (mennekes)
", - "nl": "
Type 2 met kabel (J1772)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=1" - ] - }, - "then": { - "en": "
Type 2 with cable (mennekes)
", - "nl": "
Type 2 met kabel (J1772)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_supercharger_ccs=1", - "ifnot": "socket:tesla_supercharger_ccs=", - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=1" - ] - }, - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_destination=1", - "ifnot": "socket:tesla_destination=", - "then": { - "en": "
Tesla Supercharger (destination)
", - "nl": "
Tesla Supercharger (destination)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - }, - { - "or": [ - "_country!=us" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=1" - ] - }, - "then": { - "en": "
Tesla Supercharger (destination)
", - "nl": "
Tesla Supercharger (destination)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_destination=1", - "ifnot": "socket:tesla_destination=", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - }, - { - "or": [ - "_country=us" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=1" - ] - }, - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:USB-A=1", - "ifnot": "socket:USB-A=", - "then": { - "en": "
USB to charge phones and small electronics
", - "nl": "
USB om GSMs en kleine electronica op te laden
" - } - }, - { - "if": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=1" - ] - }, - "then": { - "en": "
USB to charge phones and small electronics
", - "nl": "
USB om GSMs en kleine electronica op te laden
" - }, - "hideInAnswer": true - }, - { - "if": "socket:bosch_3pin=1", - "ifnot": "socket:bosch_3pin=", - "then": { - "en": "
Bosch Active Connect with 3 pins and cable
", - "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "bicycle=no" - ] - }, - { - "and": [ - { - "or": [ - "car=yes", - "motorcar=yes", - "hgv=yes", - "bus=yes" - ] - }, - "bicycle!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:bosch_3pin~*", - "socket:bosch_3pin!=1" - ] - }, - "then": { - "en": "
Bosch Active Connect with 3 pins and cable
", - "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "hideInAnswer": true - }, - { - "if": "socket:bosch_5pin=1", - "ifnot": "socket:bosch_5pin=", - "then": { - "en": "
Bosch Active Connect with 5 pins and cable
", - "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "bicycle=no" - ] - }, - { - "and": [ - { - "or": [ - "car=yes", - "motorcar=yes", - "hgv=yes", - "bus=yes" - ] - }, - "bicycle!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:bosch_5pin~*", - "socket:bosch_5pin!=1" - ] - }, - "then": { - "en": "
Bosch Active Connect with 5 pins and cable
", - "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
" - }, - "hideInAnswer": true - } - ] - }, - { - "id": "plugs-0", - "question": { - "en": "How much plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
are available here?", - "nl": "Hoeveel stekkers van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:schuko} plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
available here", - "nl": "Hier zijn {socket:schuko} stekkers van het type
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "freeform": { - "key": "socket:schuko", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "voltage-0", - "question": { - "en": "What voltage do the plugs with
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", - "nl": "Welke spanning levert de stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "render": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs {socket:schuko:voltage} volt", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft een spanning van {socket:schuko:voltage} volt" - }, - "freeform": { - "key": "socket:schuko:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:voltage=230 V", - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs 230 volt", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft een spanning van 230 volt" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "current-0", - "question": { - "en": "What current do the plugs with
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", - "nl": "Welke stroom levert de stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
?" - }, - "render": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most {socket:schuko:current}A", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een stroom van maximaal {socket:schuko:current}A" - }, - "freeform": { - "key": "socket:schuko:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:current=16 A", - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 16 A", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een stroom van maximaal 16 A" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "power-output-0", - "question": { - "en": "What power output does a single plug of type
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
?" - }, - "render": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most {socket:schuko:output}", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een vermogen van maximaal {socket:schuko:output}" - }, - "freeform": { - "key": "socket:schuko:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:output=3.6 kw", - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 3.6 kw", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een vermogen van maximaal 3.6 kw" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "plugs-1", - "question": { - "en": "How much plugs of type
European wall plug with ground pin (CEE7/4 type E)
are available here?", - "nl": "Hoeveel stekkers van type
Europese stekker met aardingspin (CEE7/4 type E)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:typee} plugs of type
European wall plug with ground pin (CEE7/4 type E)
available here", - "nl": "Hier zijn {socket:typee} stekkers van het type
Europese stekker met aardingspin (CEE7/4 type E)
" - }, - "freeform": { - "key": "socket:typee", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "voltage-1", - "question": { - "en": "What voltage do the plugs with
European wall plug with ground pin (CEE7/4 type E)
offer?", - "nl": "Welke spanning levert de stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
" - }, - "render": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs {socket:typee:voltage} volt", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
heeft een spanning van {socket:typee:voltage} volt" - }, - "freeform": { - "key": "socket:typee:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:voltage=230 V", - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs 230 volt", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
heeft een spanning van 230 volt" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "current-1", - "question": { - "en": "What current do the plugs with
European wall plug with ground pin (CEE7/4 type E)
offer?", - "nl": "Welke stroom levert de stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
?" - }, - "render": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most {socket:typee:current}A", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een stroom van maximaal {socket:typee:current}A" - }, - "freeform": { - "key": "socket:typee:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:current=16 A", - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 16 A", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een stroom van maximaal 16 A" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "power-output-1", - "question": { - "en": "What power output does a single plug of type
European wall plug with ground pin (CEE7/4 type E)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
?" - }, - "render": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most {socket:typee:output}", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal {socket:typee:output}" - }, - "freeform": { - "key": "socket:typee:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:output=3 kw", - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 3 kw", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal 3 kw" - } - }, - { - "if": "socket:socket:typee:output=22 kw", - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 22 kw", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "plugs-2", - "question": { - "en": "How much plugs of type
Chademo
are available here?", - "nl": "Hoeveel stekkers van type
Chademo
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:chademo} plugs of type
Chademo
available here", - "nl": "Hier zijn {socket:chademo} stekkers van het type
Chademo
" - }, - "freeform": { - "key": "socket:chademo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "voltage-2", - "question": { - "en": "What voltage do the plugs with
Chademo
offer?", - "nl": "Welke spanning levert de stekker van type
Chademo
" - }, - "render": { - "en": "
Chademo
outputs {socket:chademo:voltage} volt", - "nl": "
Chademo
heeft een spanning van {socket:chademo:voltage} volt" - }, - "freeform": { - "key": "socket:chademo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:voltage=500 V", - "then": { - "en": "
Chademo
outputs 500 volt", - "nl": "
Chademo
heeft een spanning van 500 volt" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "current-2", - "question": { - "en": "What current do the plugs with
Chademo
offer?", - "nl": "Welke stroom levert de stekker van type
Chademo
?" - }, - "render": { - "en": "
Chademo
outputs at most {socket:chademo:current}A", - "nl": "
Chademo
levert een stroom van maximaal {socket:chademo:current}A" - }, - "freeform": { - "key": "socket:chademo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:current=120 A", - "then": { - "en": "
Chademo
outputs at most 120 A", - "nl": "
Chademo
levert een stroom van maximaal 120 A" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "power-output-2", - "question": { - "en": "What power output does a single plug of type
Chademo
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Chademo
?" - }, - "render": { - "en": "
Chademo
outputs at most {socket:chademo:output}", - "nl": "
Chademo
levert een vermogen van maximaal {socket:chademo:output}" - }, - "freeform": { - "key": "socket:chademo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:output=50 kw", - "then": { - "en": "
Chademo
outputs at most 50 kw", - "nl": "
Chademo
levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "plugs-3", - "question": { - "en": "How much plugs of type
Type 1 with cable (J1772)
are available here?", - "nl": "Hoeveel stekkers van type
Type 1 met kabel (J1772)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:type1_cable} plugs of type
Type 1 with cable (J1772)
available here", - "nl": "Hier zijn {socket:type1_cable} stekkers van het type
Type 1 met kabel (J1772)
" - }, - "freeform": { - "key": "socket:type1_cable", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "voltage-3", - "question": { - "en": "What voltage do the plugs with
Type 1 with cable (J1772)
offer?", - "nl": "Welke spanning levert de stekker van type
Type 1 met kabel (J1772)
" - }, - "render": { - "en": "
Type 1 with cable (J1772)
outputs {socket:type1_cable:voltage} volt", - "nl": "
Type 1 met kabel (J1772)
heeft een spanning van {socket:type1_cable:voltage} volt" - }, - "freeform": { - "key": "socket:type1_cable:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:voltage=200 V", - "then": { - "en": "
Type 1 with cable (J1772)
outputs 200 volt", - "nl": "
Type 1 met kabel (J1772)
heeft een spanning van 200 volt" - } - }, - { - "if": "socket:socket:type1_cable:voltage=240 V", - "then": { - "en": "
Type 1 with cable (J1772)
outputs 240 volt", - "nl": "
Type 1 met kabel (J1772)
heeft een spanning van 240 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "current-3", - "question": { - "en": "What current do the plugs with
Type 1 with cable (J1772)
offer?", - "nl": "Welke stroom levert de stekker van type
Type 1 met kabel (J1772)
?" - }, - "render": { - "en": "
Type 1 with cable (J1772)
outputs at most {socket:type1_cable:current}A", - "nl": "
Type 1 met kabel (J1772)
levert een stroom van maximaal {socket:type1_cable:current}A" - }, - "freeform": { - "key": "socket:type1_cable:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:current=32 A", - "then": { - "en": "
Type 1 with cable (J1772)
outputs at most 32 A", - "nl": "
Type 1 met kabel (J1772)
levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "power-output-3", - "question": { - "en": "What power output does a single plug of type
Type 1 with cable (J1772)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Type 1 met kabel (J1772)
?" - }, - "render": { - "en": "
Type 1 with cable (J1772)
outputs at most {socket:type1_cable:output}", - "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal {socket:type1_cable:output}" - }, - "freeform": { - "key": "socket:type1_cable:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:output=3.7 kw", - "then": { - "en": "
Type 1 with cable (J1772)
outputs at most 3.7 kw", - "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal 3.7 kw" - } - }, - { - "if": "socket:socket:type1_cable:output=7 kw", - "then": { - "en": "
Type 1 with cable (J1772)
outputs at most 7 kw", - "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal 7 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "plugs-4", - "question": { - "en": "How much plugs of type
Type 1 without cable (J1772)
are available here?", - "nl": "Hoeveel stekkers van type
Type 1 zonder kabel (J1772)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:type1} plugs of type
Type 1 without cable (J1772)
available here", - "nl": "Hier zijn {socket:type1} stekkers van het type
Type 1 zonder kabel (J1772)
" - }, - "freeform": { - "key": "socket:type1", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "voltage-4", - "question": { - "en": "What voltage do the plugs with
Type 1 without cable (J1772)
offer?", - "nl": "Welke spanning levert de stekker van type
Type 1 zonder kabel (J1772)
" - }, - "render": { - "en": "
Type 1 without cable (J1772)
outputs {socket:type1:voltage} volt", - "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van {socket:type1:voltage} volt" - }, - "freeform": { - "key": "socket:type1:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:voltage=200 V", - "then": { - "en": "
Type 1 without cable (J1772)
outputs 200 volt", - "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van 200 volt" - } - }, - { - "if": "socket:socket:type1:voltage=240 V", - "then": { - "en": "
Type 1 without cable (J1772)
outputs 240 volt", - "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van 240 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "current-4", - "question": { - "en": "What current do the plugs with
Type 1 without cable (J1772)
offer?", - "nl": "Welke stroom levert de stekker van type
Type 1 zonder kabel (J1772)
?" - }, - "render": { - "en": "
Type 1 without cable (J1772)
outputs at most {socket:type1:current}A", - "nl": "
Type 1 zonder kabel (J1772)
levert een stroom van maximaal {socket:type1:current}A" - }, - "freeform": { - "key": "socket:type1:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:current=32 A", - "then": { - "en": "
Type 1 without cable (J1772)
outputs at most 32 A", - "nl": "
Type 1 zonder kabel (J1772)
levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "power-output-4", - "question": { - "en": "What power output does a single plug of type
Type 1 without cable (J1772)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Type 1 zonder kabel (J1772)
?" - }, - "render": { - "en": "
Type 1 without cable (J1772)
outputs at most {socket:type1:output}", - "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal {socket:type1:output}" - }, - "freeform": { - "key": "socket:type1:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:output=3.7 kw", - "then": { - "en": "
Type 1 without cable (J1772)
outputs at most 3.7 kw", - "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 3.7 kw" - } - }, - { - "if": "socket:socket:type1:output=6.6 kw", - "then": { - "en": "
Type 1 without cable (J1772)
outputs at most 6.6 kw", - "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 6.6 kw" - } - }, - { - "if": "socket:socket:type1:output=7 kw", - "then": { - "en": "
Type 1 without cable (J1772)
outputs at most 7 kw", - "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 7 kw" - } - }, - { - "if": "socket:socket:type1:output=7.2 kw", - "then": { - "en": "
Type 1 without cable (J1772)
outputs at most 7.2 kw", - "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 7.2 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "plugs-5", - "question": { - "en": "How much plugs of type
Type 1 CCS (aka Type 1 Combo)
are available here?", - "nl": "Hoeveel stekkers van type
Type 1 CCS (ook gekend als Type 1 Combo)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:type1_combo} plugs of type
Type 1 CCS (aka Type 1 Combo)
available here", - "nl": "Hier zijn {socket:type1_combo} stekkers van het type
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "freeform": { - "key": "socket:type1_combo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "voltage-5", - "question": { - "en": "What voltage do the plugs with
Type 1 CCS (aka Type 1 Combo)
offer?", - "nl": "Welke spanning levert de stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "render": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs {socket:type1_combo:voltage} volt", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van {socket:type1_combo:voltage} volt" - }, - "freeform": { - "key": "socket:type1_combo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:voltage=400 V", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs 400 volt", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van 400 volt" - } - }, - { - "if": "socket:socket:type1_combo:voltage=1000 V", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs 1000 volt", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van 1000 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "current-5", - "question": { - "en": "What current do the plugs with
Type 1 CCS (aka Type 1 Combo)
offer?", - "nl": "Welke stroom levert de stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
?" - }, - "render": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most {socket:type1_combo:current}A", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal {socket:type1_combo:current}A" - }, - "freeform": { - "key": "socket:type1_combo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:current=50 A", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 A", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal 50 A" - } - }, - { - "if": "socket:socket:type1_combo:current=125 A", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 125 A", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal 125 A" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "power-output-5", - "question": { - "en": "What power output does a single plug of type
Type 1 CCS (aka Type 1 Combo)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
?" - }, - "render": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most {socket:type1_combo:output}", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal {socket:type1_combo:output}" - }, - "freeform": { - "key": "socket:type1_combo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:output=50 kw", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 kw", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 50 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=62.5 kw", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 62.5 kw", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 62.5 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=150 kw", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 150 kw", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=350 kw", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 350 kw", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 350 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "plugs-6", - "question": { - "en": "How much plugs of type
Tesla Supercharger
are available here?", - "nl": "Hoeveel stekkers van type
Tesla Supercharger
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:tesla_supercharger} plugs of type
Tesla Supercharger
available here", - "nl": "Hier zijn {socket:tesla_supercharger} stekkers van het type
Tesla Supercharger
" - }, - "freeform": { - "key": "socket:tesla_supercharger", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "voltage-6", - "question": { - "en": "What voltage do the plugs with
Tesla Supercharger
offer?", - "nl": "Welke spanning levert de stekker van type
Tesla Supercharger
" - }, - "render": { - "en": "
Tesla Supercharger
outputs {socket:tesla_supercharger:voltage} volt", - "nl": "
Tesla Supercharger
heeft een spanning van {socket:tesla_supercharger:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_supercharger:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:voltage=480 V", - "then": { - "en": "
Tesla Supercharger
outputs 480 volt", - "nl": "
Tesla Supercharger
heeft een spanning van 480 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "current-6", - "question": { - "en": "What current do the plugs with
Tesla Supercharger
offer?", - "nl": "Welke stroom levert de stekker van type
Tesla Supercharger
?" - }, - "render": { - "en": "
Tesla Supercharger
outputs at most {socket:tesla_supercharger:current}A", - "nl": "
Tesla Supercharger
levert een stroom van maximaal {socket:tesla_supercharger:current}A" - }, - "freeform": { - "key": "socket:tesla_supercharger:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:current=125 A", - "then": { - "en": "
Tesla Supercharger
outputs at most 125 A", - "nl": "
Tesla Supercharger
levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_supercharger:current=350 A", - "then": { - "en": "
Tesla Supercharger
outputs at most 350 A", - "nl": "
Tesla Supercharger
levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "power-output-6", - "question": { - "en": "What power output does a single plug of type
Tesla Supercharger
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger
?" - }, - "render": { - "en": "
Tesla Supercharger
outputs at most {socket:tesla_supercharger:output}", - "nl": "
Tesla Supercharger
levert een vermogen van maximaal {socket:tesla_supercharger:output}" - }, - "freeform": { - "key": "socket:tesla_supercharger:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:output=120 kw", - "then": { - "en": "
Tesla Supercharger
outputs at most 120 kw", - "nl": "
Tesla Supercharger
levert een vermogen van maximaal 120 kw" - } - }, - { - "if": "socket:socket:tesla_supercharger:output=150 kw", - "then": { - "en": "
Tesla Supercharger
outputs at most 150 kw", - "nl": "
Tesla Supercharger
levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:tesla_supercharger:output=250 kw", - "then": { - "en": "
Tesla Supercharger
outputs at most 250 kw", - "nl": "
Tesla Supercharger
levert een vermogen van maximaal 250 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "plugs-7", - "question": { - "en": "How much plugs of type
Type 2 (mennekes)
are available here?", - "nl": "Hoeveel stekkers van type
Type 2 (mennekes)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:type2} plugs of type
Type 2 (mennekes)
available here", - "nl": "Hier zijn {socket:type2} stekkers van het type
Type 2 (mennekes)
" - }, - "freeform": { - "key": "socket:type2", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "voltage-7", - "question": { - "en": "What voltage do the plugs with
Type 2 (mennekes)
offer?", - "nl": "Welke spanning levert de stekker van type
Type 2 (mennekes)
" - }, - "render": { - "en": "
Type 2 (mennekes)
outputs {socket:type2:voltage} volt", - "nl": "
Type 2 (mennekes)
heeft een spanning van {socket:type2:voltage} volt" - }, - "freeform": { - "key": "socket:type2:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:voltage=230 V", - "then": { - "en": "
Type 2 (mennekes)
outputs 230 volt", - "nl": "
Type 2 (mennekes)
heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:type2:voltage=400 V", - "then": { - "en": "
Type 2 (mennekes)
outputs 400 volt", - "nl": "
Type 2 (mennekes)
heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "current-7", - "question": { - "en": "What current do the plugs with
Type 2 (mennekes)
offer?", - "nl": "Welke stroom levert de stekker van type
Type 2 (mennekes)
?" - }, - "render": { - "en": "
Type 2 (mennekes)
outputs at most {socket:type2:current}A", - "nl": "
Type 2 (mennekes)
levert een stroom van maximaal {socket:type2:current}A" - }, - "freeform": { - "key": "socket:type2:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:current=16 A", - "then": { - "en": "
Type 2 (mennekes)
outputs at most 16 A", - "nl": "
Type 2 (mennekes)
levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:type2:current=32 A", - "then": { - "en": "
Type 2 (mennekes)
outputs at most 32 A", - "nl": "
Type 2 (mennekes)
levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "power-output-7", - "question": { - "en": "What power output does a single plug of type
Type 2 (mennekes)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Type 2 (mennekes)
?" - }, - "render": { - "en": "
Type 2 (mennekes)
outputs at most {socket:type2:output}", - "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal {socket:type2:output}" - }, - "freeform": { - "key": "socket:type2:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:output=11 kw", - "then": { - "en": "
Type 2 (mennekes)
outputs at most 11 kw", - "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:type2:output=22 kw", - "then": { - "en": "
Type 2 (mennekes)
outputs at most 22 kw", - "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "plugs-8", - "question": { - "en": "How much plugs of type
Type 2 CCS (mennekes)
are available here?", - "nl": "Hoeveel stekkers van type
Type 2 CCS (mennekes)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:type2_combo} plugs of type
Type 2 CCS (mennekes)
available here", - "nl": "Hier zijn {socket:type2_combo} stekkers van het type
Type 2 CCS (mennekes)
" - }, - "freeform": { - "key": "socket:type2_combo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "voltage-8", - "question": { - "en": "What voltage do the plugs with
Type 2 CCS (mennekes)
offer?", - "nl": "Welke spanning levert de stekker van type
Type 2 CCS (mennekes)
" - }, - "render": { - "en": "
Type 2 CCS (mennekes)
outputs {socket:type2_combo:voltage} volt", - "nl": "
Type 2 CCS (mennekes)
heeft een spanning van {socket:type2_combo:voltage} volt" - }, - "freeform": { - "key": "socket:type2_combo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:voltage=500 V", - "then": { - "en": "
Type 2 CCS (mennekes)
outputs 500 volt", - "nl": "
Type 2 CCS (mennekes)
heeft een spanning van 500 volt" - } - }, - { - "if": "socket:socket:type2_combo:voltage=920 V", - "then": { - "en": "
Type 2 CCS (mennekes)
outputs 920 volt", - "nl": "
Type 2 CCS (mennekes)
heeft een spanning van 920 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "current-8", - "question": { - "en": "What current do the plugs with
Type 2 CCS (mennekes)
offer?", - "nl": "Welke stroom levert de stekker van type
Type 2 CCS (mennekes)
?" - }, - "render": { - "en": "
Type 2 CCS (mennekes)
outputs at most {socket:type2_combo:current}A", - "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal {socket:type2_combo:current}A" - }, - "freeform": { - "key": "socket:type2_combo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:current=125 A", - "then": { - "en": "
Type 2 CCS (mennekes)
outputs at most 125 A", - "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:type2_combo:current=350 A", - "then": { - "en": "
Type 2 CCS (mennekes)
outputs at most 350 A", - "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "power-output-8", - "question": { - "en": "What power output does a single plug of type
Type 2 CCS (mennekes)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Type 2 CCS (mennekes)
?" - }, - "render": { - "en": "
Type 2 CCS (mennekes)
outputs at most {socket:type2_combo:output}", - "nl": "
Type 2 CCS (mennekes)
levert een vermogen van maximaal {socket:type2_combo:output}" - }, - "freeform": { - "key": "socket:type2_combo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:output=50 kw", - "then": { - "en": "
Type 2 CCS (mennekes)
outputs at most 50 kw", - "nl": "
Type 2 CCS (mennekes)
levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "plugs-9", - "question": { - "en": "How much plugs of type
Type 2 with cable (mennekes)
are available here?", - "nl": "Hoeveel stekkers van type
Type 2 met kabel (J1772)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:type2_cable} plugs of type
Type 2 with cable (mennekes)
available here", - "nl": "Hier zijn {socket:type2_cable} stekkers van het type
Type 2 met kabel (J1772)
" - }, - "freeform": { - "key": "socket:type2_cable", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "voltage-9", - "question": { - "en": "What voltage do the plugs with
Type 2 with cable (mennekes)
offer?", - "nl": "Welke spanning levert de stekker van type
Type 2 met kabel (J1772)
" - }, - "render": { - "en": "
Type 2 with cable (mennekes)
outputs {socket:type2_cable:voltage} volt", - "nl": "
Type 2 met kabel (J1772)
heeft een spanning van {socket:type2_cable:voltage} volt" - }, - "freeform": { - "key": "socket:type2_cable:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:voltage=230 V", - "then": { - "en": "
Type 2 with cable (mennekes)
outputs 230 volt", - "nl": "
Type 2 met kabel (J1772)
heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:type2_cable:voltage=400 V", - "then": { - "en": "
Type 2 with cable (mennekes)
outputs 400 volt", - "nl": "
Type 2 met kabel (J1772)
heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "current-9", - "question": { - "en": "What current do the plugs with
Type 2 with cable (mennekes)
offer?", - "nl": "Welke stroom levert de stekker van type
Type 2 met kabel (J1772)
?" - }, - "render": { - "en": "
Type 2 with cable (mennekes)
outputs at most {socket:type2_cable:current}A", - "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal {socket:type2_cable:current}A" - }, - "freeform": { - "key": "socket:type2_cable:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:current=16 A", - "then": { - "en": "
Type 2 with cable (mennekes)
outputs at most 16 A", - "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:type2_cable:current=32 A", - "then": { - "en": "
Type 2 with cable (mennekes)
outputs at most 32 A", - "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "power-output-9", - "question": { - "en": "What power output does a single plug of type
Type 2 with cable (mennekes)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Type 2 met kabel (J1772)
?" - }, - "render": { - "en": "
Type 2 with cable (mennekes)
outputs at most {socket:type2_cable:output}", - "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal {socket:type2_cable:output}" - }, - "freeform": { - "key": "socket:type2_cable:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:output=11 kw", - "then": { - "en": "
Type 2 with cable (mennekes)
outputs at most 11 kw", - "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:type2_cable:output=22 kw", - "then": { - "en": "
Type 2 with cable (mennekes)
outputs at most 22 kw", - "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "plugs-10", - "question": { - "en": "How much plugs of type
Tesla Supercharger CCS (a branded type2_css)
are available here?", - "nl": "Hoeveel stekkers van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:tesla_supercharger_ccs} plugs of type
Tesla Supercharger CCS (a branded type2_css)
available here", - "nl": "Hier zijn {socket:tesla_supercharger_ccs} stekkers van het type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "voltage-10", - "question": { - "en": "What voltage do the plugs with
Tesla Supercharger CCS (a branded type2_css)
offer?", - "nl": "Welke spanning levert de stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "render": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs {socket:tesla_supercharger_ccs:voltage} volt", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:voltage=500 V", - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs 500 volt", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van 500 volt" - } - }, - { - "if": "socket:socket:tesla_supercharger_ccs:voltage=920 V", - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs 920 volt", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van 920 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "current-10", - "question": { - "en": "What current do the plugs with
Tesla Supercharger CCS (a branded type2_css)
offer?", - "nl": "Welke stroom levert de stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
?" - }, - "render": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most {socket:tesla_supercharger_ccs:current}A", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:current=125 A", - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 125 A", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_supercharger_ccs:current=350 A", - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 350 A", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "power-output-10", - "question": { - "en": "What power output does a single plug of type
Tesla Supercharger CCS (a branded type2_css)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
?" - }, - "render": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most {socket:tesla_supercharger_ccs:output}", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:output=50 kw", - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 50 kw", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "plugs-11", - "question": { - "en": "How much plugs of type
Tesla Supercharger (destination)
are available here?", - "nl": "Hoeveel stekkers van type
Tesla Supercharger (destination)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:tesla_destination} plugs of type
Tesla Supercharger (destination)
available here", - "nl": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla Supercharger (destination)
" - }, - "freeform": { - "key": "socket:tesla_destination", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "voltage-11", - "question": { - "en": "What voltage do the plugs with
Tesla Supercharger (destination)
offer?", - "nl": "Welke spanning levert de stekker van type
Tesla Supercharger (destination)
" - }, - "render": { - "en": "
Tesla Supercharger (destination)
outputs {socket:tesla_destination:voltage} volt", - "nl": "
Tesla Supercharger (destination)
heeft een spanning van {socket:tesla_destination:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_destination:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:voltage=480 V", - "then": { - "en": "
Tesla Supercharger (destination)
outputs 480 volt", - "nl": "
Tesla Supercharger (destination)
heeft een spanning van 480 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "current-11", - "question": { - "en": "What current do the plugs with
Tesla Supercharger (destination)
offer?", - "nl": "Welke stroom levert de stekker van type
Tesla Supercharger (destination)
?" - }, - "render": { - "en": "
Tesla Supercharger (destination)
outputs at most {socket:tesla_destination:current}A", - "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal {socket:tesla_destination:current}A" - }, - "freeform": { - "key": "socket:tesla_destination:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:current=125 A", - "then": { - "en": "
Tesla Supercharger (destination)
outputs at most 125 A", - "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_destination:current=350 A", - "then": { - "en": "
Tesla Supercharger (destination)
outputs at most 350 A", - "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "power-output-11", - "question": { - "en": "What power output does a single plug of type
Tesla Supercharger (destination)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger (destination)
?" - }, - "render": { - "en": "
Tesla Supercharger (destination)
outputs at most {socket:tesla_destination:output}", - "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal {socket:tesla_destination:output}" - }, - "freeform": { - "key": "socket:tesla_destination:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:output=120 kw", - "then": { - "en": "
Tesla Supercharger (destination)
outputs at most 120 kw", - "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 120 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=150 kw", - "then": { - "en": "
Tesla Supercharger (destination)
outputs at most 150 kw", - "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=250 kw", - "then": { - "en": "
Tesla Supercharger (destination)
outputs at most 250 kw", - "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 250 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "plugs-12", - "question": { - "en": "How much plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
are available here?", - "nl": "Hoeveel stekkers van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:tesla_destination} plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
available here", - "nl": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "freeform": { - "key": "socket:tesla_destination", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "voltage-12", - "question": { - "en": "What voltage do the plugs with
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", - "nl": "Welke spanning levert de stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "render": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs {socket:tesla_destination:voltage} volt", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van {socket:tesla_destination:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_destination:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:voltage=230 V", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 230 volt", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:tesla_destination:voltage=400 V", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 400 volt", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "current-12", - "question": { - "en": "What current do the plugs with
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", - "nl": "Welke stroom levert de stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
?" - }, - "render": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most {socket:tesla_destination:current}A", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal {socket:tesla_destination:current}A" - }, - "freeform": { - "key": "socket:tesla_destination:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:current=16 A", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 16 A", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:tesla_destination:current=32 A", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 32 A", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "power-output-12", - "question": { - "en": "What power output does a single plug of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
?" - }, - "render": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most {socket:tesla_destination:output}", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal {socket:tesla_destination:output}" - }, - "freeform": { - "key": "socket:tesla_destination:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:output=11 kw", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 11 kw", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=22 kw", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 22 kw", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "plugs-13", - "question": { - "en": "How much plugs of type
USB to charge phones and small electronics
are available here?", - "nl": "Hoeveel stekkers van type
USB om GSMs en kleine electronica op te laden
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:USB-A} plugs of type
USB to charge phones and small electronics
available here", - "nl": "Hier zijn {socket:USB-A} stekkers van het type
USB om GSMs en kleine electronica op te laden
" - }, - "freeform": { - "key": "socket:USB-A", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "voltage-13", - "question": { - "en": "What voltage do the plugs with
USB to charge phones and small electronics
offer?", - "nl": "Welke spanning levert de stekker van type
USB om GSMs en kleine electronica op te laden
" - }, - "render": { - "en": "
USB to charge phones and small electronics
outputs {socket:USB-A:voltage} volt", - "nl": "
USB om GSMs en kleine electronica op te laden
heeft een spanning van {socket:USB-A:voltage} volt" - }, - "freeform": { - "key": "socket:USB-A:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:USB-A:voltage=5 V", - "then": { - "en": "
USB to charge phones and small electronics
outputs 5 volt", - "nl": "
USB om GSMs en kleine electronica op te laden
heeft een spanning van 5 volt" - } - } - ], - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "current-13", - "question": { - "en": "What current do the plugs with
USB to charge phones and small electronics
offer?", - "nl": "Welke stroom levert de stekker van type
USB om GSMs en kleine electronica op te laden
?" - }, - "render": { - "en": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:current}A", - "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal {socket:USB-A:current}A" - }, - "freeform": { - "key": "socket:USB-A:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:USB-A:current=1 A", - "then": { - "en": "
USB to charge phones and small electronics
outputs at most 1 A", - "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal 1 A" - } - }, - { - "if": "socket:socket:USB-A:current=2 A", - "then": { - "en": "
USB to charge phones and small electronics
outputs at most 2 A", - "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal 2 A" - } - } - ], - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "power-output-13", - "question": { - "en": "What power output does a single plug of type
USB to charge phones and small electronics
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
USB om GSMs en kleine electronica op te laden
?" - }, - "render": { - "en": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:output}", - "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal {socket:USB-A:output}" - }, - "freeform": { - "key": "socket:USB-A:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:USB-A:output=5w", - "then": { - "en": "
USB to charge phones and small electronics
outputs at most 5w", - "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal 5w" - } - }, - { - "if": "socket:socket:USB-A:output=10w", - "then": { - "en": "
USB to charge phones and small electronics
outputs at most 10w", - "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal 10w" - } - } - ], - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "plugs-14", - "question": { - "en": "How much plugs of type
Bosch Active Connect with 3 pins and cable
are available here?", - "nl": "Hoeveel stekkers van type
Bosch Active Connect met 3 pinnen aan een kabel
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:bosch_3pin} plugs of type
Bosch Active Connect with 3 pins and cable
available here", - "nl": "Hier zijn {socket:bosch_3pin} stekkers van het type
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "freeform": { - "key": "socket:bosch_3pin", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:bosch_3pin~*", - "socket:bosch_3pin!=0" - ] - } - }, - { - "id": "voltage-14", - "question": { - "en": "What voltage do the plugs with
Bosch Active Connect with 3 pins and cable
offer?", - "nl": "Welke spanning levert de stekker van type
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "render": { - "en": "
Bosch Active Connect with 3 pins and cable
outputs {socket:bosch_3pin:voltage} volt", - "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
heeft een spanning van {socket:bosch_3pin:voltage} volt" - }, - "freeform": { - "key": "socket:bosch_3pin:voltage", - "type": "pfloat" - }, - "mappings": [], - "condition": { - "and": [ - "socket:bosch_3pin~*", - "socket:bosch_3pin!=0" - ] - } - }, - { - "id": "current-14", - "question": { - "en": "What current do the plugs with
Bosch Active Connect with 3 pins and cable
offer?", - "nl": "Welke stroom levert de stekker van type
Bosch Active Connect met 3 pinnen aan een kabel
?" - }, - "render": { - "en": "
Bosch Active Connect with 3 pins and cable
outputs at most {socket:bosch_3pin:current}A", - "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
levert een stroom van maximaal {socket:bosch_3pin:current}A" - }, - "freeform": { - "key": "socket:bosch_3pin:current", - "type": "pfloat" - }, - "mappings": [], - "condition": { - "and": [ - "socket:bosch_3pin~*", - "socket:bosch_3pin!=0" - ] - } - }, - { - "id": "power-output-14", - "question": { - "en": "What power output does a single plug of type
Bosch Active Connect with 3 pins and cable
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Bosch Active Connect met 3 pinnen aan een kabel
?" - }, - "render": { - "en": "
Bosch Active Connect with 3 pins and cable
outputs at most {socket:bosch_3pin:output}", - "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
levert een vermogen van maximaal {socket:bosch_3pin:output}" - }, - "freeform": { - "key": "socket:bosch_3pin:output", - "type": "pfloat" - }, - "mappings": [], - "condition": { - "and": [ - "socket:bosch_3pin~*", - "socket:bosch_3pin!=0" - ] - } - }, - { - "id": "plugs-15", - "question": { - "en": "How much plugs of type
Bosch Active Connect with 5 pins and cable
are available here?", - "nl": "Hoeveel stekkers van type
Bosch Active Connect met 5 pinnen aan een kabel
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:bosch_5pin} plugs of type
Bosch Active Connect with 5 pins and cable
available here", - "nl": "Hier zijn {socket:bosch_5pin} stekkers van het type
Bosch Active Connect met 5 pinnen aan een kabel
" - }, - "freeform": { - "key": "socket:bosch_5pin", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:bosch_5pin~*", - "socket:bosch_5pin!=0" - ] - } - }, - { - "id": "voltage-15", - "question": { - "en": "What voltage do the plugs with
Bosch Active Connect with 5 pins and cable
offer?", - "nl": "Welke spanning levert de stekker van type
Bosch Active Connect met 5 pinnen aan een kabel
" - }, - "render": { - "en": "
Bosch Active Connect with 5 pins and cable
outputs {socket:bosch_5pin:voltage} volt", - "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
heeft een spanning van {socket:bosch_5pin:voltage} volt" - }, - "freeform": { - "key": "socket:bosch_5pin:voltage", - "type": "pfloat" - }, - "mappings": [], - "condition": { - "and": [ - "socket:bosch_5pin~*", - "socket:bosch_5pin!=0" - ] - } - }, - { - "id": "current-15", - "question": { - "en": "What current do the plugs with
Bosch Active Connect with 5 pins and cable
offer?", - "nl": "Welke stroom levert de stekker van type
Bosch Active Connect met 5 pinnen aan een kabel
?" - }, - "render": { - "en": "
Bosch Active Connect with 5 pins and cable
outputs at most {socket:bosch_5pin:current}A", - "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
levert een stroom van maximaal {socket:bosch_5pin:current}A" - }, - "freeform": { - "key": "socket:bosch_5pin:current", - "type": "pfloat" - }, - "mappings": [], - "condition": { - "and": [ - "socket:bosch_5pin~*", - "socket:bosch_5pin!=0" - ] - } - }, - { - "id": "power-output-15", - "question": { - "en": "What power output does a single plug of type
Bosch Active Connect with 5 pins and cable
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Bosch Active Connect met 5 pinnen aan een kabel
?" - }, - "render": { - "en": "
Bosch Active Connect with 5 pins and cable
outputs at most {socket:bosch_5pin:output}", - "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
levert een vermogen van maximaal {socket:bosch_5pin:output}" - }, - "freeform": { - "key": "socket:bosch_5pin:output", - "type": "pfloat" - }, - "mappings": [], - "condition": { - "and": [ - "socket:bosch_5pin~*", - "socket:bosch_5pin!=0" - ] - } + "id": "$$$" }, { "id": "Authentication", @@ -2801,7 +160,8 @@ "ja": "この充電ステーションはいつオープンしますか?", "nb_NO": "Når åpnet denne ladestasjonen?", "ru": "В какое время работает эта зарядная станция?", - "zh_Hant": "何時是充電站開放使用的時間?" + "zh_Hant": "何時是充電站開放使用的時間?", + "de": "Welche Authentifizierung ist an der Ladestation möglich?" }, "multiAnswer": true, "mappings": [ @@ -2809,56 +169,64 @@ "if": "authentication:membership_card=yes", "ifnot": "authentication:membership_card=no", "then": { - "en": "Authentication by a membership card" + "en": "Authentication by a membership card", + "de": "Authentifizierung durch eine Mitgliedskarte" } }, { "if": "authentication:app=yes", "ifnot": "authentication:app=no", "then": { - "en": "Authentication by an app" + "en": "Authentication by an app", + "de": "Authentifizierung durch eine App" } }, { "if": "authentication:phone_call=yes", "ifnot": "authentication:phone_call=no", "then": { - "en": "Authentication via phone call is available" + "en": "Authentication via phone call is available", + "de": "Authentifizierung per Anruf ist möglich" } }, { "if": "authentication:short_message=yes", "ifnot": "authentication:short_message=no", "then": { - "en": "Authentication via phone call is available" + "en": "Authentication via phone call is available", + "de": "Authentifizierung per Anruf ist möglich" } }, { "if": "authentication:nfc=yes", "ifnot": "authentication:nfc=no", "then": { - "en": "Authentication via NFC is available" + "en": "Authentication via NFC is available", + "de": "Authentifizierung über NFC ist möglich" } }, { "if": "authentication:money_card=yes", "ifnot": "authentication:money_card=no", "then": { - "en": "Authentication via Money Card is available" + "en": "Authentication via Money Card is available", + "de": "Authentifizierung über Geldkarte ist möglich" } }, { "if": "authentication:debit_card=yes", "ifnot": "authentication:debit_card=no", "then": { - "en": "Authentication via debit card is available" + "en": "Authentication via debit card is available", + "de": "Authentifizierung per Debitkarte ist möglich" } }, { "if": "authentication:none=yes", "ifnot": "authentication:none=no", "then": { - "en": "Charging here is (also) possible without authentication" + "en": "Charging here is (also) possible without authentication", + "de": "Keine Authentifizierung erforderlich" } } ] @@ -2871,14 +239,16 @@ "ja": "{network}", "nb_NO": "{network}", "ru": "{network}", - "zh_Hant": "{network}" + "zh_Hant": "{network}", + "de": "Authentifizierung durch Anruf oder SMS an {authentication:phone_call:number}" }, "question": { "en": "What's the phone number for authentication call or SMS?", "it": "A quale rete appartiene questa stazione di ricarica?", "ja": "この充電ステーションの運営チェーンはどこですか?", "ru": "К какой сети относится эта станция?", - "zh_Hant": "充電站所屬的網路是?" + "zh_Hant": "充電站所屬的網路是?", + "de": "Wie lautet die Telefonnummer für den Authentifizierungsanruf oder die SMS?" }, "freeform": { "key": "authentication:phone_call:number", @@ -2919,13 +289,15 @@ "type": "opening_hours" }, "question": { - "en": "When is this charging station opened?" + "en": "When is this charging station opened?", + "de": "Wann ist diese Ladestation geöffnet?" }, "mappings": [ { "if": "opening_hours=24/7", "then": { - "en": "24/7 opened (including holidays)" + "en": "24/7 opened (including holidays)", + "de": "durchgehend geöffnet (auch an Feiertagen)" } } ] @@ -3017,10 +389,12 @@ { "id": "Network", "render": { - "en": "Part of the network {network}" + "en": "Part of the network {network}", + "de": "Teil des Netzwerks {network}" }, "question": { - "en": "Is this charging station part of a network?" + "en": "Is this charging station part of a network?", + "de": "Ist diese Ladestation Teil eines Netzwerks?" }, "freeform": { "key": "network" @@ -3029,13 +403,15 @@ { "if": "no:network=yes", "then": { - "en": "Not part of a bigger network" + "en": "Not part of a bigger network", + "de": "Nicht Teil eines größeren Netzwerks" } }, { "if": "network=none", "then": { - "en": "Not part of a bigger network" + "en": "Not part of a bigger network", + "de": "Nicht Teil eines größeren Netzwerks" }, "hideInAnswer": true }, @@ -3056,10 +432,12 @@ { "id": "Operator", "question": { - "en": "Who is the operator of this charging station?" + "en": "Who is the operator of this charging station?", + "de": "Wer ist der Betreiber dieser Ladestation?" }, "render": { - "en": "This charging station is operated by {operator}" + "en": "This charging station is operated by {operator}", + "de": "Diese Ladestation wird betrieben von {operator}" }, "freeform": { "key": "operator" @@ -3072,7 +450,8 @@ ] }, "then": { - "en": "Actually, {operator} is the network" + "en": "Actually, {operator} is the network", + "de": "Eigentlich ist {operator} das Netzwerk" }, "addExtraTags": [ "operator=" @@ -3137,14 +516,16 @@ "id": "Operational status", "question": { "en": "Is this charging point in use?", - "nl": "Is dit oplaadpunt operationeel?" + "nl": "Is dit oplaadpunt operationeel?", + "de": "Ist dieser Ladepunkt in Betrieb?" }, "mappings": [ { "if": "operational_status=broken", "then": { "en": "This charging station is broken", - "nl": "Dit oplaadpunt is kapot" + "nl": "Dit oplaadpunt is kapot", + "de": "Diese Ladestation ist kaputt" } }, { @@ -3156,7 +537,8 @@ }, "then": { "en": "A charging station is planned here", - "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden" + "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden", + "de": "Hier ist eine Ladestation geplant" } }, { @@ -3168,7 +550,8 @@ }, "then": { "en": "A charging station is constructed here", - "nl": "Hier wordt op dit moment een oplaadpunt gebouwd" + "nl": "Hier wordt op dit moment een oplaadpunt gebouwd", + "de": "Hier wird eine Ladestation gebaut" } }, { @@ -3180,7 +563,8 @@ }, "then": { "en": "This charging station has beed permanently disabled and is not in use anymore but is still visible", - "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig" + "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig", + "de": "Diese Ladestation wurde dauerhaft deaktiviert und wird nicht mehr benutzt, ist aber noch sichtbar" } }, { @@ -3192,7 +576,8 @@ }, "then": { "en": "This charging station works", - "nl": "Dit oplaadpunt werkt" + "nl": "Dit oplaadpunt werkt", + "de": "Diese Ladestation funktioniert" } } ] @@ -3200,19 +585,22 @@ { "id": "Parking:fee", "question": { - "en": "Does one have to pay a parking fee while charging?" + "en": "Does one have to pay a parking fee while charging?", + "de": "Muss man beim Laden eine Parkgebühr bezahlen?" }, "mappings": [ { "if": "parking:fee=no", "then": { - "en": "No additional parking cost while charging" + "en": "No additional parking cost while charging", + "de": "Keine zusätzlichen Parkgebühren beim Laden" } }, { "if": "parking:fee=yes", "then": { - "en": "An additional parking fee should be paid while charging" + "en": "An additional parking fee should be paid while charging", + "de": "Beim Laden ist eine zusätzliche Parkgebühr zu entrichten" } } ] @@ -3221,8 +609,64 @@ "mapRendering": [ { "location": [ - "point" - ] + "point", + "centroid" + ], + "icon": { + "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", + "mappings": [ + { + "if": "bicycle=yes", + "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" + }, + { + "if": { + "or": [ + "car=yes", + "motorcar=yes" + ] + }, + "then": "pin:#fff;./assets/themes/charging_stations/car.svg" + } + ] + }, + "iconBadges": [ + { + "if": { + "or": [ + "disused:amenity=charging_station", + "operational_status=broken" + ] + }, + "then": "cross:#c22;" + }, + { + "if": { + "or": [ + "proposed:amenity=charging_station", + "planned:amenity=charging_station" + ] + }, + "then": "./assets/layers/charging_station/under_construction.svg" + }, + { + "if": { + "and": [ + "bicycle=yes", + { + "or": [ + "motorcar=yes", + "car=yes" + ] + } + ] + }, + "then": "circle:#fff;./assets/themes/charging_stations/car.svg" + } + ], + "iconSize": { + "render": "50,50,bottom" + } } ], "presets": [ @@ -3231,7 +675,9 @@ "amenity=charging_station" ], "title": { - "en": "Charging station" + "en": "Charging station", + "de": "Ladestation", + "ru": "Зарядная станция" }, "preciseInput": { "preferredBackground": "map" @@ -3246,20 +692,23 @@ { "question": { "en": "All vehicle types", - "nl": "Alle voertuigen" + "nl": "Alle voertuigen", + "de": "Alle Fahrzeugtypen" } }, { "question": { "en": "Charging station for bicycles", - "nl": "Oplaadpunten voor fietsen" + "nl": "Oplaadpunten voor fietsen", + "de": "Ladestation für Fahrräder" }, "osmTags": "bicycle=yes" }, { "question": { "en": "Charging station for cars", - "nl": "Oplaadpunten voor auto's" + "nl": "Oplaadpunten voor auto's", + "de": "Ladestation für Autos" }, "osmTags": { "or": [ @@ -3275,7 +724,8 @@ "options": [ { "question": { - "en": "Only working charging stations" + "en": "Only working charging stations", + "de": "Nur funktionierende Ladestationen" }, "osmTags": { "and": [ @@ -3292,7 +742,8 @@ { "question": { "en": "All connectors", - "nl": "Alle types" + "nl": "Alle types", + "de": "Alle Anschlüsse" } }, { @@ -3428,11 +879,15 @@ ], "human": { "en": " minutes", - "nl": " minuten" + "nl": " minuten", + "de": " Minuten", + "ru": " минут" }, "humanSingular": { "en": " minute", - "nl": " minuut" + "nl": " minuut", + "de": " Minute", + "ru": " минута" } }, { @@ -3448,11 +903,14 @@ ], "human": { "en": " hours", - "nl": " uren" + "nl": " uren", + "de": " Stunden", + "ru": " часов" }, "humanSingular": { "en": " hour", - "nl": " uur" + "nl": " uur", + "ru": " час" } }, { @@ -3465,11 +923,14 @@ ], "human": { "en": " days", - "nl": " day" + "nl": " day", + "de": " Tage", + "ru": " дней" }, "humanSingular": { "en": " day", - "nl": " dag" + "nl": " dag", + "ru": " день" } } ] @@ -3505,7 +966,8 @@ ], "human": { "en": "Volts", - "nl": "volt" + "nl": "volt", + "ru": "Вольт" } } ], @@ -3574,7 +1036,8 @@ ], "human": { "en": "kilowatt", - "nl": "kilowatt" + "nl": "kilowatt", + "ru": "киловатт" } }, { @@ -3584,7 +1047,8 @@ ], "human": { "en": "megawatt", - "nl": "megawatt" + "nl": "megawatt", + "ru": "мегаватт" } } ], diff --git a/assets/layers/charging_station/charging_station.protojson b/assets/layers/charging_station/charging_station.protojson index a44a660ba..504b026ed 100644 --- a/assets/layers/charging_station/charging_station.protojson +++ b/assets/layers/charging_station/charging_station.protojson @@ -55,7 +55,7 @@ }, { "if": "motorcar=yes", - "ifnot": "motorcar=no", + "ifnot": "motorcar=no", "then": { "en": "Cars can be charged here" } @@ -137,7 +137,9 @@ "type": "pnat" } }, - {"id": "$$$"}, + { + "id": "$$$" + }, { "id": "Authentication", "question": { @@ -356,7 +358,7 @@ "en": "No timelimit on leaving your vehicle here", "nl": "Geen maximum parkeertijd" } - } + } ] }, { @@ -530,7 +532,10 @@ }, { "if": { - "and": ["amenity=charging_station","operational_status="] + "and": [ + "amenity=charging_station", + "operational_status=" + ] }, "then": { "en": "This charging station works", @@ -562,6 +567,10 @@ ], "mapRendering": [ { + "location": [ + "point", + "centroid" + ], "icon": { "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", "mappings": [ @@ -580,7 +589,7 @@ } ] }, - "iconOverlays": [ + "iconBadges": [ { "if": { "or": [ @@ -588,7 +597,7 @@ "operational_status=broken" ] }, - "then": "cross_bottom_right:#c22;" + "then": "cross:#c22;" }, { "if": { @@ -597,8 +606,7 @@ "planned:amenity=charging_station" ] }, - "then": "./assets/layers/charging_station/under_construction.svg", - "badge": true + "then": "./assets/layers/charging_station/under_construction.svg" }, { "if": { @@ -612,8 +620,7 @@ } ] }, - "then": "circle:#fff;./assets/themes/charging_stations/car.svg", - "badge": true + "then": "circle:#fff;./assets/themes/charging_stations/car.svg" } ], "iconSize": { @@ -740,7 +747,7 @@ "en": " days", "nl": " day" }, - "humanSingular":{ + "humanSingular": { "en": " day", "nl": " dag" } diff --git a/assets/svg/license_info.json b/assets/svg/license_info.json index 8e1eaa626..837704d79 100644 --- a/assets/svg/license_info.json +++ b/assets/svg/license_info.json @@ -1412,7 +1412,7 @@ ] }, { - "path": "teardrop_with_hole_green.svg", + "path": "teardrop.svg", "license": "CC-BY-SA 3.0 Unported", "authors": [ "Mono, derivated work User:Benoit Rochon" @@ -1421,8 +1421,8 @@ "https://commons.wikimedia.org/wiki/File:Map_pin_icon_green.svg" ] }, - { - "path": "teardrop.svg", + { + "path": "teardrop_with_hole_green.svg", "license": "CC-BY-SA 3.0 Unported", "authors": [ "Mono, derivated work User:Benoit Rochon" @@ -1523,4 +1523,4 @@ "https://www.wikipedia.org/" ] } -] +] \ No newline at end of file diff --git a/langs/layers/de.json b/langs/layers/de.json index 3e15741f4..b53b829ed 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -998,74 +998,6 @@ }, "question": "Welche Authentifizierung ist an der Ladestation möglich?" }, - "Available_charging_stations (generated)": { - "mappings": { - "5": { - "then": "
Chademo
" - }, - "6": { - "then": "
Typ 1 mit Kabel (J1772)
" - }, - "7": { - "then": "
Typ 1 mit Kabel (J1772)
" - }, - "8": { - "then": "
Typ 1 ohne Kabel (J1772)
" - }, - "9": { - "then": "
Typ 1 ohne Kabel (J1772)
" - }, - "10": { - "then": "
Typ 1 CCS (auch bekannt als Typ 1 Combo)
" - }, - "11": { - "then": "
Typ 1 CCS (auch bekannt als Typ 1 Combo)
" - }, - "12": { - "then": "
Tesla Supercharger
" - }, - "13": { - "then": "
Tesla Supercharger
" - }, - "14": { - "then": "
Typ 2 (Mennekes)
" - }, - "15": { - "then": "
Typ 2 (Mennekes)
" - }, - "16": { - "then": "
Typ 2 CCS (Mennekes)
" - }, - "17": { - "then": "
Typ 2 CCS (Mennekes)
" - }, - "18": { - "then": "
Typ 2 mit Kabel (Mennekes)
" - }, - "19": { - "then": "
Typ 2 mit Kabel (Mennekes)
" - }, - "20": { - "then": "
Tesla Supercharger CCS (Typ 2 CSS)
" - }, - "21": { - "then": "
Tesla Supercharger CCS (Typ 2 CSS)
" - }, - "26": { - "then": "
USB zum Laden von Smartphones oder Elektrokleingeräten
" - }, - "27": { - "then": "
USB zum Laden von Smartphones und Elektrokleingeräten
" - }, - "30": { - "then": "
Bosch Active Connect mit 5 Pins und Kabel
" - }, - "31": { - "then": "
Bosch Active Connect mit 5 Pins und Kabel
" - } - }, - "question": "Welche Ladestationen gibt es hier?" - }, "Network": { "mappings": { "0": { diff --git a/langs/layers/en.json b/langs/layers/en.json index ec5f82bd1..7c61d370c 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -1052,107 +1052,6 @@ }, "question": "What kind of authentication is available at the charging station?" }, - "Available_charging_stations (generated)": { - "mappings": { - "0": { - "then": "
Schuko wall plug without ground pin (CEE7/4 type F)
" - }, - "1": { - "then": "
Schuko wall plug without ground pin (CEE7/4 type F)
" - }, - "2": { - "then": "
European wall plug with ground pin (CEE7/4 type E)
" - }, - "3": { - "then": "
European wall plug with ground pin (CEE7/4 type E)
" - }, - "4": { - "then": "
Chademo
" - }, - "5": { - "then": "
Chademo
" - }, - "6": { - "then": "
Type 1 with cable (J1772)
" - }, - "7": { - "then": "
Type 1 with cable (J1772)
" - }, - "8": { - "then": "
Type 1 without cable (J1772)
" - }, - "9": { - "then": "
Type 1 without cable (J1772)
" - }, - "10": { - "then": "
Type 1 CCS (aka Type 1 Combo)
" - }, - "11": { - "then": "
Type 1 CCS (aka Type 1 Combo)
" - }, - "12": { - "then": "
Tesla Supercharger
" - }, - "13": { - "then": "
Tesla Supercharger
" - }, - "14": { - "then": "
Type 2 (mennekes)
" - }, - "15": { - "then": "
Type 2 (mennekes)
" - }, - "16": { - "then": "
Type 2 CCS (mennekes)
" - }, - "17": { - "then": "
Type 2 CCS (mennekes)
" - }, - "18": { - "then": "
Type 2 with cable (mennekes)
" - }, - "19": { - "then": "
Type 2 with cable (mennekes)
" - }, - "20": { - "then": "
Tesla Supercharger CCS (a branded type2_css)
" - }, - "21": { - "then": "
Tesla Supercharger CCS (a branded type2_css)
" - }, - "22": { - "then": "
Tesla Supercharger (destination)
" - }, - "23": { - "then": "
Tesla Supercharger (destination)
" - }, - "24": { - "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
" - }, - "25": { - "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
" - }, - "26": { - "then": "
USB to charge phones and small electronics
" - }, - "27": { - "then": "
USB to charge phones and small electronics
" - }, - "28": { - "then": "
Bosch Active Connect with 3 pins and cable
" - }, - "29": { - "then": "
Bosch Active Connect with 3 pins and cable
" - }, - "30": { - "then": "
Bosch Active Connect with 5 pins and cable
" - }, - "31": { - "then": "
Bosch Active Connect with 5 pins and cable
" - } - }, - "question": "Which charging stations are available here?" - }, "Network": { "mappings": { "0": { @@ -1241,167 +1140,6 @@ "question": "How much vehicles can be charged here at the same time?", "render": "{capacity} vehicles can be charged here at the same time" }, - "current-0": { - "mappings": { - "0": { - "then": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 16 A" - } - }, - "question": "What current do the plugs with
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", - "render": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most {socket:schuko:current}A" - }, - "current-1": { - "mappings": { - "0": { - "then": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 16 A" - } - }, - "question": "What current do the plugs with
European wall plug with ground pin (CEE7/4 type E)
offer?", - "render": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most {socket:typee:current}A" - }, - "current-10": { - "mappings": { - "0": { - "then": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 125 A" - }, - "1": { - "then": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 350 A" - } - }, - "question": "What current do the plugs with
Tesla Supercharger CCS (a branded type2_css)
offer?", - "render": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most {socket:tesla_supercharger_ccs:current}A" - }, - "current-11": { - "mappings": { - "0": { - "then": "
Tesla Supercharger (destination)
outputs at most 125 A" - }, - "1": { - "then": "
Tesla Supercharger (destination)
outputs at most 350 A" - } - }, - "question": "What current do the plugs with
Tesla Supercharger (destination)
offer?", - "render": "
Tesla Supercharger (destination)
outputs at most {socket:tesla_destination:current}A" - }, - "current-12": { - "mappings": { - "0": { - "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 16 A" - }, - "1": { - "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 32 A" - } - }, - "question": "What current do the plugs with
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", - "render": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most {socket:tesla_destination:current}A" - }, - "current-13": { - "mappings": { - "0": { - "then": "
USB to charge phones and small electronics
outputs at most 1 A" - }, - "1": { - "then": "
USB to charge phones and small electronics
outputs at most 2 A" - } - }, - "question": "What current do the plugs with
USB to charge phones and small electronics
offer?", - "render": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:current}A" - }, - "current-14": { - "question": "What current do the plugs with
Bosch Active Connect with 3 pins and cable
offer?", - "render": "
Bosch Active Connect with 3 pins and cable
outputs at most {socket:bosch_3pin:current}A" - }, - "current-15": { - "question": "What current do the plugs with
Bosch Active Connect with 5 pins and cable
offer?", - "render": "
Bosch Active Connect with 5 pins and cable
outputs at most {socket:bosch_5pin:current}A" - }, - "current-2": { - "mappings": { - "0": { - "then": "
Chademo
outputs at most 120 A" - } - }, - "question": "What current do the plugs with
Chademo
offer?", - "render": "
Chademo
outputs at most {socket:chademo:current}A" - }, - "current-3": { - "mappings": { - "0": { - "then": "
Type 1 with cable (J1772)
outputs at most 32 A" - } - }, - "question": "What current do the plugs with
Type 1 with cable (J1772)
offer?", - "render": "
Type 1 with cable (J1772)
outputs at most {socket:type1_cable:current}A" - }, - "current-4": { - "mappings": { - "0": { - "then": "
Type 1 without cable (J1772)
outputs at most 32 A" - } - }, - "question": "What current do the plugs with
Type 1 without cable (J1772)
offer?", - "render": "
Type 1 without cable (J1772)
outputs at most {socket:type1:current}A" - }, - "current-5": { - "mappings": { - "0": { - "then": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 A" - }, - "1": { - "then": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 125 A" - } - }, - "question": "What current do the plugs with
Type 1 CCS (aka Type 1 Combo)
offer?", - "render": "
Type 1 CCS (aka Type 1 Combo)
outputs at most {socket:type1_combo:current}A" - }, - "current-6": { - "mappings": { - "0": { - "then": "
Tesla Supercharger
outputs at most 125 A" - }, - "1": { - "then": "
Tesla Supercharger
outputs at most 350 A" - } - }, - "question": "What current do the plugs with
Tesla Supercharger
offer?", - "render": "
Tesla Supercharger
outputs at most {socket:tesla_supercharger:current}A" - }, - "current-7": { - "mappings": { - "0": { - "then": "
Type 2 (mennekes)
outputs at most 16 A" - }, - "1": { - "then": "
Type 2 (mennekes)
outputs at most 32 A" - } - }, - "question": "What current do the plugs with
Type 2 (mennekes)
offer?", - "render": "
Type 2 (mennekes)
outputs at most {socket:type2:current}A" - }, - "current-8": { - "mappings": { - "0": { - "then": "
Type 2 CCS (mennekes)
outputs at most 125 A" - }, - "1": { - "then": "
Type 2 CCS (mennekes)
outputs at most 350 A" - } - }, - "question": "What current do the plugs with
Type 2 CCS (mennekes)
offer?", - "render": "
Type 2 CCS (mennekes)
outputs at most {socket:type2_combo:current}A" - }, - "current-9": { - "mappings": { - "0": { - "then": "
Type 2 with cable (mennekes)
outputs at most 16 A" - }, - "1": { - "then": "
Type 2 with cable (mennekes)
outputs at most 32 A" - } - }, - "question": "What current do the plugs with
Type 2 with cable (mennekes)
offer?", - "render": "
Type 2 with cable (mennekes)
outputs at most {socket:type2_cable:current}A" - }, "email": { "question": "What is the email address of the operator?", "render": "In case of problems, send an email to {email}" @@ -1440,414 +1178,10 @@ "question": "What number can one call if there is a problem with this charging station?", "render": "In case of problems, call {phone}" }, - "plugs-0": { - "question": "How much plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
are available here?", - "render": "There are {socket:schuko} plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
available here" - }, - "plugs-1": { - "question": "How much plugs of type
European wall plug with ground pin (CEE7/4 type E)
are available here?", - "render": "There are {socket:typee} plugs of type
European wall plug with ground pin (CEE7/4 type E)
available here" - }, - "plugs-10": { - "question": "How much plugs of type
Tesla Supercharger CCS (a branded type2_css)
are available here?", - "render": "There are {socket:tesla_supercharger_ccs} plugs of type
Tesla Supercharger CCS (a branded type2_css)
available here" - }, - "plugs-11": { - "question": "How much plugs of type
Tesla Supercharger (destination)
are available here?", - "render": "There are {socket:tesla_destination} plugs of type
Tesla Supercharger (destination)
available here" - }, - "plugs-12": { - "question": "How much plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
are available here?", - "render": "There are {socket:tesla_destination} plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
available here" - }, - "plugs-13": { - "question": "How much plugs of type
USB to charge phones and small electronics
are available here?", - "render": "There are {socket:USB-A} plugs of type
USB to charge phones and small electronics
available here" - }, - "plugs-14": { - "question": "How much plugs of type
Bosch Active Connect with 3 pins and cable
are available here?", - "render": "There are {socket:bosch_3pin} plugs of type
Bosch Active Connect with 3 pins and cable
available here" - }, - "plugs-15": { - "question": "How much plugs of type
Bosch Active Connect with 5 pins and cable
are available here?", - "render": "There are {socket:bosch_5pin} plugs of type
Bosch Active Connect with 5 pins and cable
available here" - }, - "plugs-2": { - "question": "How much plugs of type
Chademo
are available here?", - "render": "There are {socket:chademo} plugs of type
Chademo
available here" - }, - "plugs-3": { - "question": "How much plugs of type
Type 1 with cable (J1772)
are available here?", - "render": "There are {socket:type1_cable} plugs of type
Type 1 with cable (J1772)
available here" - }, - "plugs-4": { - "question": "How much plugs of type
Type 1 without cable (J1772)
are available here?", - "render": "There are {socket:type1} plugs of type
Type 1 without cable (J1772)
available here" - }, - "plugs-5": { - "question": "How much plugs of type
Type 1 CCS (aka Type 1 Combo)
are available here?", - "render": "There are {socket:type1_combo} plugs of type
Type 1 CCS (aka Type 1 Combo)
available here" - }, - "plugs-6": { - "question": "How much plugs of type
Tesla Supercharger
are available here?", - "render": "There are {socket:tesla_supercharger} plugs of type
Tesla Supercharger
available here" - }, - "plugs-7": { - "question": "How much plugs of type
Type 2 (mennekes)
are available here?", - "render": "There are {socket:type2} plugs of type
Type 2 (mennekes)
available here" - }, - "plugs-8": { - "question": "How much plugs of type
Type 2 CCS (mennekes)
are available here?", - "render": "There are {socket:type2_combo} plugs of type
Type 2 CCS (mennekes)
available here" - }, - "plugs-9": { - "question": "How much plugs of type
Type 2 with cable (mennekes)
are available here?", - "render": "There are {socket:type2_cable} plugs of type
Type 2 with cable (mennekes)
available here" - }, - "power-output-0": { - "mappings": { - "0": { - "then": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 3.6 kw" - } - }, - "question": "What power output does a single plug of type
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", - "render": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most {socket:schuko:output}" - }, - "power-output-1": { - "mappings": { - "0": { - "then": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 3 kw" - }, - "1": { - "then": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 22 kw" - } - }, - "question": "What power output does a single plug of type
European wall plug with ground pin (CEE7/4 type E)
offer?", - "render": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most {socket:typee:output}" - }, - "power-output-10": { - "mappings": { - "0": { - "then": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 50 kw" - } - }, - "question": "What power output does a single plug of type
Tesla Supercharger CCS (a branded type2_css)
offer?", - "render": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most {socket:tesla_supercharger_ccs:output}" - }, - "power-output-11": { - "mappings": { - "0": { - "then": "
Tesla Supercharger (destination)
outputs at most 120 kw" - }, - "1": { - "then": "
Tesla Supercharger (destination)
outputs at most 150 kw" - }, - "2": { - "then": "
Tesla Supercharger (destination)
outputs at most 250 kw" - } - }, - "question": "What power output does a single plug of type
Tesla Supercharger (destination)
offer?", - "render": "
Tesla Supercharger (destination)
outputs at most {socket:tesla_destination:output}" - }, - "power-output-12": { - "mappings": { - "0": { - "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 11 kw" - }, - "1": { - "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 22 kw" - } - }, - "question": "What power output does a single plug of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", - "render": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most {socket:tesla_destination:output}" - }, - "power-output-13": { - "mappings": { - "0": { - "then": "
USB to charge phones and small electronics
outputs at most 5w" - }, - "1": { - "then": "
USB to charge phones and small electronics
outputs at most 10w" - } - }, - "question": "What power output does a single plug of type
USB to charge phones and small electronics
offer?", - "render": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:output}" - }, - "power-output-14": { - "question": "What power output does a single plug of type
Bosch Active Connect with 3 pins and cable
offer?", - "render": "
Bosch Active Connect with 3 pins and cable
outputs at most {socket:bosch_3pin:output}" - }, - "power-output-15": { - "question": "What power output does a single plug of type
Bosch Active Connect with 5 pins and cable
offer?", - "render": "
Bosch Active Connect with 5 pins and cable
outputs at most {socket:bosch_5pin:output}" - }, - "power-output-2": { - "mappings": { - "0": { - "then": "
Chademo
outputs at most 50 kw" - } - }, - "question": "What power output does a single plug of type
Chademo
offer?", - "render": "
Chademo
outputs at most {socket:chademo:output}" - }, - "power-output-3": { - "mappings": { - "0": { - "then": "
Type 1 with cable (J1772)
outputs at most 3.7 kw" - }, - "1": { - "then": "
Type 1 with cable (J1772)
outputs at most 7 kw" - } - }, - "question": "What power output does a single plug of type
Type 1 with cable (J1772)
offer?", - "render": "
Type 1 with cable (J1772)
outputs at most {socket:type1_cable:output}" - }, - "power-output-4": { - "mappings": { - "0": { - "then": "
Type 1 without cable (J1772)
outputs at most 3.7 kw" - }, - "1": { - "then": "
Type 1 without cable (J1772)
outputs at most 6.6 kw" - }, - "2": { - "then": "
Type 1 without cable (J1772)
outputs at most 7 kw" - }, - "3": { - "then": "
Type 1 without cable (J1772)
outputs at most 7.2 kw" - } - }, - "question": "What power output does a single plug of type
Type 1 without cable (J1772)
offer?", - "render": "
Type 1 without cable (J1772)
outputs at most {socket:type1:output}" - }, - "power-output-5": { - "mappings": { - "0": { - "then": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 kw" - }, - "1": { - "then": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 62.5 kw" - }, - "2": { - "then": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 150 kw" - }, - "3": { - "then": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 350 kw" - } - }, - "question": "What power output does a single plug of type
Type 1 CCS (aka Type 1 Combo)
offer?", - "render": "
Type 1 CCS (aka Type 1 Combo)
outputs at most {socket:type1_combo:output}" - }, - "power-output-6": { - "mappings": { - "0": { - "then": "
Tesla Supercharger
outputs at most 120 kw" - }, - "1": { - "then": "
Tesla Supercharger
outputs at most 150 kw" - }, - "2": { - "then": "
Tesla Supercharger
outputs at most 250 kw" - } - }, - "question": "What power output does a single plug of type
Tesla Supercharger
offer?", - "render": "
Tesla Supercharger
outputs at most {socket:tesla_supercharger:output}" - }, - "power-output-7": { - "mappings": { - "0": { - "then": "
Type 2 (mennekes)
outputs at most 11 kw" - }, - "1": { - "then": "
Type 2 (mennekes)
outputs at most 22 kw" - } - }, - "question": "What power output does a single plug of type
Type 2 (mennekes)
offer?", - "render": "
Type 2 (mennekes)
outputs at most {socket:type2:output}" - }, - "power-output-8": { - "mappings": { - "0": { - "then": "
Type 2 CCS (mennekes)
outputs at most 50 kw" - } - }, - "question": "What power output does a single plug of type
Type 2 CCS (mennekes)
offer?", - "render": "
Type 2 CCS (mennekes)
outputs at most {socket:type2_combo:output}" - }, - "power-output-9": { - "mappings": { - "0": { - "then": "
Type 2 with cable (mennekes)
outputs at most 11 kw" - }, - "1": { - "then": "
Type 2 with cable (mennekes)
outputs at most 22 kw" - } - }, - "question": "What power output does a single plug of type
Type 2 with cable (mennekes)
offer?", - "render": "
Type 2 with cable (mennekes)
outputs at most {socket:type2_cable:output}" - }, "ref": { "question": "What is the reference number of this charging station?", "render": "Reference number is {ref}" }, - "voltage-0": { - "mappings": { - "0": { - "then": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs 230 volt" - } - }, - "question": "What voltage do the plugs with
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", - "render": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs {socket:schuko:voltage} volt" - }, - "voltage-1": { - "mappings": { - "0": { - "then": "
European wall plug with ground pin (CEE7/4 type E)
outputs 230 volt" - } - }, - "question": "What voltage do the plugs with
European wall plug with ground pin (CEE7/4 type E)
offer?", - "render": "
European wall plug with ground pin (CEE7/4 type E)
outputs {socket:typee:voltage} volt" - }, - "voltage-10": { - "mappings": { - "0": { - "then": "
Tesla Supercharger CCS (a branded type2_css)
outputs 500 volt" - }, - "1": { - "then": "
Tesla Supercharger CCS (a branded type2_css)
outputs 920 volt" - } - }, - "question": "What voltage do the plugs with
Tesla Supercharger CCS (a branded type2_css)
offer?", - "render": "
Tesla Supercharger CCS (a branded type2_css)
outputs {socket:tesla_supercharger_ccs:voltage} volt" - }, - "voltage-11": { - "mappings": { - "0": { - "then": "
Tesla Supercharger (destination)
outputs 480 volt" - } - }, - "question": "What voltage do the plugs with
Tesla Supercharger (destination)
offer?", - "render": "
Tesla Supercharger (destination)
outputs {socket:tesla_destination:voltage} volt" - }, - "voltage-12": { - "mappings": { - "0": { - "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 230 volt" - }, - "1": { - "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 400 volt" - } - }, - "question": "What voltage do the plugs with
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", - "render": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs {socket:tesla_destination:voltage} volt" - }, - "voltage-13": { - "mappings": { - "0": { - "then": "
USB to charge phones and small electronics
outputs 5 volt" - } - }, - "question": "What voltage do the plugs with
USB to charge phones and small electronics
offer?", - "render": "
USB to charge phones and small electronics
outputs {socket:USB-A:voltage} volt" - }, - "voltage-14": { - "question": "What voltage do the plugs with
Bosch Active Connect with 3 pins and cable
offer?", - "render": "
Bosch Active Connect with 3 pins and cable
outputs {socket:bosch_3pin:voltage} volt" - }, - "voltage-15": { - "question": "What voltage do the plugs with
Bosch Active Connect with 5 pins and cable
offer?", - "render": "
Bosch Active Connect with 5 pins and cable
outputs {socket:bosch_5pin:voltage} volt" - }, - "voltage-2": { - "mappings": { - "0": { - "then": "
Chademo
outputs 500 volt" - } - }, - "question": "What voltage do the plugs with
Chademo
offer?", - "render": "
Chademo
outputs {socket:chademo:voltage} volt" - }, - "voltage-3": { - "mappings": { - "0": { - "then": "
Type 1 with cable (J1772)
outputs 200 volt" - }, - "1": { - "then": "
Type 1 with cable (J1772)
outputs 240 volt" - } - }, - "question": "What voltage do the plugs with
Type 1 with cable (J1772)
offer?", - "render": "
Type 1 with cable (J1772)
outputs {socket:type1_cable:voltage} volt" - }, - "voltage-4": { - "mappings": { - "0": { - "then": "
Type 1 without cable (J1772)
outputs 200 volt" - }, - "1": { - "then": "
Type 1 without cable (J1772)
outputs 240 volt" - } - }, - "question": "What voltage do the plugs with
Type 1 without cable (J1772)
offer?", - "render": "
Type 1 without cable (J1772)
outputs {socket:type1:voltage} volt" - }, - "voltage-5": { - "mappings": { - "0": { - "then": "
Type 1 CCS (aka Type 1 Combo)
outputs 400 volt" - }, - "1": { - "then": "
Type 1 CCS (aka Type 1 Combo)
outputs 1000 volt" - } - }, - "question": "What voltage do the plugs with
Type 1 CCS (aka Type 1 Combo)
offer?", - "render": "
Type 1 CCS (aka Type 1 Combo)
outputs {socket:type1_combo:voltage} volt" - }, - "voltage-6": { - "mappings": { - "0": { - "then": "
Tesla Supercharger
outputs 480 volt" - } - }, - "question": "What voltage do the plugs with
Tesla Supercharger
offer?", - "render": "
Tesla Supercharger
outputs {socket:tesla_supercharger:voltage} volt" - }, - "voltage-7": { - "mappings": { - "0": { - "then": "
Type 2 (mennekes)
outputs 230 volt" - }, - "1": { - "then": "
Type 2 (mennekes)
outputs 400 volt" - } - }, - "question": "What voltage do the plugs with
Type 2 (mennekes)
offer?", - "render": "
Type 2 (mennekes)
outputs {socket:type2:voltage} volt" - }, - "voltage-8": { - "mappings": { - "0": { - "then": "
Type 2 CCS (mennekes)
outputs 500 volt" - }, - "1": { - "then": "
Type 2 CCS (mennekes)
outputs 920 volt" - } - }, - "question": "What voltage do the plugs with
Type 2 CCS (mennekes)
offer?", - "render": "
Type 2 CCS (mennekes)
outputs {socket:type2_combo:voltage} volt" - }, - "voltage-9": { - "mappings": { - "0": { - "then": "
Type 2 with cable (mennekes)
outputs 230 volt" - }, - "1": { - "then": "
Type 2 with cable (mennekes)
outputs 400 volt" - } - }, - "question": "What voltage do the plugs with
Type 2 with cable (mennekes)
offer?", - "render": "
Type 2 with cable (mennekes)
outputs {socket:type2_cable:voltage} volt" - }, "website": { "question": "What is the website of the operator?", "render": "More info on {website}" diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 5666dadd1..46830d4d0 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -1115,106 +1115,6 @@ } }, "tagRenderings": { - "Available_charging_stations (generated)": { - "mappings": { - "0": { - "then": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "1": { - "then": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "2": { - "then": "
Europese stekker met aardingspin (CEE7/4 type E)
" - }, - "3": { - "then": "
Europese stekker met aardingspin (CEE7/4 type E)
" - }, - "4": { - "then": "
Chademo
" - }, - "5": { - "then": "
Chademo
" - }, - "6": { - "then": "
Type 1 met kabel (J1772)
" - }, - "7": { - "then": "
Type 1 met kabel (J1772)
" - }, - "8": { - "then": "
Type 1 zonder kabel (J1772)
" - }, - "9": { - "then": "
Type 1 zonder kabel (J1772)
" - }, - "10": { - "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "11": { - "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "12": { - "then": "
Tesla Supercharger
" - }, - "13": { - "then": "
Tesla Supercharger
" - }, - "14": { - "then": "
Type 2 (mennekes)
" - }, - "15": { - "then": "
Type 2 (mennekes)
" - }, - "16": { - "then": "
Type 2 CCS (mennekes)
" - }, - "17": { - "then": "
Type 2 CCS (mennekes)
" - }, - "18": { - "then": "
Type 2 met kabel (J1772)
" - }, - "19": { - "then": "
Type 2 met kabel (J1772)
" - }, - "20": { - "then": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "21": { - "then": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "22": { - "then": "
Tesla Supercharger (destination)
" - }, - "23": { - "then": "
Tesla Supercharger (destination)
" - }, - "24": { - "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "25": { - "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "26": { - "then": "
USB om GSMs en kleine electronica op te laden
" - }, - "27": { - "then": "
USB om GSMs en kleine electronica op te laden
" - }, - "28": { - "then": "
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "29": { - "then": "
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "30": { - "then": "
Bosch Active Connect met 5 pinnen aan een kabel
" - }, - "31": { - "then": "
Bosch Active Connect met 5 pinnen aan een kabel
" - } - } - }, "Operational status": { "mappings": { "0": { @@ -1239,167 +1139,6 @@ "question": "Hoeveel voertuigen kunnen hier opgeladen worden?", "render": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden" }, - "current-0": { - "mappings": { - "0": { - "then": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een stroom van maximaal 16 A" - } - }, - "question": "Welke stroom levert de stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
?", - "render": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een stroom van maximaal {socket:schuko:current}A" - }, - "current-1": { - "mappings": { - "0": { - "then": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een stroom van maximaal 16 A" - } - }, - "question": "Welke stroom levert de stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
?", - "render": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een stroom van maximaal {socket:typee:current}A" - }, - "current-10": { - "mappings": { - "0": { - "then": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal 125 A" - }, - "1": { - "then": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal 350 A" - } - }, - "question": "Welke stroom levert de stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
?", - "render": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" - }, - "current-11": { - "mappings": { - "0": { - "then": "
Tesla Supercharger (destination)
levert een stroom van maximaal 125 A" - }, - "1": { - "then": "
Tesla Supercharger (destination)
levert een stroom van maximaal 350 A" - } - }, - "question": "Welke stroom levert de stekker van type
Tesla Supercharger (destination)
?", - "render": "
Tesla Supercharger (destination)
levert een stroom van maximaal {socket:tesla_destination:current}A" - }, - "current-12": { - "mappings": { - "0": { - "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal 16 A" - }, - "1": { - "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal 32 A" - } - }, - "question": "Welke stroom levert de stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
?", - "render": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal {socket:tesla_destination:current}A" - }, - "current-13": { - "mappings": { - "0": { - "then": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal 1 A" - }, - "1": { - "then": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal 2 A" - } - }, - "question": "Welke stroom levert de stekker van type
USB om GSMs en kleine electronica op te laden
?", - "render": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal {socket:USB-A:current}A" - }, - "current-14": { - "question": "Welke stroom levert de stekker van type
Bosch Active Connect met 3 pinnen aan een kabel
?", - "render": "
Bosch Active Connect met 3 pinnen aan een kabel
levert een stroom van maximaal {socket:bosch_3pin:current}A" - }, - "current-15": { - "question": "Welke stroom levert de stekker van type
Bosch Active Connect met 5 pinnen aan een kabel
?", - "render": "
Bosch Active Connect met 5 pinnen aan een kabel
levert een stroom van maximaal {socket:bosch_5pin:current}A" - }, - "current-2": { - "mappings": { - "0": { - "then": "
Chademo
levert een stroom van maximaal 120 A" - } - }, - "question": "Welke stroom levert de stekker van type
Chademo
?", - "render": "
Chademo
levert een stroom van maximaal {socket:chademo:current}A" - }, - "current-3": { - "mappings": { - "0": { - "then": "
Type 1 met kabel (J1772)
levert een stroom van maximaal 32 A" - } - }, - "question": "Welke stroom levert de stekker van type
Type 1 met kabel (J1772)
?", - "render": "
Type 1 met kabel (J1772)
levert een stroom van maximaal {socket:type1_cable:current}A" - }, - "current-4": { - "mappings": { - "0": { - "then": "
Type 1 zonder kabel (J1772)
levert een stroom van maximaal 32 A" - } - }, - "question": "Welke stroom levert de stekker van type
Type 1 zonder kabel (J1772)
?", - "render": "
Type 1 zonder kabel (J1772)
levert een stroom van maximaal {socket:type1:current}A" - }, - "current-5": { - "mappings": { - "0": { - "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal 50 A" - }, - "1": { - "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal 125 A" - } - }, - "question": "Welke stroom levert de stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
?", - "render": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal {socket:type1_combo:current}A" - }, - "current-6": { - "mappings": { - "0": { - "then": "
Tesla Supercharger
levert een stroom van maximaal 125 A" - }, - "1": { - "then": "
Tesla Supercharger
levert een stroom van maximaal 350 A" - } - }, - "question": "Welke stroom levert de stekker van type
Tesla Supercharger
?", - "render": "
Tesla Supercharger
levert een stroom van maximaal {socket:tesla_supercharger:current}A" - }, - "current-7": { - "mappings": { - "0": { - "then": "
Type 2 (mennekes)
levert een stroom van maximaal 16 A" - }, - "1": { - "then": "
Type 2 (mennekes)
levert een stroom van maximaal 32 A" - } - }, - "question": "Welke stroom levert de stekker van type
Type 2 (mennekes)
?", - "render": "
Type 2 (mennekes)
levert een stroom van maximaal {socket:type2:current}A" - }, - "current-8": { - "mappings": { - "0": { - "then": "
Type 2 CCS (mennekes)
levert een stroom van maximaal 125 A" - }, - "1": { - "then": "
Type 2 CCS (mennekes)
levert een stroom van maximaal 350 A" - } - }, - "question": "Welke stroom levert de stekker van type
Type 2 CCS (mennekes)
?", - "render": "
Type 2 CCS (mennekes)
levert een stroom van maximaal {socket:type2_combo:current}A" - }, - "current-9": { - "mappings": { - "0": { - "then": "
Type 2 met kabel (J1772)
levert een stroom van maximaal 16 A" - }, - "1": { - "then": "
Type 2 met kabel (J1772)
levert een stroom van maximaal 32 A" - } - }, - "question": "Welke stroom levert de stekker van type
Type 2 met kabel (J1772)
?", - "render": "
Type 2 met kabel (J1772)
levert een stroom van maximaal {socket:type2_cable:current}A" - }, "fee/charge": { "mappings": { "0": { @@ -1429,410 +1168,6 @@ } } } - }, - "plugs-0": { - "question": "Hoeveel stekkers van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft dit oplaadpunt?", - "render": "Hier zijn {socket:schuko} stekkers van het type
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "plugs-1": { - "question": "Hoeveel stekkers van type
Europese stekker met aardingspin (CEE7/4 type E)
heeft dit oplaadpunt?", - "render": "Hier zijn {socket:typee} stekkers van het type
Europese stekker met aardingspin (CEE7/4 type E)
" - }, - "plugs-10": { - "question": "Hoeveel stekkers van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft dit oplaadpunt?", - "render": "Hier zijn {socket:tesla_supercharger_ccs} stekkers van het type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "plugs-11": { - "question": "Hoeveel stekkers van type
Tesla Supercharger (destination)
heeft dit oplaadpunt?", - "render": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla Supercharger (destination)
" - }, - "plugs-12": { - "question": "Hoeveel stekkers van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft dit oplaadpunt?", - "render": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "plugs-13": { - "question": "Hoeveel stekkers van type
USB om GSMs en kleine electronica op te laden
heeft dit oplaadpunt?", - "render": "Hier zijn {socket:USB-A} stekkers van het type
USB om GSMs en kleine electronica op te laden
" - }, - "plugs-14": { - "question": "Hoeveel stekkers van type
Bosch Active Connect met 3 pinnen aan een kabel
heeft dit oplaadpunt?", - "render": "Hier zijn {socket:bosch_3pin} stekkers van het type
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "plugs-15": { - "question": "Hoeveel stekkers van type
Bosch Active Connect met 5 pinnen aan een kabel
heeft dit oplaadpunt?", - "render": "Hier zijn {socket:bosch_5pin} stekkers van het type
Bosch Active Connect met 5 pinnen aan een kabel
" - }, - "plugs-2": { - "question": "Hoeveel stekkers van type
Chademo
heeft dit oplaadpunt?", - "render": "Hier zijn {socket:chademo} stekkers van het type
Chademo
" - }, - "plugs-3": { - "question": "Hoeveel stekkers van type
Type 1 met kabel (J1772)
heeft dit oplaadpunt?", - "render": "Hier zijn {socket:type1_cable} stekkers van het type
Type 1 met kabel (J1772)
" - }, - "plugs-4": { - "question": "Hoeveel stekkers van type
Type 1 zonder kabel (J1772)
heeft dit oplaadpunt?", - "render": "Hier zijn {socket:type1} stekkers van het type
Type 1 zonder kabel (J1772)
" - }, - "plugs-5": { - "question": "Hoeveel stekkers van type
Type 1 CCS (ook gekend als Type 1 Combo)
heeft dit oplaadpunt?", - "render": "Hier zijn {socket:type1_combo} stekkers van het type
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "plugs-6": { - "question": "Hoeveel stekkers van type
Tesla Supercharger
heeft dit oplaadpunt?", - "render": "Hier zijn {socket:tesla_supercharger} stekkers van het type
Tesla Supercharger
" - }, - "plugs-7": { - "question": "Hoeveel stekkers van type
Type 2 (mennekes)
heeft dit oplaadpunt?", - "render": "Hier zijn {socket:type2} stekkers van het type
Type 2 (mennekes)
" - }, - "plugs-8": { - "question": "Hoeveel stekkers van type
Type 2 CCS (mennekes)
heeft dit oplaadpunt?", - "render": "Hier zijn {socket:type2_combo} stekkers van het type
Type 2 CCS (mennekes)
" - }, - "plugs-9": { - "question": "Hoeveel stekkers van type
Type 2 met kabel (J1772)
heeft dit oplaadpunt?", - "render": "Hier zijn {socket:type2_cable} stekkers van het type
Type 2 met kabel (J1772)
" - }, - "power-output-0": { - "mappings": { - "0": { - "then": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een vermogen van maximaal 3.6 kw" - } - }, - "question": "Welk vermogen levert een enkele stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
?", - "render": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een vermogen van maximaal {socket:schuko:output}" - }, - "power-output-1": { - "mappings": { - "0": { - "then": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal 3 kw" - }, - "1": { - "then": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal 22 kw" - } - }, - "question": "Welk vermogen levert een enkele stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
?", - "render": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal {socket:typee:output}" - }, - "power-output-10": { - "mappings": { - "0": { - "then": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een vermogen van maximaal 50 kw" - } - }, - "question": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
?", - "render": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" - }, - "power-output-11": { - "mappings": { - "0": { - "then": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 120 kw" - }, - "1": { - "then": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 150 kw" - }, - "2": { - "then": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 250 kw" - } - }, - "question": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger (destination)
?", - "render": "
Tesla Supercharger (destination)
levert een vermogen van maximaal {socket:tesla_destination:output}" - }, - "power-output-12": { - "mappings": { - "0": { - "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal 11 kw" - }, - "1": { - "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal 22 kw" - } - }, - "question": "Welk vermogen levert een enkele stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
?", - "render": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal {socket:tesla_destination:output}" - }, - "power-output-13": { - "mappings": { - "0": { - "then": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal 5w" - }, - "1": { - "then": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal 10w" - } - }, - "question": "Welk vermogen levert een enkele stekker van type
USB om GSMs en kleine electronica op te laden
?", - "render": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal {socket:USB-A:output}" - }, - "power-output-14": { - "question": "Welk vermogen levert een enkele stekker van type
Bosch Active Connect met 3 pinnen aan een kabel
?", - "render": "
Bosch Active Connect met 3 pinnen aan een kabel
levert een vermogen van maximaal {socket:bosch_3pin:output}" - }, - "power-output-15": { - "question": "Welk vermogen levert een enkele stekker van type
Bosch Active Connect met 5 pinnen aan een kabel
?", - "render": "
Bosch Active Connect met 5 pinnen aan een kabel
levert een vermogen van maximaal {socket:bosch_5pin:output}" - }, - "power-output-2": { - "mappings": { - "0": { - "then": "
Chademo
levert een vermogen van maximaal 50 kw" - } - }, - "question": "Welk vermogen levert een enkele stekker van type
Chademo
?", - "render": "
Chademo
levert een vermogen van maximaal {socket:chademo:output}" - }, - "power-output-3": { - "mappings": { - "0": { - "then": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal 3.7 kw" - }, - "1": { - "then": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal 7 kw" - } - }, - "question": "Welk vermogen levert een enkele stekker van type
Type 1 met kabel (J1772)
?", - "render": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal {socket:type1_cable:output}" - }, - "power-output-4": { - "mappings": { - "0": { - "then": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 3.7 kw" - }, - "1": { - "then": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 6.6 kw" - }, - "2": { - "then": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 7 kw" - }, - "3": { - "then": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 7.2 kw" - } - }, - "question": "Welk vermogen levert een enkele stekker van type
Type 1 zonder kabel (J1772)
?", - "render": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal {socket:type1:output}" - }, - "power-output-5": { - "mappings": { - "0": { - "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 50 kw" - }, - "1": { - "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 62.5 kw" - }, - "2": { - "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 150 kw" - }, - "3": { - "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 350 kw" - } - }, - "question": "Welk vermogen levert een enkele stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
?", - "render": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal {socket:type1_combo:output}" - }, - "power-output-6": { - "mappings": { - "0": { - "then": "
Tesla Supercharger
levert een vermogen van maximaal 120 kw" - }, - "1": { - "then": "
Tesla Supercharger
levert een vermogen van maximaal 150 kw" - }, - "2": { - "then": "
Tesla Supercharger
levert een vermogen van maximaal 250 kw" - } - }, - "question": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger
?", - "render": "
Tesla Supercharger
levert een vermogen van maximaal {socket:tesla_supercharger:output}" - }, - "power-output-7": { - "mappings": { - "0": { - "then": "
Type 2 (mennekes)
levert een vermogen van maximaal 11 kw" - }, - "1": { - "then": "
Type 2 (mennekes)
levert een vermogen van maximaal 22 kw" - } - }, - "question": "Welk vermogen levert een enkele stekker van type
Type 2 (mennekes)
?", - "render": "
Type 2 (mennekes)
levert een vermogen van maximaal {socket:type2:output}" - }, - "power-output-8": { - "mappings": { - "0": { - "then": "
Type 2 CCS (mennekes)
levert een vermogen van maximaal 50 kw" - } - }, - "question": "Welk vermogen levert een enkele stekker van type
Type 2 CCS (mennekes)
?", - "render": "
Type 2 CCS (mennekes)
levert een vermogen van maximaal {socket:type2_combo:output}" - }, - "power-output-9": { - "mappings": { - "0": { - "then": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal 11 kw" - }, - "1": { - "then": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal 22 kw" - } - }, - "question": "Welk vermogen levert een enkele stekker van type
Type 2 met kabel (J1772)
?", - "render": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal {socket:type2_cable:output}" - }, - "voltage-0": { - "mappings": { - "0": { - "then": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft een spanning van 230 volt" - } - }, - "question": "Welke spanning levert de stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
", - "render": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft een spanning van {socket:schuko:voltage} volt" - }, - "voltage-1": { - "mappings": { - "0": { - "then": "
Europese stekker met aardingspin (CEE7/4 type E)
heeft een spanning van 230 volt" - } - }, - "question": "Welke spanning levert de stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
", - "render": "
Europese stekker met aardingspin (CEE7/4 type E)
heeft een spanning van {socket:typee:voltage} volt" - }, - "voltage-10": { - "mappings": { - "0": { - "then": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van 500 volt" - }, - "1": { - "then": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van 920 volt" - } - }, - "question": "Welke spanning levert de stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
", - "render": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" - }, - "voltage-11": { - "mappings": { - "0": { - "then": "
Tesla Supercharger (destination)
heeft een spanning van 480 volt" - } - }, - "question": "Welke spanning levert de stekker van type
Tesla Supercharger (destination)
", - "render": "
Tesla Supercharger (destination)
heeft een spanning van {socket:tesla_destination:voltage} volt" - }, - "voltage-12": { - "mappings": { - "0": { - "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van 230 volt" - }, - "1": { - "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van 400 volt" - } - }, - "question": "Welke spanning levert de stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
", - "render": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van {socket:tesla_destination:voltage} volt" - }, - "voltage-13": { - "mappings": { - "0": { - "then": "
USB om GSMs en kleine electronica op te laden
heeft een spanning van 5 volt" - } - }, - "question": "Welke spanning levert de stekker van type
USB om GSMs en kleine electronica op te laden
", - "render": "
USB om GSMs en kleine electronica op te laden
heeft een spanning van {socket:USB-A:voltage} volt" - }, - "voltage-14": { - "question": "Welke spanning levert de stekker van type
Bosch Active Connect met 3 pinnen aan een kabel
", - "render": "
Bosch Active Connect met 3 pinnen aan een kabel
heeft een spanning van {socket:bosch_3pin:voltage} volt" - }, - "voltage-15": { - "question": "Welke spanning levert de stekker van type
Bosch Active Connect met 5 pinnen aan een kabel
", - "render": "
Bosch Active Connect met 5 pinnen aan een kabel
heeft een spanning van {socket:bosch_5pin:voltage} volt" - }, - "voltage-2": { - "mappings": { - "0": { - "then": "
Chademo
heeft een spanning van 500 volt" - } - }, - "question": "Welke spanning levert de stekker van type
Chademo
", - "render": "
Chademo
heeft een spanning van {socket:chademo:voltage} volt" - }, - "voltage-3": { - "mappings": { - "0": { - "then": "
Type 1 met kabel (J1772)
heeft een spanning van 200 volt" - }, - "1": { - "then": "
Type 1 met kabel (J1772)
heeft een spanning van 240 volt" - } - }, - "question": "Welke spanning levert de stekker van type
Type 1 met kabel (J1772)
", - "render": "
Type 1 met kabel (J1772)
heeft een spanning van {socket:type1_cable:voltage} volt" - }, - "voltage-4": { - "mappings": { - "0": { - "then": "
Type 1 zonder kabel (J1772)
heeft een spanning van 200 volt" - }, - "1": { - "then": "
Type 1 zonder kabel (J1772)
heeft een spanning van 240 volt" - } - }, - "question": "Welke spanning levert de stekker van type
Type 1 zonder kabel (J1772)
", - "render": "
Type 1 zonder kabel (J1772)
heeft een spanning van {socket:type1:voltage} volt" - }, - "voltage-5": { - "mappings": { - "0": { - "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van 400 volt" - }, - "1": { - "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van 1000 volt" - } - }, - "question": "Welke spanning levert de stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
", - "render": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van {socket:type1_combo:voltage} volt" - }, - "voltage-6": { - "mappings": { - "0": { - "then": "
Tesla Supercharger
heeft een spanning van 480 volt" - } - }, - "question": "Welke spanning levert de stekker van type
Tesla Supercharger
", - "render": "
Tesla Supercharger
heeft een spanning van {socket:tesla_supercharger:voltage} volt" - }, - "voltage-7": { - "mappings": { - "0": { - "then": "
Type 2 (mennekes)
heeft een spanning van 230 volt" - }, - "1": { - "then": "
Type 2 (mennekes)
heeft een spanning van 400 volt" - } - }, - "question": "Welke spanning levert de stekker van type
Type 2 (mennekes)
", - "render": "
Type 2 (mennekes)
heeft een spanning van {socket:type2:voltage} volt" - }, - "voltage-8": { - "mappings": { - "0": { - "then": "
Type 2 CCS (mennekes)
heeft een spanning van 500 volt" - }, - "1": { - "then": "
Type 2 CCS (mennekes)
heeft een spanning van 920 volt" - } - }, - "question": "Welke spanning levert de stekker van type
Type 2 CCS (mennekes)
", - "render": "
Type 2 CCS (mennekes)
heeft een spanning van {socket:type2_combo:voltage} volt" - }, - "voltage-9": { - "mappings": { - "0": { - "then": "
Type 2 met kabel (J1772)
heeft een spanning van 230 volt" - }, - "1": { - "then": "
Type 2 met kabel (J1772)
heeft een spanning van 400 volt" - } - }, - "question": "Welke spanning levert de stekker van type
Type 2 met kabel (J1772)
", - "render": "
Type 2 met kabel (J1772)
heeft een spanning van {socket:type2_cable:voltage} volt" } }, "units": { diff --git a/langs/themes/en.json b/langs/themes/en.json index 9f96d084e..824ca51c8 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -1237,6 +1237,20 @@ "shortDescription": "An editable map with basic shop information", "title": "Open Shop Map" }, + "sidewalks": { + "description": "Experimental theme", + "layers": { + "0": { + "description": "Layer showing sidewalks of highways", + "name": "Sidewalks", + "title": { + "render": "Street {name}" + } + } + }, + "shortDescription": "Sidewalk mapping", + "title": "Sidewalks" + }, "sport_pitches": { "description": "A sport pitch is an area where sports are played", "shortDescription": "A map showing sport pitches", From 40c4ae769d0b52a4aa679761ad4a26194c2bee37 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 22 Oct 2021 13:35:11 +0200 Subject: [PATCH 19/95] Rewrite left-right tags when updating them on click --- Logic/Actors/SelectedElementTagsUpdater.ts | 22 +++- Logic/SimpleMetaTagger.ts | 146 +++++++++++---------- 2 files changed, 95 insertions(+), 73 deletions(-) diff --git a/Logic/Actors/SelectedElementTagsUpdater.ts b/Logic/Actors/SelectedElementTagsUpdater.ts index 765c9ee19..1a47b6af5 100644 --- a/Logic/Actors/SelectedElementTagsUpdater.ts +++ b/Logic/Actors/SelectedElementTagsUpdater.ts @@ -6,6 +6,8 @@ import {ElementStorage} from "../ElementStorage"; import {Changes} from "../Osm/Changes"; import {OsmObject} from "../Osm/OsmObject"; import {OsmConnection} from "../Osm/OsmConnection"; +import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; +import SimpleMetaTagger from "../SimpleMetaTagger"; export default class SelectedElementTagsUpdater { @@ -14,13 +16,14 @@ export default class SelectedElementTagsUpdater { "changeset", "user", "uid", - "id"] ) + "id"]) constructor(state: { selectedElement: UIEventSource, allElements: ElementStorage, changes: Changes, - osmConnection: OsmConnection + osmConnection: OsmConnection, + layoutToUse: LayoutConfig }) { @@ -37,7 +40,8 @@ export default class SelectedElementTagsUpdater { selectedElement: UIEventSource, allElements: ElementStorage, changes: Changes, - osmConnection: OsmConnection + osmConnection: OsmConnection, + layoutToUse: LayoutConfig }) { @@ -70,11 +74,18 @@ export default class SelectedElementTagsUpdater { selectedElement: UIEventSource, allElements: ElementStorage, changes: Changes, - osmConnection: OsmConnection + osmConnection: OsmConnection, + layoutToUse: LayoutConfig }, latestTags: any, id: string ) { try { + const leftRightSensitive = state.layoutToUse.layers.some(layer => layer.lineRendering.some(lr => lr.leftRightSensitive)) + + if (leftRightSensitive) { + SimpleMetaTagger.removeBothTagging(latestTags) + } + const pendingChanges = state.changes.pendingChanges.data .filter(change => change.type + "/" + change.id === id) .filter(change => change.tags !== undefined); @@ -92,6 +103,7 @@ export default class SelectedElementTagsUpdater { } } + // With the changes applied, we merge them onto the upstream object let somethingChanged = false; const currentTagsSource = state.allElements.getEventSourceById(id); @@ -115,7 +127,7 @@ export default class SelectedElementTagsUpdater { if (currentKey.startsWith("_")) { continue } - if(this.metatags.has(currentKey)){ + if (this.metatags.has(currentKey)) { continue } if (currentKey in latestTags) { diff --git a/Logic/SimpleMetaTagger.ts b/Logic/SimpleMetaTagger.ts index f5c1b77e2..cbe30c726 100644 --- a/Logic/SimpleMetaTagger.ts +++ b/Logic/SimpleMetaTagger.ts @@ -78,6 +78,83 @@ export default class SimpleMetaTagger { return true; } ) + + /** + * Edits the given object to rewrite 'both'-tagging into a 'left-right' tagging scheme. + * These changes are performed in-place. + * + * Returns 'true' is at least one change has been made + * @param tags + */ + public static removeBothTagging(tags: any): boolean{ + let somethingChanged = false + /** + * Sets the key onto the properties (but doesn't overwrite if already existing) + */ + function set(k, value) { + if (tags[k] === undefined || tags[k] === "") { + tags[k] = value + somethingChanged = true + } + } + + if (tags["sidewalk"]) { + + const v = tags["sidewalk"] + switch (v) { + case "none": + case "no": + set("sidewalk:left", "no"); + set("sidewalk:right", "no"); + break + case "both": + set("sidewalk:left", "yes"); + set("sidewalk:right", "yes"); + break; + case "left": + set("sidewalk:left", "yes"); + set("sidewalk:right", "no"); + break; + case "right": + set("sidewalk:left", "no"); + set("sidewalk:right", "yes"); + break; + default: + set("sidewalk:left", v); + set("sidewalk:right", v); + break; + } + delete tags["sidewalk"] + somethingChanged = true + } + + + const regex = /\([^:]*\):both:\(.*\)/ + for (const key in tags) { + const v = tags[key] + if (key.endsWith(":both")) { + const strippedKey = key.substring(0, key.length - ":both".length) + set(strippedKey + ":left", v) + set(strippedKey + ":right", v) + delete tags[key] + continue + } + + const match = key.match(regex) + if (match !== null) { + const strippedKey = match[1] + const property = match[1] + set(strippedKey + ":left:" + property, v) + set(strippedKey + ":right:" + property, v) + console.log("Left-right rewritten " + key) + delete tags[key] + } + } + + + return somethingChanged + } + private static noBothButLeftRight = new SimpleMetaTagger( { keys: ["sidewalk:left", "sidewalk:right", "generic_key:left:property", "generic_key:right:property"], @@ -91,74 +168,7 @@ export default class SimpleMetaTagger { return; } - const tgs = feature.properties; - let somethingChanged = false - - /** - * Sets the key onto the properties (but doesn't overwrite if already existing) - */ - function set(k, value) { - if (tgs[k] === undefined || tgs[k] === "") { - tgs[k] = value - somethingChanged = true - } - } - - if (tgs["sidewalk"]) { - - const v = tgs["sidewalk"] - switch (v) { - case "none": - case "no": - set("sidewalk:left", "no"); - set("sidewalk:right", "no"); - break - case "both": - set("sidewalk:left", "yes"); - set("sidewalk:right", "yes"); - break; - case "left": - set("sidewalk:left", "yes"); - set("sidewalk:right", "no"); - break; - case "right": - set("sidewalk:left", "no"); - set("sidewalk:right", "yes"); - break; - default: - set("sidewalk:left", v); - set("sidewalk:right", v); - break; - } - delete tgs["sidewalk"] - somethingChanged = true - } - - - const regex = /\([^:]*\):both:\(.*\)/ - for (const key in tgs) { - const v = tgs[key] - if (key.endsWith(":both")) { - const strippedKey = key.substring(0, key.length - ":both".length) - set(strippedKey + ":left", v) - set(strippedKey + ":right", v) - delete tgs[key] - continue - } - - const match = key.match(regex) - if (match !== null) { - const strippedKey = match[1] - const property = match[1] - set(strippedKey + ":left:" + property, v) - set(strippedKey + ":right:" + property, v) - console.log("Left-right rewritten " + key) - delete tgs[key] - } - } - - - return somethingChanged + return SimpleMetaTagger.removeBothTagging(feature.properties) }) ) private static surfaceArea = new SimpleMetaTagger( From 0dc7187bab2dfc4d79817b7aa3a758d81cd45cc3 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 22 Oct 2021 14:01:40 +0200 Subject: [PATCH 20/95] Changes do apply left-right splitting before uploading too --- Logic/Actors/SelectedElementTagsUpdater.ts | 2 +- Logic/Osm/Changes.ts | 10 +++++++++- Logic/State/ElementsState.ts | 13 ++++++++----- Models/ThemeConfig/LayerConfig.ts | 4 ++++ Models/ThemeConfig/LayoutConfig.ts | 4 ++++ UI/Popup/TagRenderingQuestion.ts | 3 +-- 6 files changed, 27 insertions(+), 9 deletions(-) diff --git a/Logic/Actors/SelectedElementTagsUpdater.ts b/Logic/Actors/SelectedElementTagsUpdater.ts index 1a47b6af5..2aa2c942e 100644 --- a/Logic/Actors/SelectedElementTagsUpdater.ts +++ b/Logic/Actors/SelectedElementTagsUpdater.ts @@ -80,7 +80,7 @@ export default class SelectedElementTagsUpdater { ) { try { - const leftRightSensitive = state.layoutToUse.layers.some(layer => layer.lineRendering.some(lr => lr.leftRightSensitive)) + const leftRightSensitive = state.layoutToUse.isLeftRightSensitive() if (leftRightSensitive) { SimpleMetaTagger.removeBothTagging(latestTags) diff --git a/Logic/Osm/Changes.ts b/Logic/Osm/Changes.ts index 7faa23494..f17697770 100644 --- a/Logic/Osm/Changes.ts +++ b/Logic/Osm/Changes.ts @@ -6,6 +6,7 @@ import OsmChangeAction from "./Actions/OsmChangeAction"; import {ChangeDescription} from "./Actions/ChangeDescription"; import {Utils} from "../../Utils"; import {LocalStorageSource} from "../Web/LocalStorageSource"; +import SimpleMetaTagger from "../SimpleMetaTagger"; /** * Handles all changes made to OSM. @@ -26,8 +27,10 @@ export class Changes { private readonly isUploading = new UIEventSource(false); private readonly previouslyCreated: OsmObject[] = [] + private readonly _leftRightSensitive: boolean; - constructor() { + constructor(leftRightSensitive : boolean = false) { + this._leftRightSensitive = leftRightSensitive; // We keep track of all changes just as well this.allChanges.setData([...this.pendingChanges.data]) // If a pending change contains a negative ID, we save that @@ -121,6 +124,11 @@ export class Changes { const self = this; const neededIds = Changes.GetNeededIds(pending) const osmObjects = await Promise.all(neededIds.map(id => OsmObject.DownloadObjectAsync(id))); + + if(this._leftRightSensitive){ + osmObjects.forEach(obj => SimpleMetaTagger.removeBothTagging(obj.tags)) + } + console.log("Got the fresh objects!", osmObjects, "pending: ", pending) const changes: { newObjects: OsmObject[], diff --git a/Logic/State/ElementsState.ts b/Logic/State/ElementsState.ts index 241badaa2..a4674430b 100644 --- a/Logic/State/ElementsState.ts +++ b/Logic/State/ElementsState.ts @@ -15,7 +15,7 @@ import TitleHandler from "../Actors/TitleHandler"; /** * The part of the state keeping track of where the elements, loading them, configuring the feature pipeline etc */ -export default class ElementsState extends FeatureSwitchState{ +export default class ElementsState extends FeatureSwitchState { /** The mapping from id -> UIEventSource @@ -24,7 +24,7 @@ export default class ElementsState extends FeatureSwitchState{ /** THe change handler */ - public changes: Changes = new Changes(); + public changes: Changes; /** The latest element that was selected @@ -34,7 +34,7 @@ export default class ElementsState extends FeatureSwitchState{ "Selected element" ); - + /** * The map location: currently centered lat, lon and zoom */ @@ -48,6 +48,9 @@ export default class ElementsState extends FeatureSwitchState{ constructor(layoutToUse: LayoutConfig) { super(layoutToUse); + + this.changes = new Changes(layoutToUse.isLeftRightSensitive()) + { // -- Location control initialization const zoom = UIEventSource.asFloat( @@ -84,10 +87,10 @@ export default class ElementsState extends FeatureSwitchState{ lon.setData(latlonz.lon); }); } - + new ChangeToElementsActor(this.changes, this.allElements) new PendingChangesUploader(this.changes, this.selectedElement); new TitleHandler(this); - + } } \ No newline at end of file diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts index 566c55dfa..f9888f9b0 100644 --- a/Models/ThemeConfig/LayerConfig.ts +++ b/Models/ThemeConfig/LayerConfig.ts @@ -297,4 +297,8 @@ export default class LayerConfig extends WithContextLoader{ return allIcons; } + + public isLeftRightSensitive() : boolean{ + return this.lineRendering.some(lr => lr.leftRightSensitive) + } } \ No newline at end of file diff --git a/Models/ThemeConfig/LayoutConfig.ts b/Models/ThemeConfig/LayoutConfig.ts index 56d169fc5..fc125b154 100644 --- a/Models/ThemeConfig/LayoutConfig.ts +++ b/Models/ThemeConfig/LayoutConfig.ts @@ -279,5 +279,9 @@ export default class LayoutConfig { }) return new LayoutConfig(JSON.parse(originalJson), false, "Layout rewriting") } + + public isLeftRightSensitive(){ + return this.layers.some(l => l.isLeftRightSensitive()) + } } \ No newline at end of file diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts index 556635c57..00f572b3d 100644 --- a/UI/Popup/TagRenderingQuestion.ts +++ b/UI/Popup/TagRenderingQuestion.ts @@ -9,7 +9,6 @@ import CheckBoxes from "../Input/Checkboxes"; import InputElementMap from "../Input/InputElementMap"; import {SaveButton} from "./SaveButton"; import State from "../../State"; -import {Changes} from "../../Logic/Osm/Changes"; import {VariableUiElement} from "../Base/VariableUIElement"; import Translations from "../i18n/Translations"; import {FixedUiElement} from "../Base/FixedUiElement"; @@ -84,7 +83,7 @@ export default class TagRenderingQuestion extends Combine { const save = () => { const selection = inputElement.GetValue().data; if (selection) { - (State.state?.changes ?? new Changes()) + (State.state?.changes) .applyAction(new ChangeTagAction( tags.data.id, selection, tags.data, { theme: State.state?.layoutToUse?.id ?? "unkown", From 02a1d9696f69b6a1c436fad2d68d08c97cc15160 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 22 Oct 2021 14:24:30 +0200 Subject: [PATCH 21/95] Linting themes --- .../charging_station/charging_station.json | 60 +----------- .../layers/drinking_water/drinking_water.json | 3 +- assets/layers/food/food.json | 6 +- assets/layers/playground/playground.json | 3 +- assets/layers/shops/shops.json | 3 +- assets/layers/sport_pitch/sport_pitch.json | 6 +- assets/themes/campersite/campersite.json | 55 ++++++++++- assets/themes/hailhydrant/hailhydrant.json | 85 ++++++++++++++++- .../openwindpowermap/openwindpowermap.json | 17 ++++ assets/themes/postboxes/postboxes.json | 55 ++++++++++- assets/themes/sidewalks/sidewalks.json | 46 +-------- assets/themes/uk_addresses/uk_addresses.json | 95 ++++++++++++++++++- 12 files changed, 311 insertions(+), 123 deletions(-) diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index 054d6a7b7..28772fd1d 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -972,64 +972,8 @@ "mapRendering": [ { "location": [ - "point", - "centroid" - ], - "icon": { - "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", - "mappings": [ - { - "if": "bicycle=yes", - "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" - }, - { - "if": { - "or": [ - "car=yes", - "motorcar=yes" - ] - }, - "then": "pin:#fff;./assets/themes/charging_stations/car.svg" - } - ] - }, - "iconBadges": [ - { - "if": { - "or": [ - "disused:amenity=charging_station", - "operational_status=broken" - ] - }, - "then": "cross:#c22;" - }, - { - "if": { - "or": [ - "proposed:amenity=charging_station", - "planned:amenity=charging_station" - ] - }, - "then": "./assets/layers/charging_station/under_construction.svg" - }, - { - "if": { - "and": [ - "bicycle=yes", - { - "or": [ - "motorcar=yes", - "car=yes" - ] - } - ] - }, - "then": "circle:#fff;./assets/themes/charging_stations/car.svg" - } - ], - "iconSize": { - "render": "50,50,bottom" - } + "point" + ] } ] } \ No newline at end of file diff --git a/assets/layers/drinking_water/drinking_water.json b/assets/layers/drinking_water/drinking_water.json index 617650f6a..439579473 100644 --- a/assets/layers/drinking_water/drinking_water.json +++ b/assets/layers/drinking_water/drinking_water.json @@ -33,8 +33,7 @@ "operational_status=closed" ] }, - "then": "close:#c33", - "badge": true + "then": "close:#c33" } ], "iconSize": "40,40,bottom", diff --git a/assets/layers/food/food.json b/assets/layers/food/food.json index 0e6b5db97..b0df677b3 100644 --- a/assets/layers/food/food.json +++ b/assets/layers/food/food.json @@ -35,8 +35,7 @@ "iconOverlays": [ { "if": "opening_hours~*", - "then": "isOpen", - "badge": true + "then": "isOpen" }, { "if": { @@ -47,8 +46,7 @@ }, "then": { "render": "circle:white;./assets/themes/fritures/Vegetarian-mark.svg" - }, - "badge": true + } } ], "label": { diff --git a/assets/layers/playground/playground.json b/assets/layers/playground/playground.json index a0156e78e..cf44f3723 100644 --- a/assets/layers/playground/playground.json +++ b/assets/layers/playground/playground.json @@ -482,8 +482,7 @@ "opening_hours~*" ] }, - "then": "isOpen", - "badge": true + "then": "isOpen" } ], "width": { diff --git a/assets/layers/shops/shops.json b/assets/layers/shops/shops.json index 54d158fbb..a5458a680 100644 --- a/assets/layers/shops/shops.json +++ b/assets/layers/shops/shops.json @@ -280,8 +280,7 @@ "iconOverlays": [ { "if": "opening_hours~*", - "then": "isOpen", - "badge": true + "then": "isOpen" } ], "width": { diff --git a/assets/layers/sport_pitch/sport_pitch.json b/assets/layers/sport_pitch/sport_pitch.json index 86ddcaadc..f34979561 100644 --- a/assets/layers/sport_pitch/sport_pitch.json +++ b/assets/layers/sport_pitch/sport_pitch.json @@ -423,8 +423,7 @@ "opening_hours~*" ] }, - "then": "isOpen", - "badge": true + "then": "isOpen" }, { "if": { @@ -434,8 +433,7 @@ "access=no" ] }, - "then": "circle:white;./assets/layers/sport_pitch/lock.svg", - "badge": true + "then": "circle:white;./assets/layers/sport_pitch/lock.svg" } ], "width": { diff --git a/assets/themes/campersite/campersite.json b/assets/themes/campersite/campersite.json index 948e68cfd..7a572044f 100644 --- a/assets/themes/campersite/campersite.json +++ b/assets/themes/campersite/campersite.json @@ -676,7 +676,39 @@ } } ], - "wayHandling": 2 + "wayHandling": 2, + "mapRendering": [ + { + "icon": { + "render": "circle:white;./assets/themes/campersite/caravan.svg", + "mappings": [ + { + "if": { + "and": [ + "fee=no" + ] + }, + "then": "circle:white;./assets/themes/campersite/caravan_green.svg" + } + ] + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point", + "centroid" + ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "8" + } + } + ] }, { "id": "dumpstations", @@ -1075,6 +1107,27 @@ "de": "Fügen Sie eine neue sanitäre Entsorgungsstation hinzu. Hier können Camper Abwasser oder chemischen Toilettenabfälle entsorgen. Oft gibt es auch Trinkwasser und Strom." } } + ], + "mapRendering": [ + { + "icon": { + "render": "circle:white;./assets/themes/campersite/sanitary_dump_station.svg" + }, + "iconSize": { + "render": "32,32,center" + }, + "location": [ + "point" + ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "8" + } + } ] } ], diff --git a/assets/themes/hailhydrant/hailhydrant.json b/assets/themes/hailhydrant/hailhydrant.json index b53683b3b..09f793af5 100644 --- a/assets/themes/hailhydrant/hailhydrant.json +++ b/assets/themes/hailhydrant/hailhydrant.json @@ -318,7 +318,29 @@ } } ], - "wayHandling": 2 + "wayHandling": 2, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/hailhydrant/hydrant.svg" + }, + "iconSize": { + "render": "20,20,center" + }, + "location": [ + "point", + "centroid" + ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "8" + } + } + ] }, { "id": "extinguisher", @@ -435,7 +457,20 @@ } } ], - "wayHandling": 1 + "wayHandling": 1, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/hailhydrant/Twemoji12_1f9ef.svg" + }, + "iconSize": { + "render": "20,20,center" + }, + "location": [ + "point" + ] + } + ] }, { "id": "fire_stations", @@ -659,6 +694,28 @@ "fr": "Une caserne de pompiers est un lieu où les pompiers et leur équipements sont situés en dehors des missions." } } + ], + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/hailhydrant/Twemoji12_1f692.svg" + }, + "iconSize": { + "render": "35,35,center" + }, + "location": [ + "point", + "centroid" + ] + }, + { + "color": { + "render": "#c22" + }, + "width": { + "render": "1" + } + } ] }, { @@ -860,7 +917,29 @@ } } ], - "wayHandling": 2 + "wayHandling": 2, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/hailhydrant/Twemoji_1f691.svg" + }, + "iconSize": { + "render": "35,35,center" + }, + "location": [ + "point", + "centroid" + ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "1" + } + } + ] } ], "defaultBackgroundId": "HDM_HOT" diff --git a/assets/themes/openwindpowermap/openwindpowermap.json b/assets/themes/openwindpowermap/openwindpowermap.json index c7e910d48..05490dc11 100644 --- a/assets/themes/openwindpowermap/openwindpowermap.json +++ b/assets/themes/openwindpowermap/openwindpowermap.json @@ -229,6 +229,23 @@ } ] } + ], + "mapRendering": [ + { + "icon": "./assets/themes/openwindpowermap/wind_turbine.svg", + "label": { + "mappings": [ + { + "if": "generator:output:electricity~^[0-9]+.*[W]$", + "then": "
{generator:output:electricity}
" + } + ] + }, + "iconSize": "40, 40, bottom", + "location": [ + "point" + ] + } ] } ], diff --git a/assets/themes/postboxes/postboxes.json b/assets/themes/postboxes/postboxes.json index 7cc368b83..cce2b5ccb 100644 --- a/assets/themes/postboxes/postboxes.json +++ b/assets/themes/postboxes/postboxes.json @@ -80,7 +80,29 @@ "razed:amenity=post_box" ] } - } + }, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/postboxes/postbox.svg" + }, + "iconSize": { + "render": "40,40,bottom" + }, + "location": [ + "point", + "centroid" + ] + }, + { + "color": { + "render": "#DADADA" + }, + "width": { + "render": "1" + } + } + ] }, { "id": "postoffices", @@ -133,8 +155,7 @@ "iconOverlays": [ { "if": "opening_hours~*", - "then": "isOpen", - "badge": true + "then": "isOpen" } ], "width": { @@ -169,6 +190,34 @@ } ] } + ], + "mapRendering": [ + { + "icon": { + "render": "square:white;./assets/themes/postboxes/post_office.svg" + }, + "iconBadges": [ + { + "if": "opening_hours~*", + "then": "isOpen" + } + ], + "iconSize": { + "render": "40,40,bottom" + }, + "location": [ + "point", + "centroid" + ] + }, + { + "color": { + "render": "#DADADA" + }, + "width": { + "render": "1" + } + } ] } ] diff --git a/assets/themes/sidewalks/sidewalks.json b/assets/themes/sidewalks/sidewalks.json index 60a245ccc..940caba80 100644 --- a/assets/themes/sidewalks/sidewalks.json +++ b/assets/themes/sidewalks/sidewalks.json @@ -41,49 +41,11 @@ "tagRenderings": [], "mapRendering": [ { - "color": "#ffffff55", - "width": 8 + "location": [ + "point" + ] }, - { - "color": { - "render": "#888" - }, - "width": { - "render": 6, - "mappings": [ - { - "if": { - "or": [ - "sidewalk:left=", - "sidewalk:left=no", - "sidewalk:left=separate" - ] - }, - "then": 0 - } - ] - }, - "offset": -6 - }, - { - "color": "#888", - "width": { - "render": 6, - "mappings": [ - { - "if": { - "or": [ - "sidewalk:right=", - "sidewalk:right=no", - "sidewalk:right=separate" - ] - }, - "then": 0 - } - ] - }, - "offset": 6 - } + {} ], "allowSplit": true } diff --git a/assets/themes/uk_addresses/uk_addresses.json b/assets/themes/uk_addresses/uk_addresses.json index ad8968845..537562380 100644 --- a/assets/themes/uk_addresses/uk_addresses.json +++ b/assets/themes/uk_addresses/uk_addresses.json @@ -112,6 +112,29 @@ } ] } + ], + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/uk_addresses/housenumber_unknown.svg", + "mappings": [ + { + "if": "_embedding_object:id~*", + "then": "./assets/themes/uk_addresses/housenumber_unknown_small.svg" + }, + { + "if": "_imported=yes", + "then": "./assets/themes/uk_addresses/housenumber_unknown_small.svg" + } + ] + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } ] }, { @@ -258,7 +281,60 @@ "then": "#ff0" } ] - } + }, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/uk_addresses/housenumber_ok.svg", + "mappings": [ + { + "if": { + "or": [ + { + "and": [ + "addr:housenumber=", + "nohousenumber!=yes" + ] + }, + "addr:street=" + ] + }, + "then": "./assets/themes/uk_addresses/housenumber_unknown.svg" + } + ] + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + }, + { + "color": { + "render": "#00f", + "mappings": [ + { + "if": { + "or": [ + { + "and": [ + "addr:housenumber=", + "nohousenumber!=yes" + ] + }, + "addr:street=" + ] + }, + "then": "#ff0" + } + ] + }, + "width": { + "render": "8" + } + } + ] }, { "id": "named_streets", @@ -276,7 +352,22 @@ }, "width": { "render": "0" - } + }, + "mapRendering": [ + { + "location": [ + "point" + ] + }, + { + "color": { + "render": "#ccc" + }, + "width": { + "render": "0" + } + } + ] } ] } \ No newline at end of file From ff0ee35ec11ec57c62292638d42e67815227cb32 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 22 Oct 2021 18:53:07 +0200 Subject: [PATCH 22/95] First usable sidewalks theme --- Logic/FeatureSource/FeaturePipeline.ts | 2 +- .../RenderingMultiPlexerFeatureSource.ts | 107 +++++++++++++++ .../WayHandlingApplyingFeatureSource.ts | 65 --------- Logic/State/ElementsState.ts | 2 +- Logic/UIEventSource.ts | 14 ++ Models/ThemeConfig/Json/LayerConfigJson.ts | 7 +- .../Json/PointRenderingConfigJson.ts | 2 +- .../Json/TagRenderingConfigJson.ts | 6 + Models/ThemeConfig/LayerConfig.ts | 128 +++++++++++++++--- Models/ThemeConfig/PointRenderingConfig.ts | 2 +- Models/ThemeConfig/TagRenderingConfig.ts | 5 +- Models/ThemeConfig/WithContextLoader.ts | 60 ++++---- UI/Input/ValidatedTextField.ts | 3 +- UI/Popup/FeatureInfoBox.ts | 24 ++-- UI/ShowDataLayer/ShowDataLayer.ts | 17 ++- UI/SpecialVisualizations.ts | 119 ++++++++++++---- .../left_right_style/left_right_style.json | 41 ++++++ assets/themes/sidewalks/sidewalks.json | 105 +++++++++++++- scripts/lint.ts | 2 +- 19 files changed, 537 insertions(+), 174 deletions(-) create mode 100644 Logic/FeatureSource/Sources/RenderingMultiPlexerFeatureSource.ts delete mode 100644 Logic/FeatureSource/Sources/WayHandlingApplyingFeatureSource.ts create mode 100644 assets/layers/left_right_style/left_right_style.json diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index 9aa8937c3..1d2636d90 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -12,7 +12,7 @@ import OverpassFeatureSource from "../Actors/OverpassFeatureSource"; import {Changes} from "../Osm/Changes"; import GeoJsonSource from "./Sources/GeoJsonSource"; import Loc from "../../Models/Loc"; -import WayHandlingApplyingFeatureSource from "./Sources/WayHandlingApplyingFeatureSource"; +import WayHandlingApplyingFeatureSource from "./Sources/RenderingMultiPlexerFeatureSource"; import RegisteringAllFromFeatureSourceActor from "./Actors/RegisteringAllFromFeatureSourceActor"; import TiledFromLocalStorageSource from "./TiledFeatureSource/TiledFromLocalStorageSource"; import SaveTileToLocalStorageActor from "./Actors/SaveTileToLocalStorageActor"; diff --git a/Logic/FeatureSource/Sources/RenderingMultiPlexerFeatureSource.ts b/Logic/FeatureSource/Sources/RenderingMultiPlexerFeatureSource.ts new file mode 100644 index 000000000..5d2161a4e --- /dev/null +++ b/Logic/FeatureSource/Sources/RenderingMultiPlexerFeatureSource.ts @@ -0,0 +1,107 @@ +/** + * This feature source helps the ShowDataLayer class: it introduces the necessary extra features and indiciates with what renderConfig it should be rendered. + */ +import {UIEventSource} from "../../UIEventSource"; +import {GeoOperations} from "../../GeoOperations"; +import FeatureSource from "../FeatureSource"; +import PointRenderingConfig from "../../../Models/ThemeConfig/PointRenderingConfig"; +import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"; + + +export default class RenderingMultiPlexerFeatureSource { + public readonly features: UIEventSource<(any & { pointRenderingIndex: number | undefined, lineRenderingIndex: number | undefined })[]>; + + constructor(upstream: FeatureSource, layer: LayerConfig) { + this.features = upstream.features.map( + features => { + if (features === undefined) { + return; + } + + const pointRenderObjects: { rendering: PointRenderingConfig, index: number }[] = layer.mapRendering.map((r, i) => ({ + rendering: r, + index: i + })) + const pointRenderings = pointRenderObjects.filter(r => r.rendering.location.has("point")) + const centroidRenderings = pointRenderObjects.filter(r => r.rendering.location.has("centroid")) + const startRenderings = pointRenderObjects.filter(r => r.rendering.location.has("start")) + const endRenderings = pointRenderObjects.filter(r => r.rendering.location.has("end")) + + const lineRenderObjects = layer.lineRendering + + const withIndex: (any & { pointRenderingIndex: number | undefined, lineRenderingIndex: number | undefined })[] = []; + + + function addAsPoint(feat, rendering, coordinate) { + const patched = { + ...feat, + pointRenderingIndex: rendering.index + } + patched.geometry = { + type: "Point", + coordinates: coordinate + } + withIndex.push(patched) + } + + for (const f of features) { + const feat = f.feature; + + + if (feat.geometry.type === "Point") { + + for (const rendering of pointRenderings) { + withIndex.push({ + ...feat, + pointRenderingIndex: rendering.index + }) + } + } else { + // This is a a line + for (const rendering of centroidRenderings) { + addAsPoint(feat, rendering, GeoOperations.centerpointCoordinates(feat)) + } + + if (feat.geometry.type === "LineString") { + const coordinates = feat.geometry.coordinates + for (const rendering of startRenderings) { + addAsPoint(feat, rendering, coordinates[0]) + } + for (const rendering of endRenderings) { + const coordinate = coordinates[coordinates.length - 1] + addAsPoint(feat, rendering, coordinate) + } + } + + if (feat.geometry.type === "MultiLineString") { + const lineList = feat.geometry.coordinates + for (const coordinates of lineList) { + + for (const rendering of startRenderings) { + const coordinate = coordinates[0] + addAsPoint(feat, rendering, coordinate) + } + for (const rendering of endRenderings) { + const coordinate = coordinates[coordinates.length - 1] + addAsPoint(feat, rendering, coordinate) + } + } + } + + + for (let i = 0; i < lineRenderObjects.length; i++) { + withIndex.push({ + ...feat, + lineRenderingIndex: i + }) + } + + } + } + return withIndex; + } + ); + + } + +} \ No newline at end of file diff --git a/Logic/FeatureSource/Sources/WayHandlingApplyingFeatureSource.ts b/Logic/FeatureSource/Sources/WayHandlingApplyingFeatureSource.ts deleted file mode 100644 index f996ad9a0..000000000 --- a/Logic/FeatureSource/Sources/WayHandlingApplyingFeatureSource.ts +++ /dev/null @@ -1,65 +0,0 @@ -/** - * This feature source helps the ShowDataLayer class: it introduces the necessary extra features and indiciates with what renderConfig it should be rendered. - */ -import {UIEventSource} from "../../UIEventSource"; -import {GeoOperations} from "../../GeoOperations"; -import FeatureSource from "../FeatureSource"; -import PointRenderingConfig from "../../../Models/ThemeConfig/PointRenderingConfig"; -import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"; - - -export default class RenderingMultiPlexerFeatureSource { - public readonly features: UIEventSource<(any & {pointRenderingIndex: number | undefined, lineRenderingIndex: number | undefined})[]>; - - constructor(upstream: FeatureSource, layer: LayerConfig) { - this.features = upstream.features.map( - features => { - if (features === undefined) { - return; - } - - const pointRenderObjects: { rendering: PointRenderingConfig, index: number }[] = layer.mapRendering.map((r, i) => ({rendering: r, index: i})) - const pointRenderings = pointRenderObjects.filter(r => r.rendering.location.has("point")) - const centroidRenderings = pointRenderObjects.filter(r => r.rendering.location.has("centroid")) - - const lineRenderObjects = layer.lineRendering - - const withIndex : (any & {pointRenderingIndex: number | undefined, lineRenderingIndex: number | undefined})[] = []; - - for (const f of features) { - const feat = f.feature; - - if(feat.geometry.type === "Point"){ - - for (const rendering of pointRenderings) { - withIndex.push({ - ...feat, - pointRenderingIndex: rendering.index - }) - } - }else{ - // This is a a line - for (const rendering of centroidRenderings) { - withIndex.push({ - ...GeoOperations.centerpoint(feat), - pointRenderingIndex: rendering.index - }) - } - - - for (let i = 0; i < lineRenderObjects.length; i++){ - withIndex.push({ - ...feat, - lineRenderingIndex:i - }) - } - - } - } - return withIndex; - } - ); - - } - -} \ No newline at end of file diff --git a/Logic/State/ElementsState.ts b/Logic/State/ElementsState.ts index a4674430b..637482e60 100644 --- a/Logic/State/ElementsState.ts +++ b/Logic/State/ElementsState.ts @@ -49,7 +49,7 @@ export default class ElementsState extends FeatureSwitchState { constructor(layoutToUse: LayoutConfig) { super(layoutToUse); - this.changes = new Changes(layoutToUse.isLeftRightSensitive()) + this.changes = new Changes(layoutToUse?.isLeftRightSensitive() ?? false) { // -- Location control initialization diff --git a/Logic/UIEventSource.ts b/Logic/UIEventSource.ts index 571c74f8e..8051128ce 100644 --- a/Logic/UIEventSource.ts +++ b/Logic/UIEventSource.ts @@ -109,6 +109,20 @@ export class UIEventSource { promise?.catch(err => src.setData({error: err})) return src } + + public withEqualityStabilized(comparator: (t:T | undefined, t1:T | undefined) => boolean): UIEventSource{ + let oldValue = undefined; + return this.map(v => { + if(v == oldValue){ + return oldValue + } + if(comparator(oldValue, v)){ + return oldValue + } + oldValue = v; + return v; + }) + } /** * Given a UIEVentSource with a list, returns a new UIEventSource which is only updated if the _contents_ of the list are different. diff --git a/Models/ThemeConfig/Json/LayerConfigJson.ts b/Models/ThemeConfig/Json/LayerConfigJson.ts index 353cfcce5..09e8284a4 100644 --- a/Models/ThemeConfig/Json/LayerConfigJson.ts +++ b/Models/ThemeConfig/Json/LayerConfigJson.ts @@ -197,7 +197,12 @@ export interface LayerConfigJson { * A special value is 'questions', which indicates the location of the questions box. If not specified, it'll be appended to the bottom of the featureInfobox. * */ - tagRenderings?: (string | {builtin: string, override: any} | TagRenderingConfigJson) [], + tagRenderings?: (string | {builtin: string, override: any} | TagRenderingConfigJson | { + leftRightKeys: string[], + renderings: (string | {builtin: string, override: any} | TagRenderingConfigJson)[] + }) [], + + /** diff --git a/Models/ThemeConfig/Json/PointRenderingConfigJson.ts b/Models/ThemeConfig/Json/PointRenderingConfigJson.ts index 1ab46c8d0..45f34b755 100644 --- a/Models/ThemeConfig/Json/PointRenderingConfigJson.ts +++ b/Models/ThemeConfig/Json/PointRenderingConfigJson.ts @@ -15,7 +15,7 @@ export default interface PointRenderingConfigJson { * All the locations that this point should be rendered at. * Using `location: ["point", "centroid"] will always render centerpoint */ - location: ("point" | "centroid")[] + location: ("point" | "centroid" | "start" | "end")[] /** * The icon for an element. diff --git a/Models/ThemeConfig/Json/TagRenderingConfigJson.ts b/Models/ThemeConfig/Json/TagRenderingConfigJson.ts index f834ec19b..c2aa3d323 100644 --- a/Models/ThemeConfig/Json/TagRenderingConfigJson.ts +++ b/Models/ThemeConfig/Json/TagRenderingConfigJson.ts @@ -11,6 +11,12 @@ export interface TagRenderingConfigJson { * Used to keep the translations in sync. Only used in the tagRenderings-array of a layerConfig, not requered otherwise */ id?: string, + + /** + * Optional: this can group questions together in one question box. + * Written by 'left-right'-keys automatically + */ + group?: string /** * Renders this value. Note that "{key}"-parts are substituted by the corresponding values of the element. diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts index f9888f9b0..6518caca9 100644 --- a/Models/ThemeConfig/LayerConfig.ts +++ b/Models/ThemeConfig/LayerConfig.ts @@ -15,8 +15,9 @@ import WithContextLoader from "./WithContextLoader"; import LineRenderingConfig from "./LineRenderingConfig"; import PointRenderingConfigJson from "./Json/PointRenderingConfigJson"; import LineRenderingConfigJson from "./Json/LineRenderingConfigJson"; +import {TagRenderingConfigJson} from "./Json/TagRenderingConfigJson"; -export default class LayerConfig extends WithContextLoader{ +export default class LayerConfig extends WithContextLoader { id: string; name: Translation; @@ -31,7 +32,7 @@ export default class LayerConfig extends WithContextLoader{ maxzoom: number; title?: TagRenderingConfig; titleIcons: TagRenderingConfig[]; - + public readonly mapRendering: PointRenderingConfig[] public readonly lineRendering: LineRenderingConfig[] @@ -82,7 +83,7 @@ export default class LayerConfig extends WithContextLoader{ throw context + "Use 'geoJson' instead of 'geojson' (the J is a capital letter)"; } - this. source = new SourceConfig( + this.source = new SourceConfig( { osmTags: osmTags, geojsonSource: json.source["geoJson"], @@ -92,14 +93,15 @@ export default class LayerConfig extends WithContextLoader{ }, json.id ); + }else if(legacy === undefined){ + throw "No valid source defined ("+context+")" } else { - this. source = new SourceConfig({ + this.source = new SourceConfig({ osmTags: legacy, }); } - this.id = json.id; this.allowSplit = json.allowSplit ?? false; this.name = Translations.T(json.name, context + ".name"); @@ -191,22 +193,21 @@ export default class LayerConfig extends WithContextLoader{ return config; }); - if(json.mapRendering === undefined){ - throw "MapRendering is undefined in "+context + if (json.mapRendering === undefined) { + throw "MapRendering is undefined in " + context } this.mapRendering = json.mapRendering .filter(r => r["icon"] !== undefined || r["label"] !== undefined) - .map((r, i) => new PointRenderingConfig(r, context+".mapRendering["+i+"]")) + .map((r, i) => new PointRenderingConfig(r, context + ".mapRendering[" + i + "]")) this.lineRendering = json.mapRendering .filter(r => r["icon"] === undefined && r["label"] === undefined) - .map((r, i) => new LineRenderingConfig(r, context+".mapRendering["+i+"]")) + .map((r, i) => new LineRenderingConfig(r, context + ".mapRendering[" + i + "]")) - this.tagRenderings = this.trs(json.tagRenderings, false); - - const missingIds = json.tagRenderings?.filter(tr => typeof tr !== "string" && tr["builtin"] === undefined && tr["id"] === undefined) ?? []; + this.tagRenderings = this.ExtractLayerTagRenderings(json) + const missingIds = json.tagRenderings?.filter(tr => typeof tr !== "string" && tr["builtin"] === undefined && tr["id"] === undefined && tr["leftRightKeys"] === undefined) ?? []; if (missingIds.length > 0 && official) { console.error("Some tagRenderings of", this.id, "are missing an id:", missingIds) @@ -237,11 +238,11 @@ export default class LayerConfig extends WithContextLoader{ } } - this.titleIcons = this.trs(titleIcons, true); + this.titleIcons = this.ParseTagRenderings(titleIcons, true); this.title = this.tr("title", undefined); this.isShown = this.tr("isShown", "yes"); - + this.deletion = null; if (json.deletion === true) { json.deletion = {}; @@ -269,6 +270,98 @@ export default class LayerConfig extends WithContextLoader{ } } + public ExtractLayerTagRenderings(json: LayerConfigJson): TagRenderingConfig[] { + + if (json.tagRenderings === undefined) { + return [] + } + + const normalTagRenderings: (string | { builtin: string, override: any } | TagRenderingConfigJson)[] = [] + const leftRightRenderings: ({ leftRightKeys: string[], renderings: (string | { builtin: string, override: any } | TagRenderingConfigJson)[] })[] = [] + for (let i = 0; i < json.tagRenderings.length; i++) { + const tr = json.tagRenderings[i]; + const lrkDefined = tr["leftRightKeys"] !== undefined + const renderingsDefined = tr["renderings"] + + if (!lrkDefined && !renderingsDefined) { + // @ts-ignore + normalTagRenderings.push(tr) + continue + } + if (lrkDefined && renderingsDefined) { + // @ts-ignore + leftRightRenderings.push(tr) + continue + } + throw `Error in ${this._context}.tagrenderings[${i}]: got a value which defines either \`leftRightKeys\` or \`renderings\`, but not both. Either define both or move the \`renderings\` out of this scope` + } + + const allRenderings = this.ParseTagRenderings(normalTagRenderings, false); + + if(leftRightRenderings.length === 0){ + return allRenderings + } + + const leftRenderings : TagRenderingConfig[] = [] + const rightRenderings : TagRenderingConfig[] = [] + + function prepConfig(target:string, tr: TagRenderingConfigJson){ + + function replaceRecursive(transl: string | any){ + if(typeof transl === "string"){ + return transl.replace("left|right", target) + } + if(transl.map !== undefined){ + return transl.map(o => replaceRecursive(o)) + } + transl = {...transl} + for (const key in transl) { + transl[key] = replaceRecursive(transl[key]) + } + return transl + } + + const orig = tr; + tr = replaceRecursive(tr) + + tr.id = target+"-"+orig.id + tr.group = target + return tr + } + + + for (const leftRightRendering of leftRightRenderings) { + + const keysToRewrite = leftRightRendering.leftRightKeys + const tagRenderings = leftRightRendering.renderings + + const left = this.ParseTagRenderings(tagRenderings, false, tr => prepConfig("left", tr)) + const right = this.ParseTagRenderings(tagRenderings, false, tr => prepConfig("right", tr)) + + leftRenderings.push(...left) + rightRenderings.push(...right) + + } + + leftRenderings.push(new TagRenderingConfig({ + id: "questions", + group:"left", + }, "layerConfig.ts.leftQuestionBox")) + + rightRenderings.push(new TagRenderingConfig({ + id: "questions", + group:"right", + }, "layerConfig.ts.rightQuestionBox")) + + allRenderings.push(...leftRenderings) + allRenderings.push(...rightRenderings) + + + return allRenderings; + + } + + public CustomCodeSnippets(): string[] { if (this.calculatedTags === undefined) { return []; @@ -277,8 +370,6 @@ export default class LayerConfig extends WithContextLoader{ } - - public ExtractImages(): Set { const parts: Set[] = []; parts.push(...this.tagRenderings?.map((tr) => tr.ExtractImages(false))); @@ -293,12 +384,11 @@ export default class LayerConfig extends WithContextLoader{ for (const part of parts) { part?.forEach(allIcons.add, allIcons); } - return allIcons; } - - public isLeftRightSensitive() : boolean{ + + public isLeftRightSensitive(): boolean { return this.lineRendering.some(lr => lr.leftRightSensitive) } } \ No newline at end of file diff --git a/Models/ThemeConfig/PointRenderingConfig.ts b/Models/ThemeConfig/PointRenderingConfig.ts index e50891c8e..e34d6e34c 100644 --- a/Models/ThemeConfig/PointRenderingConfig.ts +++ b/Models/ThemeConfig/PointRenderingConfig.ts @@ -15,7 +15,7 @@ import {VariableUiElement} from "../../UI/Base/VariableUIElement"; export default class PointRenderingConfig extends WithContextLoader { - public readonly location: Set<"point" | "centroid"> + public readonly location: Set<"point" | "centroid" | "start" | "end"> public readonly icon: TagRenderingConfig; public readonly iconBadges: { if: TagsFilter; then: TagRenderingConfig }[]; diff --git a/Models/ThemeConfig/TagRenderingConfig.ts b/Models/ThemeConfig/TagRenderingConfig.ts index 51d3e9f53..5f223e6d6 100644 --- a/Models/ThemeConfig/TagRenderingConfig.ts +++ b/Models/ThemeConfig/TagRenderingConfig.ts @@ -14,6 +14,7 @@ import {Utils} from "../../Utils"; export default class TagRenderingConfig { readonly id: string; + readonly group: string; readonly render?: Translation; readonly question?: Translation; readonly condition?: TagsFilter; @@ -46,7 +47,8 @@ export default class TagRenderingConfig { this.question = null; this.condition = null; } - + + if(typeof json === "number"){ this.render = Translations.WT( ""+json) return; @@ -64,6 +66,7 @@ export default class TagRenderingConfig { this.id = json.id ?? ""; + this.group = json.group ?? ""; this.render = Translations.T(json.render, context + ".render"); this.question = Translations.T(json.question, context + ".question"); this.condition = TagUtils.Tag(json.condition ?? {"and": []}, `${context}.condition`); diff --git a/Models/ThemeConfig/WithContextLoader.ts b/Models/ThemeConfig/WithContextLoader.ts index cfdc5439b..0cab6616c 100644 --- a/Models/ThemeConfig/WithContextLoader.ts +++ b/Models/ThemeConfig/WithContextLoader.ts @@ -5,8 +5,8 @@ import {Utils} from "../../Utils"; export default class WithContextLoader { private readonly _json: any; - private readonly _context: string; - + protected readonly _context: string; + constructor(json: any, context: string) { this._json = json; this._context = context; @@ -43,20 +43,22 @@ export default class WithContextLoader { * Converts a list of tagRenderingCOnfigJSON in to TagRenderingConfig * A string is interpreted as a name to call */ - public trs( + public ParseTagRenderings( tagRenderings?: (string | { builtin: string, override: any } | TagRenderingConfigJson)[], - readOnly = false + readOnly = false, + prepConfig: ((config: TagRenderingConfigJson) => TagRenderingConfigJson) = undefined ) { if (tagRenderings === undefined) { return []; } - - const context = this._context - - const renderings: TagRenderingConfig[] = [] + const context = this._context + const renderings: TagRenderingConfig[] = [] + if (prepConfig === undefined) { + prepConfig = c => c + } for (let i = 0; i < tagRenderings.length; i++) { - let renderingJson= tagRenderings[i] + let renderingJson = tagRenderings[i] if (typeof renderingJson === "string") { renderingJson = {builtin: renderingJson, override: undefined} } @@ -70,41 +72,29 @@ export default class WithContextLoader { )}`; } - const tr = new TagRenderingConfig("questions", context); - renderings.push(tr) + const tr = new TagRenderingConfig("questions", context); + renderings.push(tr) continue; } if (renderingJson["override"] !== undefined) { - const sharedJson = SharedTagRenderings.SharedTagRenderingJson.get(renderingId) - const tr = new TagRenderingConfig( - Utils.Merge(renderingJson["override"], sharedJson), - `${context}.tagrendering[${i}]+override` - ); - renderings.push(tr) - continue - } + let sharedJson = SharedTagRenderings.SharedTagRenderingJson.get(renderingId) - const shared = SharedTagRenderings.SharedTagRendering.get(renderingId); + if (sharedJson === undefined) { + const keys = Array.from(SharedTagRenderings.SharedTagRenderingJson.keys()); + throw `Predefined tagRendering ${renderingId} not found in ${context}.\n Try one of ${keys.join( + ", " + )}\n If you intent to output this text literally, use {\"render\": } instead"}`; + } - if (shared !== undefined) { - renderings.push( shared) - continue + renderingJson = Utils.Merge(renderingJson["override"], sharedJson) } - if (Utils.runningFromConsole) { - continue - } - - const keys = Array.from( SharedTagRenderings.SharedTagRendering.keys() ); - throw `Predefined tagRendering ${renderingId} not found in ${context}.\n Try one of ${keys.join( - ", " - )}\n If you intent to output this text literally, use {\"render\": } instead"}`; } - const tr = new TagRenderingConfig( - renderingJson, - `${context}.tagrendering[${i}]` - ); + + const patchedConfig = prepConfig(renderingJson) + + const tr = new TagRenderingConfig(patchedConfig, `${context}.tagrendering[${i}]`); renderings.push(tr) } diff --git a/UI/Input/ValidatedTextField.ts b/UI/Input/ValidatedTextField.ts index ad5554346..78d543657 100644 --- a/UI/Input/ValidatedTextField.ts +++ b/UI/Input/ValidatedTextField.ts @@ -117,7 +117,8 @@ export default class ValidatedTextField { if (args[0]) { zoom = Number(args[0]) if (isNaN(zoom)) { - throw "Invalid zoom level for argument at 'length'-input" + console.error("Invalid zoom level for argument at 'length'-input. The offending argument is: ",args[0]," (using 19 instead)") + zoom = 19 } } diff --git a/UI/Popup/FeatureInfoBox.ts b/UI/Popup/FeatureInfoBox.ts index c31eff395..dde27d9c1 100644 --- a/UI/Popup/FeatureInfoBox.ts +++ b/UI/Popup/FeatureInfoBox.ts @@ -18,6 +18,7 @@ import {Translation} from "../i18n/Translation"; import {Utils} from "../../Utils"; import {SubstitutedTranslation} from "../SubstitutedTranslation"; import MoveWizard from "./MoveWizard"; +import {FixedUiElement} from "../Base/FixedUiElement"; export default class FeatureInfoBox extends ScrollableFullScreen { @@ -52,26 +53,33 @@ export default class FeatureInfoBox extends ScrollableFullScreen { private static GenerateContent(tags: UIEventSource, layerConfig: LayerConfig): BaseUIElement { - let questionBox: BaseUIElement = undefined; + let questionBoxes: Map = new Map(); if (State.state.featureSwitchUserbadge.data) { - questionBox = new QuestionBox(tags, layerConfig.tagRenderings, layerConfig.units); + const allGroupNames = Utils.Dedup(layerConfig.tagRenderings.map(tr => tr.group)) + for (const groupName of allGroupNames) { + const questions = layerConfig.tagRenderings.filter(tr => tr.group === groupName) + const questionBox = new QuestionBox(tags, questions, layerConfig.units); + console.log("Groupname:", groupName) + questionBoxes.set(groupName, questionBox) + } } - let questionBoxIsUsed = false; const renderings: BaseUIElement[] = layerConfig.tagRenderings.map(tr => { - if (tr.question === null) { - // This is the question box! - questionBoxIsUsed = true; + if (tr.question === null || tr.id === "questions") { + console.log("Rendering is", tr) + // This is a question box! + const questionBox = questionBoxes.get(tr.group) + questionBoxes.delete(tr.group) return questionBox; } return new EditableTagRendering(tags, tr, layerConfig.units); }); let editElements: BaseUIElement[] = [] - if (!questionBoxIsUsed) { + questionBoxes.forEach(questionBox => { editElements.push(questionBox); - } + }) if(layerConfig.allowMove) { editElements.push( diff --git a/UI/ShowDataLayer/ShowDataLayer.ts b/UI/ShowDataLayer/ShowDataLayer.ts index 00f04c199..589d2f726 100644 --- a/UI/ShowDataLayer/ShowDataLayer.ts +++ b/UI/ShowDataLayer/ShowDataLayer.ts @@ -4,7 +4,7 @@ import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; import FeatureInfoBox from "../Popup/FeatureInfoBox"; import {ShowDataLayerOptions} from "./ShowDataLayerOptions"; import {ElementStorage} from "../../Logic/ElementStorage"; -import RenderingMultiPlexerFeatureSource from "../../Logic/FeatureSource/Sources/WayHandlingApplyingFeatureSource"; +import RenderingMultiPlexerFeatureSource from "../../Logic/FeatureSource/Sources/RenderingMultiPlexerFeatureSource"; /* // import 'leaflet-polylineoffset'; We don't actually import it here. It is imported in the 'MinimapImplementation'-class, which'll result in a patched 'L' object. @@ -161,7 +161,18 @@ export default class ShowDataLayer { const coords = L.GeoJSON.coordsToLatLngs(feat.geometry.coordinates) const tagsSource = this.allElements?.addOrGetElement(feat) ?? new UIEventSource(feat.properties); let offsettedLine; - tagsSource.map(tags => this._layerToShow.lineRendering[feat.lineRenderingIndex].GenerateLeafletStyle(tags)).addCallbackAndRunD(lineStyle => { + tagsSource + .map(tags => this._layerToShow.lineRendering[feat.lineRenderingIndex].GenerateLeafletStyle(tags)) + .withEqualityStabilized((a, b) => { + if(a === b){ + return true + } + if(a === undefined || b === undefined){ + return false + } + return a.offset === b.offset && a.color === b.color && a.weight === b.weight && a.dashArray === b.dashArray + }) + .addCallbackAndRunD(lineStyle => { if (offsettedLine !== undefined) { self.geoLayer.removeLayer(offsettedLine) } @@ -261,7 +272,7 @@ export default class ShowDataLayer { let infobox: FeatureInfoBox = undefined; - const id = `popup-${feature.properties.id}-${feature.geometry.type}-${this.showDataLayerid}-${this._cleanCount}-${feature.pointerRenderingIndex ?? feature.lineRenderingIndex}` + const id = `popup-${feature.properties.id}-${feature.geometry.type}-${this.showDataLayerid}-${this._cleanCount}-${feature.pointRenderingIndex ?? feature.lineRenderingIndex}` popup.setContent(`
Popup for ${feature.properties.id} ${feature.geometry.type} ${id} is loading
`) leafletLayer.on("popupopen", () => { if (infobox === undefined) { diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index 188215296..6d0784b7d 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -29,6 +29,8 @@ import AllImageProviders from "../Logic/ImageProviders/AllImageProviders"; import WikipediaBox from "./Wikipedia/WikipediaBox"; import SimpleMetaTagger from "../Logic/SimpleMetaTagger"; import MultiApply from "./Popup/MultiApply"; +import AllKnownLayers from "../Customizations/AllKnownLayers"; +import ShowDataLayer from "./ShowDataLayer/ShowDataLayer"; export interface SpecialVisualization { funcName: string, @@ -49,7 +51,7 @@ export default class SpecialVisualizations { constr: ((state: State, tags: UIEventSource) => { const calculatedTags = [].concat( SimpleMetaTagger.lazyTags, - ... state.layoutToUse.layers.map(l => l.calculatedTags?.map(c => c[0]) ?? [])) + ...state.layoutToUse.layers.map(l => l.calculatedTags?.map(c => c[0]) ?? [])) return new VariableUiElement(tags.map(tags => { const parts = []; for (const key in tags) { @@ -57,20 +59,20 @@ export default class SpecialVisualizations { continue } let v = tags[key] - if(v === ""){ + if (v === "") { v = "empty string" } parts.push([key, v ?? "undefined"]); } - - for(const key of calculatedTags){ + + for (const key of calculatedTags) { const value = tags[key] - if(value === undefined){ + if (value === undefined) { continue } - parts.push([ ""+key+"", value ]) + parts.push(["" + key + "", value]) } - + return new Table( ["key", "value"], parts @@ -88,7 +90,7 @@ export default class SpecialVisualizations { }], constr: (state: State, tags, args) => { let imagePrefixes: string[] = undefined; - if(args.length > 0){ + if (args.length > 0) { imagePrefixes = [].concat(...args.map(a => a.split(","))); } return new ImageCarousel(AllImageProviders.LoadImagesFor(tags, imagePrefixes), tags, imagePrefixes); @@ -101,9 +103,9 @@ export default class SpecialVisualizations { name: "image-key", doc: "Image tag to add the URL to (or image-tag:0, image-tag:1 when multiple images are added)", defaultValue: "image" - },{ - name:"label", - doc:"The text to show on the button", + }, { + name: "label", + doc: "The text to show on the button", defaultValue: "Add image" }], constr: (state: State, tags, args) => { @@ -125,17 +127,16 @@ export default class SpecialVisualizations { new VariableUiElement( tagsSource.map(tags => tags[args[0]]) .map(wikidata => { - const wikidatas : string[] = + const wikidatas: string[] = Utils.NoEmpty(wikidata?.split(";")?.map(wd => wd.trim()) ?? []) return new WikipediaBox(wikidatas) }) - ) - + }, { funcName: "minimap", - docs: "A small map showing the selected feature. Note that no styling is applied, wrap this in a div", + docs: "A small map showing the selected feature.", args: [ { doc: "The (maximum) zoomlevel: the target zoomlevel after fitting the entire feature. The minimap will fit the entire feature, then zoom out to this zoom level. The higher, the more zoomed in with 1 being the entire world and 19 being really close", @@ -214,6 +215,54 @@ export default class SpecialVisualizations { ) + minimap.SetStyle("overflow: hidden; pointer-events: none;") + return minimap; + } + }, + + { + funcName: "sided_minimap", + docs: "A small map showing _only one side_ the selected feature. *This features requires to have linerenderings with offset* as only linerenderings with a postive or negative offset will be shown. Note: in most cases, this map will be automatically introduced", + args: [ + { + doc: "The side to show, either `left` or `right`", + name: "side", + } + ], + example: "`{sided_minimap(left)}`", + constr: (state, tagSource, args) => { + + const properties = tagSource.data; + const locationSource = new UIEventSource({ + lat: Number(properties._lat), + lon: Number(properties._lon), + zoom: 18 + }) + const minimap = Minimap.createMiniMap( + { + background: state.backgroundLayer, + location: locationSource, + allowMoving: false + } + ) + const side = args[0] + const feature = state.allElements.ContainingFeatures.get(tagSource.data.id) + const copy = {...feature} + copy.properties = { + id: side + } + new ShowDataLayer( + { + leafletMap: minimap["leafletMap"], + enablePopups: false, + zoomToFeatures: true, + layerToShow: AllKnownLayers.sharedLayers.get("left_right_style"), + features: new StaticFeatureSource([copy], false), + allElements: State.state.allElements + } + ) + + minimap.SetStyle("overflow: hidden; pointer-events: none;") return minimap; } @@ -432,9 +481,11 @@ export default class SpecialVisualizations { doc: "A nice icon to show in the button", defaultValue: "./assets/svg/addSmall.svg" }, - {name:"minzoom", - doc: "How far the contributor must zoom in before being able to import the point", - defaultValue: "18"}], + { + name: "minzoom", + doc: "How far the contributor must zoom in before being able to import the point", + defaultValue: "18" + }], docs: `This button will copy the data from an external dataset into OpenStreetMap. It is only functional in official themes but can be tested in unofficial themes. If you want to import a dataset, make sure that: @@ -487,14 +538,24 @@ There are also some technicalities in your theme to keep in mind: ) } }, - {funcName: "multi_apply", + { + funcName: "multi_apply", docs: "A button to apply the tagging of this object onto a list of other features. This is an advanced feature for which you'll need calculatedTags", - args:[ + args: [ {name: "feature_ids", doc: "A JSOn-serialized list of IDs of features to apply the tagging on"}, - {name: "keys", doc: "One key (or multiple keys, seperated by ';') of the attribute that should be copied onto the other features." }, + { + name: "keys", + doc: "One key (or multiple keys, seperated by ';') of the attribute that should be copied onto the other features." + }, {name: "text", doc: "The text to show on the button"}, - {name:"autoapply",doc:"A boolean indicating wether this tagging should be applied automatically if the relevant tags on this object are changed. A visual element indicating the multi_apply is still shown"}, - {name:"overwrite",doc:"If set to 'true', the tags on the other objects will always be overwritten. The default behaviour will be to only change the tags on other objects if they are either undefined or had the same value before the change"} + { + name: "autoapply", + doc: "A boolean indicating wether this tagging should be applied automatically if the relevant tags on this object are changed. A visual element indicating the multi_apply is still shown" + }, + { + name: "overwrite", + doc: "If set to 'true', the tags on the other objects will always be overwritten. The default behaviour will be to only change the tags on other objects if they are either undefined or had the same value before the change" + } ], example: "{multi_apply(_features_with_the_same_name_within_100m, name:etymology:wikidata;name:etymology, Apply etymology information on all nearby objects with the same name)}", constr: (state, tagsSource, args) => { @@ -503,14 +564,14 @@ There are also some technicalities in your theme to keep in mind: const text = args[2] const autoapply = args[3]?.toLowerCase() === "true" const overwrite = args[4]?.toLowerCase() === "true" - const featureIds : UIEventSource = tagsSource.map(tags => { - const ids = tags[featureIdsKey] - try{ - if(ids === undefined){ + const featureIds: UIEventSource = tagsSource.map(tags => { + const ids = tags[featureIdsKey] + try { + if (ids === undefined) { return [] } return JSON.parse(ids); - }catch(e){ + } catch (e) { console.warn("Could not parse ", ids, "as JSON to extract IDS which should be shown on the map.") return [] } @@ -526,7 +587,7 @@ There are also some technicalities in your theme to keep in mind: state } ); - + } } ] diff --git a/assets/layers/left_right_style/left_right_style.json b/assets/layers/left_right_style/left_right_style.json new file mode 100644 index 000000000..029cad2f1 --- /dev/null +++ b/assets/layers/left_right_style/left_right_style.json @@ -0,0 +1,41 @@ +{ + "id": "left_right_style", + "description": "Special meta-style which will show one single line, either on the left or on the right depending on the id. This is used in the small popups with left_right roads", + "source": { + "osmTags": { + "or": [ + "id=left", + "id=right" + ] + } + }, + "mapRendering": [ + { + "width": 6, + "offset": { + "mappings": [ + { + "if": "id=left", + "then": "-5" + }, + { + "if": "id=right", + "then": "5" + } + ] + }, + "color": { + "mappings": [ + { + "if": "id=left", + "then": "#00f" + }, + { + "if": "id=right", + "then": "#f00" + } + ] + } + } + ] +} \ No newline at end of file diff --git a/assets/themes/sidewalks/sidewalks.json b/assets/themes/sidewalks/sidewalks.json index 940caba80..8e30b0a2b 100644 --- a/assets/themes/sidewalks/sidewalks.json +++ b/assets/themes/sidewalks/sidewalks.json @@ -32,20 +32,111 @@ }, "title": { "render": { - "en": "Street {name}" - } + "en": "{name}" + }, + "mappings": [ + { + "if": "name=", + "then": "Nameless street" + } + ] }, "description": { "en": "Layer showing sidewalks of highways" }, - "tagRenderings": [], + "tagRenderings": [ + + { + "id": "streetname", + "render": { + "en": "This street is named {name}" + } + }, + + { + "leftRightKeys": "sidewalk:left|right", + "renderings": [ + { + "id": "sidewalk_minimap", + "render": "{sided_minimap(left|right):height:3rem;border-radius:0.5rem;overflow:hidden}" + }, + { + "id": "has_sidewalk", + "question": "Is there a sidewalk on the left|right side of the road?", + "mappings": [{ + "if": "sidewalk:left|right=yes", + "then": "Yes, there is a sidewalk on this side of the road" + }, + { + "if": "sidewalk:left|right=no", + "then": "No, there is no seperated sidewalk to walk on" + }] + }, + { + "id": "sidewalk_width", + "question": "What is the width of the sidewalk on this side of the road?", + "render": "This sidewalk is {sidewalk:left|right:width}m wide", + "condition": "sidewalk:left|right=yes", + "freeform": { + "key": "sidewalk:left|right:width", + "type": "length", + "helperArgs": ["21", "map"] + } + } + ] + + + }], "mapRendering": [ { - "location": [ - "point" - ] + "location": ["start","end"], + "icon": "circle:#ccc", + "iconSize": "20,20,center" }, - {} + { + "color": "#ffffff55", + "width": 8 + }, + { + "color": { + "render": "#888" + }, + "width": { + "render": 6, + "mappings": [ + { + "if": { + "or": [ + "sidewalk:left=", + "sidewalk:left=no", + "sidewalk:left=separate" + ] + }, + "then": 0 + } + ] + }, + "offset": -6 + }, + { + "color": "#888", + "width": { + "render": 6, + "mappings": [ + { + "if": { + "or": [ + "sidewalk:right=", + "sidewalk:right=no", + "sidewalk:right=separate" + ] + }, + "then": 0 + } + ] + }, + "offset": 6 + } ], "allowSplit": true } diff --git a/scripts/lint.ts b/scripts/lint.ts index 633c4a98b..31261d6b1 100644 --- a/scripts/lint.ts +++ b/scripts/lint.ts @@ -32,7 +32,7 @@ function fixLayerConfig(config: LayerConfigJson): void { } } - if (config.mapRendering === undefined || true) { + if (config.mapRendering === undefined) { // This is a legacy format, lets create a pointRendering let location: ("point" | "centroid")[] = ["point"] let wayHandling: number = config["wayHandling"] ?? 0 From 688b991677748fd4702f0bb2782ccab200e8ae45 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 23 Oct 2021 00:31:41 +0200 Subject: [PATCH 23/95] Change to groups and exposure of groups, add sticky header to first group item, fix shared questions --- Models/ThemeConfig/Json/LayerConfigJson.ts | 8 +- .../Json/TagRenderingConfigJson.ts | 6 +- Models/ThemeConfig/LayerConfig.ts | 75 ++++++++++--------- Models/ThemeConfig/WithContextLoader.ts | 23 +++--- UI/Input/LengthInput.ts | 1 + UI/Popup/EditableTagRendering.ts | 1 - UI/Popup/FeatureInfoBox.ts | 54 ++++++++----- assets/themes/sidewalks/sidewalks.json | 5 +- 8 files changed, 101 insertions(+), 72 deletions(-) diff --git a/Models/ThemeConfig/Json/LayerConfigJson.ts b/Models/ThemeConfig/Json/LayerConfigJson.ts index 09e8284a4..aa822f8ec 100644 --- a/Models/ThemeConfig/Json/LayerConfigJson.ts +++ b/Models/ThemeConfig/Json/LayerConfigJson.ts @@ -196,9 +196,15 @@ export interface LayerConfigJson { * * A special value is 'questions', which indicates the location of the questions box. If not specified, it'll be appended to the bottom of the featureInfobox. * + * At last, one can define a group of renderings where parts of all strings will be replaced by multiple other strings. + * This is mainly create questions for a 'left' and a 'right' side of the road. + * These will be grouped and questions will be asked together */ tagRenderings?: (string | {builtin: string, override: any} | TagRenderingConfigJson | { - leftRightKeys: string[], + rewrite: { + sourceString: string, + into: string[] + }[], renderings: (string | {builtin: string, override: any} | TagRenderingConfigJson)[] }) [], diff --git a/Models/ThemeConfig/Json/TagRenderingConfigJson.ts b/Models/ThemeConfig/Json/TagRenderingConfigJson.ts index c2aa3d323..456c693b5 100644 --- a/Models/ThemeConfig/Json/TagRenderingConfigJson.ts +++ b/Models/ThemeConfig/Json/TagRenderingConfigJson.ts @@ -13,8 +13,8 @@ export interface TagRenderingConfigJson { id?: string, /** - * Optional: this can group questions together in one question box. - * Written by 'left-right'-keys automatically + * If 'group' is defined on many tagRenderings, these are grouped together when shown. The questions are grouped together as well. + * The first tagRendering of a group will always be a sticky element. */ group?: string @@ -173,7 +173,5 @@ export interface TagRenderingConfigJson { * If this is important to your usecase, consider using multiple radiobutton-fields without `multiAnswer` */ ifnot?: AndOrTagConfigJson | string - }[] - } \ No newline at end of file diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts index 6518caca9..7099705a7 100644 --- a/Models/ThemeConfig/LayerConfig.ts +++ b/Models/ThemeConfig/LayerConfig.ts @@ -207,7 +207,7 @@ export default class LayerConfig extends WithContextLoader { this.tagRenderings = this.ExtractLayerTagRenderings(json) - const missingIds = json.tagRenderings?.filter(tr => typeof tr !== "string" && tr["builtin"] === undefined && tr["id"] === undefined && tr["leftRightKeys"] === undefined) ?? []; + const missingIds = json.tagRenderings?.filter(tr => typeof tr !== "string" && tr["builtin"] === undefined && tr["id"] === undefined && tr["rewrite"] === undefined) ?? []; if (missingIds.length > 0 && official) { console.error("Some tagRenderings of", this.id, "are missing an id:", missingIds) @@ -277,39 +277,41 @@ export default class LayerConfig extends WithContextLoader { } const normalTagRenderings: (string | { builtin: string, override: any } | TagRenderingConfigJson)[] = [] - const leftRightRenderings: ({ leftRightKeys: string[], renderings: (string | { builtin: string, override: any } | TagRenderingConfigJson)[] })[] = [] + + + const renderingsToRewrite: ({ rewrite:{ + sourceString: string, + into: string[] + }, renderings: (string | { builtin: string, override: any } | TagRenderingConfigJson)[] })[] = [] for (let i = 0; i < json.tagRenderings.length; i++) { const tr = json.tagRenderings[i]; - const lrkDefined = tr["leftRightKeys"] !== undefined + const rewriteDefined = tr["rewrite"] !== undefined const renderingsDefined = tr["renderings"] - if (!lrkDefined && !renderingsDefined) { + if (!rewriteDefined && !renderingsDefined) { // @ts-ignore normalTagRenderings.push(tr) continue } - if (lrkDefined && renderingsDefined) { + if (rewriteDefined && renderingsDefined) { // @ts-ignore - leftRightRenderings.push(tr) + renderingsToRewrite.push(tr) continue } - throw `Error in ${this._context}.tagrenderings[${i}]: got a value which defines either \`leftRightKeys\` or \`renderings\`, but not both. Either define both or move the \`renderings\` out of this scope` + throw `Error in ${this._context}.tagrenderings[${i}]: got a value which defines either \`rewrite\` or \`renderings\`, but not both. Either define both or move the \`renderings\` out of this scope` } const allRenderings = this.ParseTagRenderings(normalTagRenderings, false); - if(leftRightRenderings.length === 0){ + if(renderingsToRewrite.length === 0){ return allRenderings } - const leftRenderings : TagRenderingConfig[] = [] - const rightRenderings : TagRenderingConfig[] = [] - - function prepConfig(target:string, tr: TagRenderingConfigJson){ + function prepConfig(keyToRewrite: string, target:string, tr: TagRenderingConfigJson){ function replaceRecursive(transl: string | any){ if(typeof transl === "string"){ - return transl.replace("left|right", target) + return transl.replace(keyToRewrite, target) } if(transl.map !== undefined){ return transl.map(o => replaceRecursive(o)) @@ -328,33 +330,34 @@ export default class LayerConfig extends WithContextLoader { tr.group = target return tr } - - - for (const leftRightRendering of leftRightRenderings) { - - const keysToRewrite = leftRightRendering.leftRightKeys - const tagRenderings = leftRightRendering.renderings - - const left = this.ParseTagRenderings(tagRenderings, false, tr => prepConfig("left", tr)) - const right = this.ParseTagRenderings(tagRenderings, false, tr => prepConfig("right", tr)) - - leftRenderings.push(...left) - rightRenderings.push(...right) + const rewriteGroups: Map = new Map() + for (const rewriteGroup of renderingsToRewrite) { + + const tagRenderings = rewriteGroup.renderings + const textToReplace = rewriteGroup.rewrite.sourceString + const targets = rewriteGroup.rewrite.into + for (const target of targets) { + const parsedRenderings = this.ParseTagRenderings(tagRenderings, false, tr => prepConfig(textToReplace, target, tr)) + + if(!rewriteGroups.has(target)){ + rewriteGroups.set(target, []) + } + rewriteGroups.get(target).push(... parsedRenderings) + } } - leftRenderings.push(new TagRenderingConfig({ - id: "questions", - group:"left", - }, "layerConfig.ts.leftQuestionBox")) - - rightRenderings.push(new TagRenderingConfig({ - id: "questions", - group:"right", - }, "layerConfig.ts.rightQuestionBox")) - allRenderings.push(...leftRenderings) - allRenderings.push(...rightRenderings) + rewriteGroups.forEach((group, groupName) => { + group.push(new TagRenderingConfig({ + id:"questions", + group:groupName + })) + }) + + rewriteGroups.forEach(group => { + allRenderings.push(...group) + }) return allRenderings; diff --git a/Models/ThemeConfig/WithContextLoader.ts b/Models/ThemeConfig/WithContextLoader.ts index 0cab6616c..fea75511e 100644 --- a/Models/ThemeConfig/WithContextLoader.ts +++ b/Models/ThemeConfig/WithContextLoader.ts @@ -47,7 +47,7 @@ export default class WithContextLoader { tagRenderings?: (string | { builtin: string, override: any } | TagRenderingConfigJson)[], readOnly = false, prepConfig: ((config: TagRenderingConfigJson) => TagRenderingConfigJson) = undefined - ) { + ) : TagRenderingConfig[]{ if (tagRenderings === undefined) { return []; } @@ -77,18 +77,17 @@ export default class WithContextLoader { continue; } - if (renderingJson["override"] !== undefined) { - let sharedJson = SharedTagRenderings.SharedTagRenderingJson.get(renderingId) - - if (sharedJson === undefined) { - const keys = Array.from(SharedTagRenderings.SharedTagRenderingJson.keys()); - throw `Predefined tagRendering ${renderingId} not found in ${context}.\n Try one of ${keys.join( - ", " - )}\n If you intent to output this text literally, use {\"render\": } instead"}`; - } - - renderingJson = Utils.Merge(renderingJson["override"], sharedJson) + let sharedJson = SharedTagRenderings.SharedTagRenderingJson.get(renderingId) + if (sharedJson === undefined) { + const keys = Array.from(SharedTagRenderings.SharedTagRenderingJson.keys()); + throw `Predefined tagRendering ${renderingId} not found in ${context}.\n Try one of ${keys.join( + ", " + )}\n If you intent to output this text literally, use {\"render\": } instead"}`; } + if (renderingJson["override"] !== undefined) { + sharedJson = Utils.Merge(renderingJson["override"], sharedJson) + } + renderingJson = sharedJson } diff --git a/UI/Input/LengthInput.ts b/UI/Input/LengthInput.ts index 4eb39d7e9..2875f4362 100644 --- a/UI/Input/LengthInput.ts +++ b/UI/Input/LengthInput.ts @@ -45,6 +45,7 @@ export default class LengthInput extends InputElement { background: this.background, allowMoving: false, location: this._location, + attribution:true, leafletOptions: { tap: true } diff --git a/UI/Popup/EditableTagRendering.ts b/UI/Popup/EditableTagRendering.ts index 2be62b01b..55cd7504d 100644 --- a/UI/Popup/EditableTagRendering.ts +++ b/UI/Popup/EditableTagRendering.ts @@ -71,7 +71,6 @@ export default class EditableTagRendering extends Toggle { editMode ) } - rendering.SetClass("block w-full break-word text-default m-1 p-1 border-b border-gray-200 mb-2 pb-2") return rendering; } diff --git a/UI/Popup/FeatureInfoBox.ts b/UI/Popup/FeatureInfoBox.ts index dde27d9c1..ce30d851e 100644 --- a/UI/Popup/FeatureInfoBox.ts +++ b/UI/Popup/FeatureInfoBox.ts @@ -5,7 +5,6 @@ import Combine from "../Base/Combine"; import TagRenderingAnswer from "./TagRenderingAnswer"; import State from "../../State"; import ScrollableFullScreen from "../Base/ScrollableFullScreen"; -import {Tag} from "../../Logic/Tags/Tag"; import Constants from "../../Models/Constants"; import SharedTagRenderings from "../../Customizations/SharedTagRenderings"; import BaseUIElement from "../BaseUIElement"; @@ -18,7 +17,6 @@ import {Translation} from "../i18n/Translation"; import {Utils} from "../../Utils"; import {SubstitutedTranslation} from "../SubstitutedTranslation"; import MoveWizard from "./MoveWizard"; -import {FixedUiElement} from "../Base/FixedUiElement"; export default class FeatureInfoBox extends ScrollableFullScreen { @@ -55,8 +53,8 @@ export default class FeatureInfoBox extends ScrollableFullScreen { layerConfig: LayerConfig): BaseUIElement { let questionBoxes: Map = new Map(); + const allGroupNames = Utils.Dedup(layerConfig.tagRenderings.map(tr => tr.group)) if (State.state.featureSwitchUserbadge.data) { - const allGroupNames = Utils.Dedup(layerConfig.tagRenderings.map(tr => tr.group)) for (const groupName of allGroupNames) { const questions = layerConfig.tagRenderings.filter(tr => tr.group === groupName) const questionBox = new QuestionBox(tags, questions, layerConfig.units); @@ -65,23 +63,45 @@ export default class FeatureInfoBox extends ScrollableFullScreen { } } - const renderings: BaseUIElement[] = layerConfig.tagRenderings.map(tr => { - if (tr.question === null || tr.id === "questions") { - console.log("Rendering is", tr) - // This is a question box! - const questionBox = questionBoxes.get(tr.group) - questionBoxes.delete(tr.group) - return questionBox; + const allRenderings = [] + for (let i = 0; i < allGroupNames.length; i++) { + const groupName = allGroupNames[i]; + + const trs = layerConfig.tagRenderings.filter(tr => tr.group === groupName) + const renderingsForGroup: BaseUIElement[] = [] + for (const tr of trs) { + if (tr.question === null || tr.id === "questions") { + // This is a question box! + const questionBox = questionBoxes.get(tr.group) + questionBoxes.delete(tr.group) + renderingsForGroup.push(questionBox) + } else { + const etr = new EditableTagRendering(tags, tr, layerConfig.units).SetClass("editable-tag-rendering") + + renderingsForGroup.push(etr) + } } - return new EditableTagRendering(tags, tr, layerConfig.units); - }); + let j = 0 + if (i !== 0) { + renderingsForGroup[0]?.SetStyle("position: sticky; top: -5px") + j = 1 + } + for (/* j = 0 or 1 */; j < renderingsForGroup.length; j++) { + renderingsForGroup[j].SetClass("block w-full break-word text-default m-1 p-1 border-b border-gray-200 mb-2 pb-2") + } + + + + allRenderings.push(...renderingsForGroup) + } + let editElements: BaseUIElement[] = [] questionBoxes.forEach(questionBox => { editElements.push(questionBox); }) - if(layerConfig.allowMove) { + if (layerConfig.allowMove) { editElements.push( new VariableUiElement(tags.map(tags => tags.id).map(id => { const feature = State.state.allElements.ContainingFeatures.get(id) @@ -115,7 +135,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen { const hasMinimap = layerConfig.tagRenderings.some(tr => FeatureInfoBox.hasMinimap(tr)) if (!hasMinimap) { - renderings.push(new TagRenderingAnswer(tags, SharedTagRenderings.SharedTagRendering.get("minimap"))) + allRenderings.push(new TagRenderingAnswer(tags, SharedTagRenderings.SharedTagRendering.get("minimap"))) } editElements.push( @@ -140,7 +160,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen { new VariableUiElement( State.state.featureSwitchIsDebugging.map(isDebugging => { if (isDebugging) { - const config: TagRenderingConfig = new TagRenderingConfig({render: "{all_tags()}"}, ""); + const config: TagRenderingConfig = new TagRenderingConfig({render: "{all_tags()}"}, ""); return new TagRenderingAnswer(tags, config, "all_tags") } }) @@ -155,9 +175,9 @@ export default class FeatureInfoBox extends ScrollableFullScreen { return new Combine(editElements).SetClass("flex flex-col") } )) - renderings.push(editors) + allRenderings.push(editors) - return new Combine(renderings).SetClass("block") + return new Combine(allRenderings).SetClass("block") } diff --git a/assets/themes/sidewalks/sidewalks.json b/assets/themes/sidewalks/sidewalks.json index 8e30b0a2b..6167c6acc 100644 --- a/assets/themes/sidewalks/sidewalks.json +++ b/assets/themes/sidewalks/sidewalks.json @@ -54,7 +54,10 @@ }, { - "leftRightKeys": "sidewalk:left|right", + "rewrite": { + "sourceString": "left|right", + "into": ["left","right"] + }, "renderings": [ { "id": "sidewalk_minimap", From c71e5e8423dfa7db754b9a69ee927a4556907496 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sat, 23 Oct 2021 00:46:47 +0200 Subject: [PATCH 24/95] Split off street lamps, test with Assen Open Data --- .../street_lamps}/license_info.json | 0 .../street_lamps}/street_lamp.svg | 0 assets/layers/street_lamps/street_lamps.json | 371 +++++++++++++++++ .../street_lighting/street_lighting.json | 376 +----------------- .../street_lighting_assen.json | 50 +++ langs/layers/en.json | 148 +++++++ langs/layers/nl.json | 148 +++++++ langs/themes/en.json | 148 ------- langs/themes/nl.json | 152 +------ 9 files changed, 724 insertions(+), 669 deletions(-) rename assets/{themes/street_lighting => layers/street_lamps}/license_info.json (100%) rename assets/{themes/street_lighting => layers/street_lamps}/street_lamp.svg (100%) create mode 100644 assets/layers/street_lamps/street_lamps.json create mode 100644 assets/themes/street_lighting/street_lighting_assen.json diff --git a/assets/themes/street_lighting/license_info.json b/assets/layers/street_lamps/license_info.json similarity index 100% rename from assets/themes/street_lighting/license_info.json rename to assets/layers/street_lamps/license_info.json diff --git a/assets/themes/street_lighting/street_lamp.svg b/assets/layers/street_lamps/street_lamp.svg similarity index 100% rename from assets/themes/street_lighting/street_lamp.svg rename to assets/layers/street_lamps/street_lamp.svg diff --git a/assets/layers/street_lamps/street_lamps.json b/assets/layers/street_lamps/street_lamps.json new file mode 100644 index 000000000..40b37e88c --- /dev/null +++ b/assets/layers/street_lamps/street_lamps.json @@ -0,0 +1,371 @@ +{ + "id": "street_lamps", + "name": { + "en": "Street Lamps", + "nl": "Straatlantaarns" + }, + "source": { + "osmTags": "highway=street_lamp" + }, + "minZoom": 16, + "title": { + "render": { + "en": "Street Lamp", + "nl": "Straatlantaarn" + }, + "mappings": [ + { + "if": "ref~*", + "then": { + "en": "Street Lamp {ref}", + "nl": "Straatlantaarn {ref}" + } + } + ] + }, + "icon": "./assets/layers/street_lamps/street_lamp.svg", + "iconOverlays": [ + { + "if": "light:colour=white", + "then": "circle:white", + "badge": true + }, + { + "if": "light:colour=orange", + "then": "circle:#FFB000", + "badge": true + }, + { + "if": "light:colour=green", + "then": "circle:#55FF55", + "badge": true + } + ], + "presets": [ + { + "title": { + "en": "street lamp", + "nl": "straatlantaarn" + }, + "tags": [ + "highway=street_lamp" + ], + "preciseInput": true + } + ], + "tagRenderings": [ + { + "id": "ref", + "render": { + "en": "This street lamp has the reference number {ref}", + "nl": "Deze straatlantaarn heeft het nummer {ref}" + }, + "question": { + "en": "What is the reference number of this street lamp?", + "nl": "Wat is het nummer van deze straatlantaarn?" + }, + "freeform": { + "key": "ref" + } + }, + { + "id": "support", + "question": { + "en": "How is this street lamp mounted?", + "nl": "Hoe is deze straatlantaarn gemonteerd?" + }, + "mappings": [ + { + "if": "support=catenary", + "then": { + "en": "This lamp is suspended using cables", + "nl": "Deze lantaarn hangt aan kabels" + } + }, + { + "if": "support=ceiling", + "then": { + "en": "This lamp is mounted on a ceiling", + "nl": "Deze lantaarn hangt aan een plafond" + } + }, + { + "if": "support=ground", + "then": { + "en": "This lamp is mounted in the ground", + "nl": "Deze lantaarn zit in de grond" + } + }, + { + "if": "support=pedestal", + "then": { + "en": "This lamp is mounted on a short pole (mostly < 1.5m)", + "nl": "Deze lantaarn zit op een korte paal (meestal < 1.5m)" + } + }, + { + "if": "support=pole", + "then": { + "en": "This lamp is mounted on a pole", + "nl": "Deze lantaarn zit op een paal" + } + }, + { + "if": "support=wall", + "then": { + "en": "This lamp is mounted directly to the wall", + "nl": "Deze lantaarn hangt direct aan de muur" + } + }, + { + "if": "support=wall_mount", + "then": { + "en": "This lamp is mounted to the wall using a metal bar", + "nl": "Deze lantaarn hangt aan de muur met een metalen balk" + } + } + ] + }, + { + "id": "lamp_mount", + "question": { + "en": "How is this lamp mounted to the pole?", + "nl": "Hoe zit deze lantaarn aan de paal?" + }, + "condition": "support=pole", + "mappings": [ + { + "if": "lamp_mount=straight_mast", + "then": { + "en": "This lamp sits atop of a straight mast", + "nl": "Deze lantaarn zit boven op een rechte paal" + } + }, + { + "if": "lamp_mount=bent_mast", + "then": { + "en": "This lamp sits at the end of a bent mast", + "nl": "Deze lantaarn zit aan het eind van een gebogen paal" + } + } + ] + }, + { + "id": "method", + "question": { + "en": "What kind of lighting does this lamp use?", + "nl": "Wat voor verlichting gebruikt deze lantaarn?" + }, + "mappings": [ + { + "if": "light:method=electric", + "then": { + "en": "This lamp is lit electrically", + "nl": "Deze lantaarn is elektrisch verlicht" + }, + "hideInAnswer": true + }, + { + "if": "light:method=LED", + "then": { + "en": "This lamp uses LEDs", + "nl": "Deze lantaarn gebruikt LEDs" + } + }, + { + "if": "light:method=incandescent", + "then": { + "en": "This lamp uses incandescent lighting", + "nl": "Deze lantaarn gebruikt gloeilampen" + } + }, + { + "if": "light:method=halogen", + "then": { + "en": "This lamp uses halogen lighting", + "nl": "Deze lantaarn gebruikt halogeen verlichting" + } + }, + { + "if": "light:method=discharge", + "then": { + "en": "This lamp uses discharge lamps (unknown type)", + "nl": "Deze lantaarn gebruikt gasontladingslampen (onbekend type)" + } + }, + { + "if": "light:method=mercury", + "then": { + "en": "This lamp uses a mercury-vapour lamp (lightly blueish)", + "nl": "Deze lantaarn gebruikt een kwiklamp (enigszins blauwachtig)" + } + }, + { + "if": "light:method=metal-halide", + "then": { + "en": "This lamp uses metal-halide lamps (bright white)", + "nl": "Deze lantaarn gebruikt metaalhalidelampen" + } + }, + { + "if": "light:method=fluorescent", + "then": { + "en": "This lamp uses fluorescent lighting", + "nl": "Deze lantaarn gebruikt fluorescentieverlichting (TL en spaarlamp)" + } + }, + { + "if": "light:method=sodium", + "then": { + "en": "This lamp uses sodium lamps (unknown type)", + "nl": "Deze lantaarn gebruikt natriumlampen (onbekend type)" + } + }, + { + "if": "light:method=low_pressure_sodium", + "then": { + "en": "This lamp uses low pressure sodium lamps (monochrome orange)", + "nl": "Deze lantaarn gebruikt lagedruknatriumlampen (monochroom oranje)" + } + }, + { + "if": "light:method=high_pressure_sodium", + "then": { + "en": "This lamp uses high pressure sodium lamps (orange with white)", + "nl": "Deze lantaarn gebruikt hogedruknatriumlampen (oranje met wit)" + } + }, + { + "if": "light:method=gas", + "then": { + "en": "This lamp is lit using gas", + "nl": "Deze lantaarn wordt verlicht met gas" + } + } + ] + }, + { + "id": "colour", + "question": { + "en": "What colour light does this lamp emit?", + "nl": "Wat voor kleur licht geeft deze lantaarn?" + }, + "render": { + "en": "This lamp emits {light:colour} light", + "nl": "Deze lantaarn geeft {light:colour} licht" + }, + "freeform": { + "key": "light:colour", + "type": "color" + }, + "mappings": [ + { + "if": "light:colour=white", + "then": { + "en": "This lamp emits white light", + "nl": "Deze lantaarn geeft wit licht" + } + }, + { + "if": "light:colour=green", + "then": { + "en": "This lamp emits green light", + "nl": "Deze lantaarn geeft groen licht" + } + }, + { + "if": "light:colour=orange", + "then": { + "en": "This lamp emits orange light", + "nl": "Deze lantaarn geeft oranje licht" + } + } + ] + }, + { + "id": "count", + "render": { + "en": "This lamp has {light:count} fixtures", + "nl": "Deze lantaarn heeft {light:count} lampen" + }, + "question": { + "en": "How many fixtures does this light have?", + "nl": "Hoeveel lampen heeft deze lantaarn?" + }, + "condition": "support=pole", + "freeform": { + "key": "light:count", + "type": "pnat" + }, + "mappings": [ + { + "if": "light:count=1", + "then": { + "en": "This lamp has 1 fixture", + "nl": "Deze lantaarn heeft 1 lamp" + } + }, + { + "if": "light:count=2", + "then": { + "en": "This lamp has 2 fixtures", + "nl": "Deze lantaarn heeft 2 lampen" + } + } + ] + }, + { + "id": "lit", + "question": { + "en": "When is this lamp lit?", + "nl": "Wanneer is deze lantaarn verlicht?" + }, + "mappings": [ + { + "if": "light:lit=dusk-dawn", + "then": { + "en": "This lamp is lit at night", + "nl": "Deze lantaarn is 's nachts verlicht" + } + }, + { + "if": "light:lit=24/7", + "then": { + "en": "This lamp is lit 24/7", + "nl": "Deze lantaarn is 24/7 verlicht" + } + }, + { + "if": "light:lit=motion", + "then": { + "en": "This lamp is lit based on motion", + "nl": "Deze lantaarn is verlicht op basis van beweging" + } + }, + { + "if": "light:lit=demand", + "then": { + "en": "This lamp is lit based on demand (e.g. with a pushbutton)", + "nl": "Deze lantaarn is verlicht op verzoek (bijv. met een drukknop)" + } + } + ] + }, + { + "id": "direction", + "render": { + "en": "This lamp points towards {light:direction}", + "nl": "Deze lantaarn is gericht naar {light:direction}" + }, + "question": { + "en": "Where does this lamp point to?", + "nl": "Waar is deze lamp heengericht?" + }, + "condition": "light:count=1", + "freeform": { + "key": "light:direction", + "type": "direction" + } + } + ] +} \ No newline at end of file diff --git a/assets/themes/street_lighting/street_lighting.json b/assets/themes/street_lighting/street_lighting.json index aafa12838..31a94c047 100644 --- a/assets/themes/street_lighting/street_lighting.json +++ b/assets/themes/street_lighting/street_lighting.json @@ -1,7 +1,7 @@ { "id": "street_lighting", "maintainer": "Robin van der Linde", - "version": "20211019", + "version": "2021-10-22", "language": [ "en", "nl" @@ -14,382 +14,12 @@ "en": "A theme showing information about street lamps, like light type, mounting, light colour and much more.", "nl": "Een thema met details over straatlantaarns zoals verlichtingstype, montage, lichtkleur en veel meer." }, - "icon": "./assets/themes/street_lighting/street_lamp.svg", + "icon": "./assets/layers/street_lamps/street_lamp.svg", "startZoom": 19, "startLat": 52.99319, "startLon": 6.56113, "layers": [ - { - "id": "streetlamps", - "name": { - "en": "Street Lamps", - "nl": "Straatlantaarns" - }, - "source": { - "osmTags": "highway=street_lamp" - }, - "minZoom": 16, - "title": { - "render": { - "en": "Street Lamp", - "nl": "Straatlantaarn" - }, - "mappings": [ - { - "if": "ref~*", - "then": { - "en": "Street Lamp {ref}", - "nl": "Straatlantaarn {ref}" - } - } - ] - }, - "icon": "./assets/themes/street_lighting/street_lamp.svg", - "iconOverlays": [ - { - "if": "light:colour=white", - "then": "circle:white", - "badge": true - }, - { - "if": "light:colour=orange", - "then": "circle:#FFB000", - "badge": true - }, - { - "if": "light:colour=green", - "then": "circle:#55FF55", - "badge": true - } - ], - "presets": [ - { - "title": { - "en": "street lamp", - "nl": "straatlantaarn" - }, - "tags": [ - "highway=street_lamp" - ], - "preciseInput": true - } - ], - "tagRenderings": [ - { - "id": "ref", - "render": { - "en": "This street lamp has the reference number {ref}", - "nl": "Deze straatlantaarn heeft het nummer {ref}" - }, - "question": { - "en": "What is the reference number of this street lamp?", - "nl": "Wat is het nummer van deze straatlantaarn?" - }, - "freeform": { - "key": "ref" - } - }, - { - "id": "support", - "question": { - "en": "How is this street lamp mounted?", - "nl": "Hoe is deze straatlantaarn gemonteerd?" - }, - "mappings": [ - { - "if": "support=catenary", - "then": { - "en": "This lamp is suspended using cables", - "nl": "Deze lantaarn hangt aan kabels" - } - }, - { - "if": "support=ceiling", - "then": { - "en": "This lamp is mounted on a ceiling", - "nl": "Deze lantaarn hangt aan een plafond" - } - }, - { - "if": "support=ground", - "then": { - "en": "This lamp is mounted in the ground", - "nl": "Deze lantaarn zit in de grond" - } - }, - { - "if": "support=pedestal", - "then": { - "en": "This lamp is mounted on a short pole (mostly < 1.5m)", - "nl": "Deze lantaarn zit op een korte paal (meestal < 1.5m)" - } - }, - { - "if": "support=pole", - "then": { - "en": "This lamp is mounted on a pole", - "nl": "Deze lantaarn zit op een paal" - } - }, - { - "if": "support=wall", - "then": { - "en": "This lamp is mounted directly to the wall", - "nl": "Deze lantaarn hangt direct aan de muur" - } - }, - { - "if": "support=wall_mount", - "then": { - "en": "This lamp is mounted to the wall using a metal bar", - "nl": "Deze lantaarn hangt aan de muur met een metalen balk" - } - } - ] - }, - { - "id": "lamp_mount", - "question": { - "en": "How is this lamp mounted to the pole?", - "nl": "Hoe zit deze lantaarn aan de paal?" - }, - "condition": "support=pole", - "mappings": [ - { - "if": "lamp_mount=straight_mast", - "then": { - "en": "This lamp sits atop of a straight mast", - "nl": "Deze lantaarn zit boven op een rechte paal" - } - }, - { - "if": "lamp_mount=bent_mast", - "then": { - "en": "This lamp sits at the end of a bent mast", - "nl": "Deze lantaarn zit aan het eind van een gebogen paal" - } - } - ] - }, - { - "id": "method", - "question": { - "en": "What kind of lighting does this lamp use?", - "nl": "Wat voor verlichting gebruikt deze lantaarn?" - }, - "mappings": [ - { - "if": "light:method=electric", - "then": { - "en": "This lamp is lit electrically", - "nl": "Deze lantaarn is elektrisch verlicht" - }, - "hideInAnswer": true - }, - { - "if": "light:method=LED", - "then": { - "en": "This lamp uses LEDs", - "nl": "Deze lantaarn gebruikt LEDs" - } - }, - { - "if": "light:method=incandescent", - "then": { - "en": "This lamp uses incandescent lighting", - "nl": "Deze lantaarn gebruikt gloeilampen" - } - }, - { - "if": "light:method=halogen", - "then": { - "en": "This lamp uses halogen lighting", - "nl": "Deze lantaarn gebruikt halogeen verlichting" - } - }, - { - "if": "light:method=discharge", - "then": { - "en": "This lamp uses discharge lamps (unknown type)", - "nl": "Deze lantaarn gebruikt gasontladingslampen (onbekend type)" - } - }, - { - "if": "light:method=mercury", - "then": { - "en": "This lamp uses a mercury-vapour lamp (lightly blueish)", - "nl": "Deze lantaarn gebruikt een kwiklamp (enigszins blauwachtig)" - } - }, - { - "if": "light:method=metal-halide", - "then": { - "en": "This lamp uses metal-halide lamps (bright white)", - "nl": "Deze lantaarn gebruikt metaalhalidelampen" - } - }, - { - "if": "light:method=fluorescent", - "then": { - "en": "This lamp uses fluorescent lighting", - "nl": "Deze lantaarn gebruikt fluorescentieverlichting (TL en spaarlamp)" - } - }, - { - "if": "light:method=sodium", - "then": { - "en": "This lamp uses sodium lamps (unknown type)", - "nl": "Deze lantaarn gebruikt natriumlampen (onbekend type)" - } - }, - { - "if": "light:method=low_pressure_sodium", - "then": { - "en": "This lamp uses low pressure sodium lamps (monochrome orange)", - "nl": "Deze lantaarn gebruikt lagedruknatriumlampen (monochroom oranje)" - } - }, - { - "if": "light:method=high_pressure_sodium", - "then": { - "en": "This lamp uses high pressure sodium lamps (orange with white)", - "nl": "Deze lantaarn gebruikt hogedruknatriumlampen (oranje met wit)" - } - }, - { - "if": "light:method=gas", - "then": { - "en": "This lamp is lit using gas", - "nl": "Deze lantaarn wordt verlicht met gas" - } - } - ] - }, - { - "id": "colour", - "question": { - "en": "What colour light does this lamp emit?", - "nl": "Wat voor kleur licht geeft deze lantaarn?" - }, - "render": { - "en": "This lamp emits {light:colour} light", - "nl": "Deze lantaarn geeft {light:colour} licht" - }, - "freeform": { - "key": "light:colour", - "type": "color" - }, - "mappings": [ - { - "if": "light:colour=white", - "then": { - "en": "This lamp emits white light", - "nl": "Deze lantaarn geeft wit licht" - } - }, - { - "if": "light:colour=green", - "then": { - "en": "This lamp emits green light", - "nl": "Deze lantaarn geeft groen licht" - } - }, - { - "if": "light:colour=orange", - "then": { - "en": "This lamp emits orange light", - "nl": "Deze lantaarn geeft oranje licht" - } - } - ] - }, - { - "id": "count", - "render": { - "en": "This lamp has {light:count} fixtures", - "nl": "Deze lantaarn heeft {light:count} lampen" - }, - "question": { - "en": "How many fixtures does this light have?", - "nl": "Hoeveel lampen heeft deze lantaarn?" - }, - "condition": "support=pole", - "freeform": { - "key": "light:count", - "type": "pnat" - }, - "mappings": [ - { - "if": "light:count=1", - "then": { - "en": "This lamp has 1 fixture", - "nl": "Deze lantaarn heeft 1 lamp" - } - }, - { - "if": "light:count=2", - "then": { - "en": "This lamp has 2 fixtures", - "nl": "Deze lantaarn heeft 2 lampen" - } - } - ] - }, - { - "id": "lit", - "question": { - "en": "When is this lamp lit?", - "nl": "Wanneer is deze lantaarn verlicht?" - }, - "mappings": [ - { - "if": "light:lit=dusk-dawn", - "then": { - "en": "This lamp is lit at night", - "nl": "Deze lantaarn is 's nachts verlicht" - } - }, - { - "if": "light:lit=24/7", - "then": { - "en": "This lamp is lit 24/7", - "nl": "Deze lantaarn is 24/7 verlicht" - } - }, - { - "if": "light:lit=motion", - "then": { - "en": "This lamp is lit based on motion", - "nl": "Deze lantaarn is verlicht op basis van beweging" - } - }, - { - "if": "light:lit=demand", - "then": { - "en": "This lamp is lit based on demand (e.g. with a pushbutton)", - "nl": "Deze lantaarn is verlicht op verzoek (bijv. met een drukknop)" - } - } - ] - }, - { - "id": "direction", - "render": { - "en": "This lamp points towards {light:direction}", - "nl": "Deze lantaarn is gericht naar {light:direction}" - }, - "question": { - "en": "Where does this lamp point to?", - "nl": "Waar is deze lamp heengericht?" - }, - "condition": "light:count=1", - "freeform": { - "key": "light:direction", - "type": "direction" - } - } - ] - }, + "street_lamps", { "id": "lit_streets", "name": { diff --git a/assets/themes/street_lighting/street_lighting_assen.json b/assets/themes/street_lighting/street_lighting_assen.json new file mode 100644 index 000000000..2acfffa96 --- /dev/null +++ b/assets/themes/street_lighting/street_lighting_assen.json @@ -0,0 +1,50 @@ +{ + "id": "street_lighting_assen", + "maintainer": "Robin van der Linde", + "version": "2021-10-22", + "language": [ + "nl" + ], + "title": { + "nl": "Straatverlichting Assen" + }, + "description": { + "nl": "Straatverlichtingkaart met dataset Assen" + }, + "icon": "./assets/layers/street_lamps/street_lamp.svg", + "startZoom": 19, + "startLat": 52.99319, + "startLon": 6.56113, + "layers": [ + "street_lamps", + { + "id": "Assen", + "name": "Dataset Assen", + "source": { + "osmTags": "Lichtmastnummer~*", + "geoJson": "https://opendata.arcgis.com/datasets/ba37cdb372064b3199c548b75d16a609_0.geojson", + "isOsmCache": false + }, + "calculatedTags": [ + "_closest_osm_street_lamp=feat.closest('street_lamps')?.properties?.id", + "_closest_osm_street_lamp_distance=feat.distanceTo(feat.properties._closest_osm_street_lamp) * 1000", + "_has_closeby_feature=Number(feat.properties._closest_osm_street_lamp_distance) < 5 ? 'yes' : 'no'" + ], + "title": "Straatlantaarn in dataset", + "icon": { + "render": "circle:red", + "mappings": [ + { + "if": "_has_closeby_feature=yes", + "then": "circle:#008000aa" + } + ] + }, + "iconSize": "20,20,center", + "tagRenderings": [ + "all_tags" + ] + } + ], + "hideFromOverview": true +} \ No newline at end of file diff --git a/langs/layers/en.json b/langs/layers/en.json index dd3acaee9..ac83b2b58 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -3395,6 +3395,154 @@ "render": "Sport pitch" } }, + "street_lamps": { + "name": "Street Lamps", + "presets": { + "0": { + "title": "street lamp" + } + }, + "tagRenderings": { + "colour": { + "mappings": { + "0": { + "then": "This lamp emits white light" + }, + "1": { + "then": "This lamp emits green light" + }, + "2": { + "then": "This lamp emits orange light" + } + }, + "question": "What colour light does this lamp emit?", + "render": "This lamp emits {light:colour} light" + }, + "count": { + "mappings": { + "0": { + "then": "This lamp has 1 fixture" + }, + "1": { + "then": "This lamp has 2 fixtures" + } + }, + "question": "How many fixtures does this light have?", + "render": "This lamp has {light:count} fixtures" + }, + "direction": { + "question": "Where does this lamp point to?", + "render": "This lamp points towards {light:direction}" + }, + "lamp_mount": { + "mappings": { + "0": { + "then": "This lamp sits atop of a straight mast" + }, + "1": { + "then": "This lamp sits at the end of a bent mast" + } + }, + "question": "How is this lamp mounted to the pole?" + }, + "lit": { + "mappings": { + "0": { + "then": "This lamp is lit at night" + }, + "1": { + "then": "This lamp is lit 24/7" + }, + "2": { + "then": "This lamp is lit based on motion" + }, + "3": { + "then": "This lamp is lit based on demand (e.g. with a pushbutton)" + } + }, + "question": "When is this lamp lit?" + }, + "method": { + "mappings": { + "0": { + "then": "This lamp is lit electrically" + }, + "1": { + "then": "This lamp uses LEDs" + }, + "2": { + "then": "This lamp uses incandescent lighting" + }, + "3": { + "then": "This lamp uses halogen lighting" + }, + "4": { + "then": "This lamp uses discharge lamps (unknown type)" + }, + "5": { + "then": "This lamp uses a mercury-vapour lamp (lightly blueish)" + }, + "6": { + "then": "This lamp uses metal-halide lamps (bright white)" + }, + "7": { + "then": "This lamp uses fluorescent lighting" + }, + "8": { + "then": "This lamp uses sodium lamps (unknown type)" + }, + "9": { + "then": "This lamp uses low pressure sodium lamps (monochrome orange)" + }, + "10": { + "then": "This lamp uses high pressure sodium lamps (orange with white)" + }, + "11": { + "then": "This lamp is lit using gas" + } + }, + "question": "What kind of lighting does this lamp use?" + }, + "ref": { + "question": "What is the reference number of this street lamp?", + "render": "This street lamp has the reference number {ref}" + }, + "support": { + "mappings": { + "0": { + "then": "This lamp is suspended using cables" + }, + "1": { + "then": "This lamp is mounted on a ceiling" + }, + "2": { + "then": "This lamp is mounted in the ground" + }, + "3": { + "then": "This lamp is mounted on a short pole (mostly < 1.5m)" + }, + "4": { + "then": "This lamp is mounted on a pole" + }, + "5": { + "then": "This lamp is mounted directly to the wall" + }, + "6": { + "then": "This lamp is mounted to the wall using a metal bar" + } + }, + "question": "How is this street lamp mounted?" + } + }, + "title": { + "mappings": { + "0": { + "then": "Street Lamp {ref}" + } + }, + "render": "Street Lamp" + } + }, "surveillance_camera": { "name": "Surveillance camera's", "tagRenderings": { diff --git a/langs/layers/nl.json b/langs/layers/nl.json index b484dc8c4..d3f3f6d41 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -3623,6 +3623,154 @@ "render": "Sportterrein" } }, + "street_lamps": { + "name": "Straatlantaarns", + "presets": { + "0": { + "title": "straatlantaarn" + } + }, + "tagRenderings": { + "colour": { + "mappings": { + "0": { + "then": "Deze lantaarn geeft wit licht" + }, + "1": { + "then": "Deze lantaarn geeft groen licht" + }, + "2": { + "then": "Deze lantaarn geeft oranje licht" + } + }, + "question": "Wat voor kleur licht geeft deze lantaarn?", + "render": "Deze lantaarn geeft {light:colour} licht" + }, + "count": { + "mappings": { + "0": { + "then": "Deze lantaarn heeft 1 lamp" + }, + "1": { + "then": "Deze lantaarn heeft 2 lampen" + } + }, + "question": "Hoeveel lampen heeft deze lantaarn?", + "render": "Deze lantaarn heeft {light:count} lampen" + }, + "direction": { + "question": "Waar is deze lamp heengericht?", + "render": "Deze lantaarn is gericht naar {light:direction}" + }, + "lamp_mount": { + "mappings": { + "0": { + "then": "Deze lantaarn zit boven op een rechte paal" + }, + "1": { + "then": "Deze lantaarn zit aan het eind van een gebogen paal" + } + }, + "question": "Hoe zit deze lantaarn aan de paal?" + }, + "lit": { + "mappings": { + "0": { + "then": "Deze lantaarn is 's nachts verlicht" + }, + "1": { + "then": "Deze lantaarn is 24/7 verlicht" + }, + "2": { + "then": "Deze lantaarn is verlicht op basis van beweging" + }, + "3": { + "then": "Deze lantaarn is verlicht op verzoek (bijv. met een drukknop)" + } + }, + "question": "Wanneer is deze lantaarn verlicht?" + }, + "method": { + "mappings": { + "0": { + "then": "Deze lantaarn is elektrisch verlicht" + }, + "1": { + "then": "Deze lantaarn gebruikt LEDs" + }, + "2": { + "then": "Deze lantaarn gebruikt gloeilampen" + }, + "3": { + "then": "Deze lantaarn gebruikt halogeen verlichting" + }, + "4": { + "then": "Deze lantaarn gebruikt gasontladingslampen (onbekend type)" + }, + "5": { + "then": "Deze lantaarn gebruikt een kwiklamp (enigszins blauwachtig)" + }, + "6": { + "then": "Deze lantaarn gebruikt metaalhalidelampen" + }, + "7": { + "then": "Deze lantaarn gebruikt fluorescentieverlichting (TL en spaarlamp)" + }, + "8": { + "then": "Deze lantaarn gebruikt natriumlampen (onbekend type)" + }, + "9": { + "then": "Deze lantaarn gebruikt lagedruknatriumlampen (monochroom oranje)" + }, + "10": { + "then": "Deze lantaarn gebruikt hogedruknatriumlampen (oranje met wit)" + }, + "11": { + "then": "Deze lantaarn wordt verlicht met gas" + } + }, + "question": "Wat voor verlichting gebruikt deze lantaarn?" + }, + "ref": { + "question": "Wat is het nummer van deze straatlantaarn?", + "render": "Deze straatlantaarn heeft het nummer {ref}" + }, + "support": { + "mappings": { + "0": { + "then": "Deze lantaarn hangt aan kabels" + }, + "1": { + "then": "Deze lantaarn hangt aan een plafond" + }, + "2": { + "then": "Deze lantaarn zit in de grond" + }, + "3": { + "then": "Deze lantaarn zit op een korte paal (meestal < 1.5m)" + }, + "4": { + "then": "Deze lantaarn zit op een paal" + }, + "5": { + "then": "Deze lantaarn hangt direct aan de muur" + }, + "6": { + "then": "Deze lantaarn hangt aan de muur met een metalen balk" + } + }, + "question": "Hoe is deze straatlantaarn gemonteerd?" + } + }, + "title": { + "mappings": { + "0": { + "then": "Straatlantaarn {ref}" + } + }, + "render": "Straatlantaarn" + } + }, "surveillance_camera": { "name": "Bewakingscamera's", "tagRenderings": { diff --git a/langs/themes/en.json b/langs/themes/en.json index c48072aa6..4d0e46575 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -1230,154 +1230,6 @@ "street_lighting": { "description": "A theme showing information about street lamps, like light type, mounting, light colour and much more.", "layers": { - "0": { - "name": "Street Lamps", - "presets": { - "0": { - "title": "street lamp" - } - }, - "tagRenderings": { - "colour": { - "mappings": { - "0": { - "then": "This lamp emits white light" - }, - "1": { - "then": "This lamp emits green light" - }, - "2": { - "then": "This lamp emits orange light" - } - }, - "question": "What colour light does this lamp emit?", - "render": "This lamp emits {light:colour} light" - }, - "count": { - "mappings": { - "0": { - "then": "This lamp has 1 fixture" - }, - "1": { - "then": "This lamp has 2 fixtures" - } - }, - "question": "How many fixtures does this light have?", - "render": "This lamp has {light:count} fixtures" - }, - "direction": { - "question": "Where does this lamp point to?", - "render": "This lamp points towards {light:direction}" - }, - "lamp_mount": { - "mappings": { - "0": { - "then": "This lamp sits atop of a straight mast" - }, - "1": { - "then": "This lamp sits at the end of a bent mast" - } - }, - "question": "How is this lamp mounted to the pole?" - }, - "lit": { - "mappings": { - "0": { - "then": "This lamp is lit at night" - }, - "1": { - "then": "This lamp is lit 24/7" - }, - "2": { - "then": "This lamp is lit based on motion" - }, - "3": { - "then": "This lamp is lit based on demand (e.g. with a pushbutton)" - } - }, - "question": "When is this lamp lit?" - }, - "method": { - "mappings": { - "0": { - "then": "This lamp is lit electrically" - }, - "1": { - "then": "This lamp uses LEDs" - }, - "2": { - "then": "This lamp uses incandescent lighting" - }, - "3": { - "then": "This lamp uses halogen lighting" - }, - "4": { - "then": "This lamp uses discharge lamps (unknown type)" - }, - "5": { - "then": "This lamp uses a mercury-vapour lamp (lightly blueish)" - }, - "6": { - "then": "This lamp uses metal-halide lamps (bright white)" - }, - "7": { - "then": "This lamp uses fluorescent lighting" - }, - "8": { - "then": "This lamp uses sodium lamps (unknown type)" - }, - "9": { - "then": "This lamp uses low pressure sodium lamps (monochrome orange)" - }, - "10": { - "then": "This lamp uses high pressure sodium lamps (orange with white)" - }, - "11": { - "then": "This lamp is lit using gas" - } - }, - "question": "What kind of lighting does this lamp use?" - }, - "ref": { - "question": "What is the reference number of this street lamp?", - "render": "This street lamp has the reference number {ref}" - }, - "support": { - "mappings": { - "0": { - "then": "This lamp is suspended using cables" - }, - "1": { - "then": "This lamp is mounted on a ceiling" - }, - "2": { - "then": "This lamp is mounted in the ground" - }, - "3": { - "then": "This lamp is mounted on a short pole (mostly < 1.5m)" - }, - "4": { - "then": "This lamp is mounted on a pole" - }, - "5": { - "then": "This lamp is mounted directly to the wall" - }, - "6": { - "then": "This lamp is mounted to the wall using a metal bar" - } - }, - "question": "How is this street lamp mounted?" - } - }, - "title": { - "mappings": { - "0": { - "then": "Street Lamp {ref}" - } - }, - "render": "Street Lamp" - } - }, "1": { "name": "Lit streets", "tagRenderings": { diff --git a/langs/themes/nl.json b/langs/themes/nl.json index e4f9fae6f..5848cc0a5 100644 --- a/langs/themes/nl.json +++ b/langs/themes/nl.json @@ -900,154 +900,6 @@ "street_lighting": { "description": "Een thema met details over straatlantaarns zoals verlichtingstype, montage, lichtkleur en veel meer.", "layers": { - "0": { - "name": "Straatlantaarns", - "presets": { - "0": { - "title": "straatlantaarn" - } - }, - "tagRenderings": { - "colour": { - "mappings": { - "0": { - "then": "Deze lantaarn geeft wit licht" - }, - "1": { - "then": "Deze lantaarn geeft groen licht" - }, - "2": { - "then": "Deze lantaarn geeft oranje licht" - } - }, - "question": "Wat voor kleur licht geeft deze lantaarn?", - "render": "Deze lantaarn geeft {light:colour} licht" - }, - "count": { - "mappings": { - "0": { - "then": "Deze lantaarn heeft 1 lamp" - }, - "1": { - "then": "Deze lantaarn heeft 2 lampen" - } - }, - "question": "Hoeveel lampen heeft deze lantaarn?", - "render": "Deze lantaarn heeft {light:count} lampen" - }, - "direction": { - "question": "Waar is deze lamp heengericht?", - "render": "Deze lantaarn is gericht naar {light:direction}" - }, - "lamp_mount": { - "mappings": { - "0": { - "then": "Deze lantaarn zit boven op een rechte paal" - }, - "1": { - "then": "Deze lantaarn zit aan het eind van een gebogen paal" - } - }, - "question": "Hoe zit deze lantaarn aan de paal?" - }, - "lit": { - "mappings": { - "0": { - "then": "Deze lantaarn is 's nachts verlicht" - }, - "1": { - "then": "Deze lantaarn is 24/7 verlicht" - }, - "2": { - "then": "Deze lantaarn is verlicht op basis van beweging" - }, - "3": { - "then": "Deze lantaarn is verlicht op verzoek (bijv. met een drukknop)" - } - }, - "question": "Wanneer is deze lantaarn verlicht?" - }, - "method": { - "mappings": { - "0": { - "then": "Deze lantaarn is elektrisch verlicht" - }, - "1": { - "then": "Deze lantaarn gebruikt LEDs" - }, - "2": { - "then": "Deze lantaarn gebruikt gloeilampen" - }, - "3": { - "then": "Deze lantaarn gebruikt halogeen verlichting" - }, - "4": { - "then": "Deze lantaarn gebruikt gasontladingslampen (onbekend type)" - }, - "5": { - "then": "Deze lantaarn gebruikt een kwiklamp (enigszins blauwachtig)" - }, - "6": { - "then": "Deze lantaarn gebruikt metaalhalidelampen" - }, - "7": { - "then": "Deze lantaarn gebruikt fluorescentieverlichting (TL en spaarlamp)" - }, - "8": { - "then": "Deze lantaarn gebruikt natriumlampen (onbekend type)" - }, - "9": { - "then": "Deze lantaarn gebruikt lagedruknatriumlampen (monochroom oranje)" - }, - "10": { - "then": "Deze lantaarn gebruikt hogedruknatriumlampen (oranje met wit)" - }, - "11": { - "then": "Deze lantaarn wordt verlicht met gas" - } - }, - "question": "Wat voor verlichting gebruikt deze lantaarn?" - }, - "ref": { - "question": "Wat is het nummer van deze straatlantaarn?", - "render": "Deze straatlantaarn heeft het nummer {ref}" - }, - "support": { - "mappings": { - "0": { - "then": "Deze lantaarn hangt aan kabels" - }, - "1": { - "then": "Deze lantaarn hangt aan een plafond" - }, - "2": { - "then": "Deze lantaarn zit in de grond" - }, - "3": { - "then": "Deze lantaarn zit op een korte paal (meestal < 1.5m)" - }, - "4": { - "then": "Deze lantaarn zit op een paal" - }, - "5": { - "then": "Deze lantaarn hangt direct aan de muur" - }, - "6": { - "then": "Deze lantaarn hangt aan de muur met een metalen balk" - } - }, - "question": "Hoe is deze straatlantaarn gemonteerd?" - } - }, - "title": { - "mappings": { - "0": { - "then": "Straatlantaarn {ref}" - } - }, - "render": "Straatlantaarn" - } - }, "1": { "name": "Verlichte straten", "tagRenderings": { @@ -1101,6 +953,10 @@ }, "title": "Straatverlichting" }, + "street_lighting_assen": { + "description": "Straatverlichtingkaart met dataset Assen", + "title": "Straatverlichting Assen" + }, "surveillance": { "description": "Op deze open kaart kan je bewakingscamera's vinden.", "shortDescription": "Bewakingscameras en dergelijke", From d689140c249347bc697653e12488a65cf442d8d7 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sat, 23 Oct 2021 13:23:45 +0200 Subject: [PATCH 25/95] Move over to tiled GeoJSON + update description --- assets/themes/street_lighting/street_lighting.json | 4 ++-- .../themes/street_lighting/street_lighting_assen.json | 11 +++++++---- langs/themes/en.json | 2 +- langs/themes/nl.json | 6 +++--- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/assets/themes/street_lighting/street_lighting.json b/assets/themes/street_lighting/street_lighting.json index 31a94c047..f1bd77be0 100644 --- a/assets/themes/street_lighting/street_lighting.json +++ b/assets/themes/street_lighting/street_lighting.json @@ -11,8 +11,8 @@ "nl": "Straatverlichting" }, "description": { - "en": "A theme showing information about street lamps, like light type, mounting, light colour and much more.", - "nl": "Een thema met details over straatlantaarns zoals verlichtingstype, montage, lichtkleur en veel meer." + "en": "On this map you can find everything about street lighting", + "nl": "Op deze kaart vind je alles over straatlantaarns" }, "icon": "./assets/layers/street_lamps/street_lamp.svg", "startZoom": 19, diff --git a/assets/themes/street_lighting/street_lighting_assen.json b/assets/themes/street_lighting/street_lighting_assen.json index 2acfffa96..799950826 100644 --- a/assets/themes/street_lighting/street_lighting_assen.json +++ b/assets/themes/street_lighting/street_lighting_assen.json @@ -3,13 +3,14 @@ "maintainer": "Robin van der Linde", "version": "2021-10-22", "language": [ - "nl" + "nl", + "en" ], "title": { - "nl": "Straatverlichting Assen" + "nl": "Straatverlichting - Assen" }, "description": { - "nl": "Straatverlichtingkaart met dataset Assen" + "nl": "Op deze kaart vind je alles over straatlantaarns + een dataset van Assen" }, "icon": "./assets/layers/street_lamps/street_lamp.svg", "startZoom": 19, @@ -22,7 +23,9 @@ "name": "Dataset Assen", "source": { "osmTags": "Lichtmastnummer~*", - "geoJson": "https://opendata.arcgis.com/datasets/ba37cdb372064b3199c548b75d16a609_0.geojson", + "#geoJson": "https://opendata.arcgis.com/datasets/ba37cdb372064b3199c548b75d16a609_0.geojson", + "geoJson": "https://robinlinde.github.io/tiles/assen_street_lighting/{z}/{x}/{y}.json", + "geoJsonZoomLevel": 16, "isOsmCache": false }, "calculatedTags": [ diff --git a/langs/themes/en.json b/langs/themes/en.json index 4d0e46575..d6560316e 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -1228,7 +1228,7 @@ "title": "Sport pitches" }, "street_lighting": { - "description": "A theme showing information about street lamps, like light type, mounting, light colour and much more.", + "description": "On this map you can find everything about street lighting", "layers": { "1": { "name": "Lit streets", diff --git a/langs/themes/nl.json b/langs/themes/nl.json index 5848cc0a5..3d2097f3e 100644 --- a/langs/themes/nl.json +++ b/langs/themes/nl.json @@ -898,7 +898,7 @@ "title": "Sportvelden" }, "street_lighting": { - "description": "Een thema met details over straatlantaarns zoals verlichtingstype, montage, lichtkleur en veel meer.", + "description": "Op deze kaart vind je alles over straatlantaarns", "layers": { "1": { "name": "Verlichte straten", @@ -954,8 +954,8 @@ "title": "Straatverlichting" }, "street_lighting_assen": { - "description": "Straatverlichtingkaart met dataset Assen", - "title": "Straatverlichting Assen" + "description": "Op deze kaart vind je alles over straatlantaarns + een dataset van Assen", + "title": "Straatverlichting - Assen" }, "surveillance": { "description": "Op deze open kaart kan je bewakingscamera's vinden.", From c901c20d1d8a31b20824bfa609316d757961b0b2 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sun, 24 Oct 2021 16:08:20 +0200 Subject: [PATCH 26/95] Update icon, allow move and delete --- assets/layers/street_lamps/street_lamps.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/assets/layers/street_lamps/street_lamps.json b/assets/layers/street_lamps/street_lamps.json index 40b37e88c..26b093261 100644 --- a/assets/layers/street_lamps/street_lamps.json +++ b/assets/layers/street_lamps/street_lamps.json @@ -41,6 +41,7 @@ "badge": true } ], + "iconSize": "40,40,bottom", "presets": [ { "title": { @@ -367,5 +368,10 @@ "type": "direction" } } - ] + ], + "deletion": true, + "allowMove": { + "enableImproveAccuracy": true, + "enableRelocation": false + } } \ No newline at end of file From bb4125f5ad793bca69999940078bd0e0e5df2bd1 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Mon, 25 Oct 2021 22:33:55 +0200 Subject: [PATCH 27/95] Test with some new icons --- assets/layers/street_lamps/bent_pole_1.svg | 100 ++++++++++++ assets/layers/street_lamps/bent_pole_2.svg | 138 ++++++++++++++++ assets/layers/street_lamps/license_info.json | 24 +++ assets/layers/street_lamps/straight_pole.svg | 160 +++++++++++++++++++ assets/layers/street_lamps/street_lamps.json | 24 ++- 5 files changed, 445 insertions(+), 1 deletion(-) create mode 100644 assets/layers/street_lamps/bent_pole_1.svg create mode 100644 assets/layers/street_lamps/bent_pole_2.svg create mode 100644 assets/layers/street_lamps/straight_pole.svg diff --git a/assets/layers/street_lamps/bent_pole_1.svg b/assets/layers/street_lamps/bent_pole_1.svg new file mode 100644 index 000000000..ed33e232b --- /dev/null +++ b/assets/layers/street_lamps/bent_pole_1.svg @@ -0,0 +1,100 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/street_lamps/bent_pole_2.svg b/assets/layers/street_lamps/bent_pole_2.svg new file mode 100644 index 000000000..f41cdeaf9 --- /dev/null +++ b/assets/layers/street_lamps/bent_pole_2.svg @@ -0,0 +1,138 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/street_lamps/license_info.json b/assets/layers/street_lamps/license_info.json index d157db54d..34ca9cd83 100644 --- a/assets/layers/street_lamps/license_info.json +++ b/assets/layers/street_lamps/license_info.json @@ -1,4 +1,28 @@ [ + { + "path": "bent_pole_1.svg", + "license": "CC0", + "authors": [ + "Robin van der Linde" + ], + "sources": [] + }, + { + "path": "bent_pole_2.svg", + "license": "CC0", + "authors": [ + "Robin van der Linde" + ], + "sources": [] + }, + { + "path": "straight_pole.svg", + "license": "CC0", + "authors": [ + "Robin van der Linde" + ], + "sources": [] + }, { "path": "street_lamp.svg", "license": "CC0", diff --git a/assets/layers/street_lamps/straight_pole.svg b/assets/layers/street_lamps/straight_pole.svg new file mode 100644 index 000000000..c59f755d6 --- /dev/null +++ b/assets/layers/street_lamps/straight_pole.svg @@ -0,0 +1,160 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/street_lamps/street_lamps.json b/assets/layers/street_lamps/street_lamps.json index 26b093261..32383d616 100644 --- a/assets/layers/street_lamps/street_lamps.json +++ b/assets/layers/street_lamps/street_lamps.json @@ -23,7 +23,29 @@ } ] }, - "icon": "./assets/layers/street_lamps/street_lamp.svg", + "icon": { + "render": "./assets/layers/street_lamps/straight_pole.svg", + "mappings": [ + { + "if": { + "and": [ + "lamp_mount=bent_mast", + "light:count=1" + ] + }, + "then": "./assets/layers/street_lamps/bent_pole_1.svg" + }, + { + "if": { + "and": [ + "lamp_mount=bent_mast", + "light:count=2" + ] + }, + "then": "./assets/layers/street_lamps/bent_pole_2.svg" + } + ] + }, "iconOverlays": [ { "if": "light:colour=white", From 43240ab7fcf445f59e5aa4681fc0d55b95fbe253 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 26 Oct 2021 01:39:29 +0200 Subject: [PATCH 28/95] Reset translations --- .../bike_repair_station.json | 14 +- .../layers/defibrillator/defibrillator.json | 3 +- .../layers/drinking_water/drinking_water.json | 3 +- assets/layers/food/food.json | 6 +- .../left_right_style/left_right_style.json | 57 +- assets/layers/playground/playground.json | 3 +- assets/layers/shops/shops.json | 3 +- assets/layers/sport_pitch/sport_pitch.json | 6 +- assets/layers/waste_basket/waste_basket.json | 3 +- assets/themes/climbing/climbing.json | 112 +++- assets/themes/grb.json | 29 +- assets/themes/hailhydrant/hailhydrant.json | 85 ++- .../openwindpowermap/openwindpowermap.json | 17 + assets/themes/postboxes/postboxes.json | 55 +- assets/themes/sidewalks/sidewalks.json | 80 +-- assets/themes/uk_addresses/uk_addresses.json | 95 ++- langs/layers/de.json | 567 +++--------------- langs/layers/en.json | 149 ++--- langs/layers/fi.json | 7 - langs/layers/fr.json | 36 +- langs/layers/it.json | 44 +- langs/layers/ja.json | 4 +- langs/layers/nb_NO.json | 4 +- langs/layers/nl.json | 266 ++------ langs/layers/pt.json | 7 - langs/layers/pt_BR.json | 7 - langs/layers/ru.json | 68 ++- langs/layers/zh_Hant.json | 4 +- langs/themes/de.json | 168 ++---- langs/themes/en.json | 7 +- langs/themes/nl.json | 24 - scripts/lint.ts | 2 +- 32 files changed, 763 insertions(+), 1172 deletions(-) diff --git a/assets/layers/bike_repair_station/bike_repair_station.json b/assets/layers/bike_repair_station/bike_repair_station.json index a4e481b10..bcbc81544 100644 --- a/assets/layers/bike_repair_station/bike_repair_station.json +++ b/assets/layers/bike_repair_station/bike_repair_station.json @@ -663,8 +663,7 @@ "iconOverlays": [ { "if": "operator=De Fietsambassade Gent", - "then": "./assets/themes/cyclofix/fietsambassade_gent_logo_small.svg", - "badge": true + "then": "./assets/themes/cyclofix/fietsambassade_gent_logo_small.svg" } ], "iconSize": { @@ -762,16 +761,7 @@ "mapRendering": [ { "icon": { - "render": { - "en": "./assets/layers/bike_repair_station/repair_station.svg", - "ru": "./assets/layers/bike_repair_station/repair_station.svg", - "it": "./assets/layers/bike_repair_station/repair_station.svg", - "fi": "./assets/layers/bike_repair_station/repair_station.svg", - "fr": "./assets/layers/bike_repair_station/repair_station.svg", - "pt_BR": "./assets/layers/bike_repair_station/repair_station.svg", - "de": "./assets/layers/bike_repair_station/repair_station.svg", - "pt": "./assets/layers/bike_repair_station/repair_station.svg" - }, + "render": "./assets/layers/bike_repair_station/repair_station.svg", "mappings": [ { "if": { diff --git a/assets/layers/defibrillator/defibrillator.json b/assets/layers/defibrillator/defibrillator.json index d504eafad..699ac7446 100644 --- a/assets/layers/defibrillator/defibrillator.json +++ b/assets/layers/defibrillator/defibrillator.json @@ -568,7 +568,8 @@ "en": "./assets/layers/defibrillator/aed_checked.svg", "ru": "./assets/layers/defibrillator/aed_checked.svg", "it": "./assets/layers/defibrillator/aed_checked.svg", - "fr": "./assets/layers/defibrillator/aed_checked.svg" + "fr": "./assets/layers/defibrillator/aed_checked.svg", + "de": "./assets/layers/defibrillator/aed_checked.svg" } } ] diff --git a/assets/layers/drinking_water/drinking_water.json b/assets/layers/drinking_water/drinking_water.json index 2d37f8e8d..d2e705d9c 100644 --- a/assets/layers/drinking_water/drinking_water.json +++ b/assets/layers/drinking_water/drinking_water.json @@ -33,8 +33,7 @@ "operational_status=closed" ] }, - "then": "close:#c33", - "badge": true + "then": "close:#c33" } ], "iconSize": "40,40,bottom", diff --git a/assets/layers/food/food.json b/assets/layers/food/food.json index 64785f931..5e0e37c6d 100644 --- a/assets/layers/food/food.json +++ b/assets/layers/food/food.json @@ -36,8 +36,7 @@ "iconOverlays": [ { "if": "opening_hours~*", - "then": "isOpen", - "badge": true + "then": "isOpen" }, { "if": { @@ -48,8 +47,7 @@ }, "then": { "render": "circle:white;./assets/themes/fritures/Vegetarian-mark.svg" - }, - "badge": true + } } ], "label": { diff --git a/assets/layers/left_right_style/left_right_style.json b/assets/layers/left_right_style/left_right_style.json index 029cad2f1..f198f626a 100644 --- a/assets/layers/left_right_style/left_right_style.json +++ b/assets/layers/left_right_style/left_right_style.json @@ -1,41 +1,20 @@ { - "id": "left_right_style", - "description": "Special meta-style which will show one single line, either on the left or on the right depending on the id. This is used in the small popups with left_right roads", - "source": { - "osmTags": { - "or": [ - "id=left", - "id=right" - ] - } - }, - "mapRendering": [ - { - "width": 6, - "offset": { - "mappings": [ - { - "if": "id=left", - "then": "-5" - }, - { - "if": "id=right", - "then": "5" - } - ] - }, - "color": { - "mappings": [ - { - "if": "id=left", - "then": "#00f" - }, - { - "if": "id=right", - "then": "#f00" - } - ] - } - } - ] + "id": "left_right_style", + "description": "Special meta-style which will show one single line, either on the left or on the right depending on the id. This is used in the small popups with left_right roads", + "source": { + "osmTags": { + "or": [ + "id=left", + "id=right" + ] + } + }, + "mapRendering": [ + { + "location": [ + "point" + ] + }, + {} + ] } \ No newline at end of file diff --git a/assets/layers/playground/playground.json b/assets/layers/playground/playground.json index 9ab6b5830..f4dfc4f3e 100644 --- a/assets/layers/playground/playground.json +++ b/assets/layers/playground/playground.json @@ -488,8 +488,7 @@ "opening_hours~*" ] }, - "then": "isOpen", - "badge": true + "then": "isOpen" } ], "width": { diff --git a/assets/layers/shops/shops.json b/assets/layers/shops/shops.json index 714c9e62b..eaa2c73fe 100644 --- a/assets/layers/shops/shops.json +++ b/assets/layers/shops/shops.json @@ -296,8 +296,7 @@ "iconOverlays": [ { "if": "opening_hours~*", - "then": "isOpen", - "badge": true + "then": "isOpen" } ], "width": { diff --git a/assets/layers/sport_pitch/sport_pitch.json b/assets/layers/sport_pitch/sport_pitch.json index 30eab79e8..d5648444f 100644 --- a/assets/layers/sport_pitch/sport_pitch.json +++ b/assets/layers/sport_pitch/sport_pitch.json @@ -444,8 +444,7 @@ "opening_hours~*" ] }, - "then": "isOpen", - "badge": true + "then": "isOpen" }, { "if": { @@ -455,8 +454,7 @@ "access=no" ] }, - "then": "circle:white;./assets/layers/sport_pitch/lock.svg", - "badge": true + "then": "circle:white;./assets/layers/sport_pitch/lock.svg" } ], "width": { diff --git a/assets/layers/waste_basket/waste_basket.json b/assets/layers/waste_basket/waste_basket.json index cc2bfbf2c..8f4c53e3a 100644 --- a/assets/layers/waste_basket/waste_basket.json +++ b/assets/layers/waste_basket/waste_basket.json @@ -204,7 +204,8 @@ "then": { "en": "Waste Basket", "nl": "Vuilnisbak", - "ru": "Контейнер для мусора" + "ru": "Контейнер для мусора", + "de": "Abfalleimer" } } ] diff --git a/assets/themes/climbing/climbing.json b/assets/themes/climbing/climbing.json index bfd84bba8..cd73e5e4b 100644 --- a/assets/themes/climbing/climbing.json +++ b/assets/themes/climbing/climbing.json @@ -156,8 +156,7 @@ "iconOverlays": [ { "if": "opening_hours~*", - "then": "isOpen", - "badge": true + "then": "isOpen" } ], "width": { @@ -215,7 +214,26 @@ } } ], - "wayHandling": 1 + "wayHandling": 1, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/climbing/club.svg" + }, + "iconBadges": [ + { + "if": "opening_hours~*", + "then": "isOpen" + } + ], + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } + ] }, { "id": "climbing_gym", @@ -305,15 +323,33 @@ "iconOverlays": [ { "if": "opening_hours~*", - "then": "isOpen", - "badge": true + "then": "isOpen" } ], "width": "0", "iconSize": { "render": "40,40,center" }, - "wayHandling": 1 + "wayHandling": 1, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/climbing/climbing_gym.svg" + }, + "iconBadges": [ + { + "if": "opening_hours~*", + "then": "isOpen" + } + ], + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } + ] }, { "id": "climbing_route", @@ -532,7 +568,29 @@ ] } ], - "wayHandling": 2 + "wayHandling": 2, + "mapRendering": [ + { + "icon": { + "render": "circle:white;./assets/themes/climbing/climbing_route.svg" + }, + "iconSize": { + "render": "28,28,center" + }, + "location": [ + "point", + "centroid" + ] + }, + { + "color": { + "render": "#0f0" + }, + "width": { + "render": "4" + } + } + ] }, { "id": "climbing", @@ -801,6 +859,28 @@ "_difficulty_hist=JSON.parse(feat.properties._contained_climbing_routes_properties ?? '[]').map(p => p['climbing:grade:french'])", "_length_hist=JSON.parse(feat.properties._contained_climbing_routes_properties ?? '[]').map(p => p['climbing:length'])", "_contained_climbing_routes_count=JSON.parse(feat.properties._contained_climbing_routes_properties ?? '[]').length" + ], + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/climbing/climbing_no_rope.svg" + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point", + "centroid" + ] + }, + { + "color": { + "render": "#d38d5fAA" + }, + "width": { + "render": "8" + } + } ] }, { @@ -930,7 +1010,23 @@ "color": { "render": "#ddff55AA" }, - "wayHandling": 0 + "wayHandling": 0, + "mapRendering": [ + { + "icon": "./assets/themes/climbing/climbing_unknown.svg", + "location": [ + "point" + ] + }, + { + "color": { + "render": "#ddff55AA" + }, + "width": { + "render": "2" + } + } + ] } ], "overrideAll": { diff --git a/assets/themes/grb.json b/assets/themes/grb.json index e9e909652..fe0a3ed2f 100644 --- a/assets/themes/grb.json +++ b/assets/themes/grb.json @@ -196,7 +196,34 @@ "render": "#00f" }, "wayHandling": 2, - "presets": [] + "presets": [], + "mapRendering": [ + { + "label": { + "mappings": [ + { + "if": "addr:housenumber~*", + "then": "
{addr:housenumber}
" + } + ] + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point", + "centroid" + ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "2" + } + } + ] } ], "hideFromOverview": true, diff --git a/assets/themes/hailhydrant/hailhydrant.json b/assets/themes/hailhydrant/hailhydrant.json index 92f54e50f..9b79f8440 100644 --- a/assets/themes/hailhydrant/hailhydrant.json +++ b/assets/themes/hailhydrant/hailhydrant.json @@ -340,7 +340,29 @@ } } ], - "wayHandling": 2 + "wayHandling": 2, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/hailhydrant/hydrant.svg" + }, + "iconSize": { + "render": "20,20,center" + }, + "location": [ + "point", + "centroid" + ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "8" + } + } + ] }, { "id": "extinguisher", @@ -466,7 +488,20 @@ } } ], - "wayHandling": 1 + "wayHandling": 1, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/hailhydrant/Twemoji12_1f9ef.svg" + }, + "iconSize": { + "render": "20,20,center" + }, + "location": [ + "point" + ] + } + ] }, { "id": "fire_stations", @@ -695,6 +730,28 @@ "de": "Eine Feuerwache ist ein Ort, an dem die Feuerwehrfahrzeuge und die Feuerwehrleute untergebracht sind, wenn sie nicht im Einsatz sind." } } + ], + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/hailhydrant/Twemoji12_1f692.svg" + }, + "iconSize": { + "render": "35,35,center" + }, + "location": [ + "point", + "centroid" + ] + }, + { + "color": { + "render": "#c22" + }, + "width": { + "render": "1" + } + } ] }, { @@ -897,7 +954,29 @@ } } ], - "wayHandling": 2 + "wayHandling": 2, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/hailhydrant/Twemoji_1f691.svg" + }, + "iconSize": { + "render": "35,35,center" + }, + "location": [ + "point", + "centroid" + ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "1" + } + } + ] } ], "defaultBackgroundId": "HDM_HOT" diff --git a/assets/themes/openwindpowermap/openwindpowermap.json b/assets/themes/openwindpowermap/openwindpowermap.json index 63c957c57..394f28418 100644 --- a/assets/themes/openwindpowermap/openwindpowermap.json +++ b/assets/themes/openwindpowermap/openwindpowermap.json @@ -230,6 +230,23 @@ } ] } + ], + "mapRendering": [ + { + "icon": "./assets/themes/openwindpowermap/wind_turbine.svg", + "label": { + "mappings": [ + { + "if": "generator:output:electricity~^[0-9]+.*[W]$", + "then": "
{generator:output:electricity}
" + } + ] + }, + "iconSize": "40, 40, bottom", + "location": [ + "point" + ] + } ] } ], diff --git a/assets/themes/postboxes/postboxes.json b/assets/themes/postboxes/postboxes.json index 680770b35..520f05d20 100644 --- a/assets/themes/postboxes/postboxes.json +++ b/assets/themes/postboxes/postboxes.json @@ -82,7 +82,29 @@ "razed:amenity=post_box" ] } - } + }, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/postboxes/postbox.svg" + }, + "iconSize": { + "render": "40,40,bottom" + }, + "location": [ + "point", + "centroid" + ] + }, + { + "color": { + "render": "#DADADA" + }, + "width": { + "render": "1" + } + } + ] }, { "id": "postoffices", @@ -137,8 +159,7 @@ "iconOverlays": [ { "if": "opening_hours~*", - "then": "isOpen", - "badge": true + "then": "isOpen" } ], "width": { @@ -173,6 +194,34 @@ } ] } + ], + "mapRendering": [ + { + "icon": { + "render": "square:white;./assets/themes/postboxes/post_office.svg" + }, + "iconBadges": [ + { + "if": "opening_hours~*", + "then": "isOpen" + } + ], + "iconSize": { + "render": "40,40,bottom" + }, + "location": [ + "point", + "centroid" + ] + }, + { + "color": { + "render": "#DADADA" + }, + "width": { + "render": "1" + } + } ] } ] diff --git a/assets/themes/sidewalks/sidewalks.json b/assets/themes/sidewalks/sidewalks.json index 6167c6acc..59c29a952 100644 --- a/assets/themes/sidewalks/sidewalks.json +++ b/assets/themes/sidewalks/sidewalks.json @@ -45,54 +45,62 @@ "en": "Layer showing sidewalks of highways" }, "tagRenderings": [ - { "id": "streetname", "render": { "en": "This street is named {name}" } }, - { - "rewrite": { - "sourceString": "left|right", - "into": ["left","right"] - }, - "renderings": [ - { - "id": "sidewalk_minimap", - "render": "{sided_minimap(left|right):height:3rem;border-radius:0.5rem;overflow:hidden}" + "rewrite": { + "sourceString": "left|right", + "into": [ + "left", + "right" + ] }, - { - "id": "has_sidewalk", - "question": "Is there a sidewalk on the left|right side of the road?", - "mappings": [{ - "if": "sidewalk:left|right=yes", - "then": "Yes, there is a sidewalk on this side of the road" + "renderings": [ + { + "id": "sidewalk_minimap", + "render": "{sided_minimap(left|right):height:3rem;border-radius:0.5rem;overflow:hidden}" }, - { - "if": "sidewalk:left|right=no", - "then": "No, there is no seperated sidewalk to walk on" - }] - }, - { - "id": "sidewalk_width", - "question": "What is the width of the sidewalk on this side of the road?", - "render": "This sidewalk is {sidewalk:left|right:width}m wide", - "condition": "sidewalk:left|right=yes", - "freeform": { - "key": "sidewalk:left|right:width", - "type": "length", - "helperArgs": ["21", "map"] + { + "id": "has_sidewalk", + "question": "Is there a sidewalk on the left|right side of the road?", + "mappings": [ + { + "if": "sidewalk:left|right=yes", + "then": "Yes, there is a sidewalk on this side of the road" + }, + { + "if": "sidewalk:left|right=no", + "then": "No, there is no seperated sidewalk to walk on" + } + ] + }, + { + "id": "sidewalk_width", + "question": "What is the width of the sidewalk on this side of the road?", + "render": "This sidewalk is {sidewalk:left|right:width}m wide", + "condition": "sidewalk:left|right=yes", + "freeform": { + "key": "sidewalk:left|right:width", + "type": "length", + "helperArgs": [ + "21", + "map" + ] + } } - } - ] - - - }], + ] + } + ], "mapRendering": [ { - "location": ["start","end"], + "location": [ + "start", + "end" + ], "icon": "circle:#ccc", "iconSize": "20,20,center" }, diff --git a/assets/themes/uk_addresses/uk_addresses.json b/assets/themes/uk_addresses/uk_addresses.json index ff865b914..5fae7d09c 100644 --- a/assets/themes/uk_addresses/uk_addresses.json +++ b/assets/themes/uk_addresses/uk_addresses.json @@ -112,6 +112,29 @@ } ] } + ], + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/uk_addresses/housenumber_unknown.svg", + "mappings": [ + { + "if": "_embedding_object:id~*", + "then": "./assets/themes/uk_addresses/housenumber_unknown_small.svg" + }, + { + "if": "_imported=yes", + "then": "./assets/themes/uk_addresses/housenumber_unknown_small.svg" + } + ] + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } ] }, { @@ -267,7 +290,60 @@ "then": "#ff0" } ] - } + }, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/uk_addresses/housenumber_ok.svg", + "mappings": [ + { + "if": { + "or": [ + { + "and": [ + "addr:housenumber=", + "nohousenumber!=yes" + ] + }, + "addr:street=" + ] + }, + "then": "./assets/themes/uk_addresses/housenumber_unknown.svg" + } + ] + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + }, + { + "color": { + "render": "#00f", + "mappings": [ + { + "if": { + "or": [ + { + "and": [ + "addr:housenumber=", + "nohousenumber!=yes" + ] + }, + "addr:street=" + ] + }, + "then": "#ff0" + } + ] + }, + "width": { + "render": "8" + } + } + ] }, { "id": "named_streets", @@ -285,7 +361,22 @@ }, "width": { "render": "0" - } + }, + "mapRendering": [ + { + "location": [ + "point" + ] + }, + { + "color": { + "render": "#ccc" + }, + "width": { + "render": "0" + } + } + ] } ] } \ No newline at end of file diff --git a/langs/layers/de.json b/langs/layers/de.json index c67eac7ea..a73b9ac7f 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -684,13 +684,6 @@ } }, "render": "Fahrradstation (Pumpe & Reparatur)" - }, - "mapRendering": { - "0": { - "icon": { - "render": "./assets/layers/bike_repair_station/repair_station.svg" - } - } } }, "bike_shop": { @@ -951,12 +944,6 @@ "options": { "0": { "question": "Alle Anschlüsse" - }, - "3": { - "question": "Hat einen
Chademo
Stecker" - }, - "7": { - "question": "Hat einen
Tesla Supercharger
Stecker" } } } @@ -965,6 +952,9 @@ "presets": { "0": { "title": "Ladestation" + }, + "3": { + "title": "Ladestation" } }, "tagRenderings": { @@ -996,7 +986,7 @@ "then": "Authentifizierung per Debitkarte ist möglich" }, "7": { - "then": "Das Aufladen ist hier (auch) ohne Authentifizierung möglich" + "then": "Keine Authentifizierung erforderlich" } }, "question": "Welche Authentifizierung ist an der Ladestation möglich?" @@ -1061,70 +1051,22 @@ }, "question": "Muss man beim Laden eine Parkgebühr bezahlen?" }, - "Type": { + "fee/charge": { "mappings": { "0": { - "then": "Fahrräder können hier geladen werden" - }, - "1": { - "then": "Autos können hier geladen werden" - }, - "2": { - "then": " Roller können hier geladen werden" - }, - "3": { - "then": "Lastkraftwagen (LKW) können hier geladen werden" - }, - "4": { - "then": "Busse können hier geladen werden" + "then": "Nutzung kostenlos" } }, - "question": "Welche Fahrzeuge dürfen hier geladen werden?" - }, - "access": { - "question": "Wer darf diese Ladestation benutzen?", - "render": "Zugang ist {access}" - }, - "capacity": { - "question": "Wie viele Fahrzeuge können hier gleichzeitig geladen werden?", - "render": "{capacity} Fahrzeuge können hier gleichzeitig geladen werden" + "render": "Die Nutzung dieser Ladestation kostet {charge}" }, "maxstay": { - "render": "Die maximale Parkzeit beträgt {canonical(maxstay)}", - "question": "Was ist die Höchstdauer des Aufenthalts hier?", - "mappings": { - "0": { - "then": "Keine Höchstparkdauer" - } - } + "render": "Die maximale Parkzeit beträgt {canonical(maxstay)}" }, "ref": { - "render": "Die Kennziffer ist {ref}", - "question": "Wie lautet die Kennung dieser Ladestation?" + "render": "Die Kennziffer ist {ref}" }, "website": { - "render": "Weitere Informationen auf {website}", - "question": "Wie ist die Webseite des Betreibers?" - }, - "phone": { - "question": "Welche Nummer kann man anrufen, wenn es ein Problem mit dieser Ladestation gibt?", - "render": "Bei Problemen, anrufen unter {phone}" - }, - "payment-options": { - "override": { - "mappings+": { - "0": { - "then": "Bezahlung mit einer speziellen App" - }, - "1": { - "then": "Bezahlung mit einer Mitgliedskarte" - } - } - } - }, - "email": { - "question": "Wie ist die Email-Adresse des Betreibers?", - "render": "Bei Problemen senden Sie eine E-Mail an {email}" + "render": "Weitere Informationen auf {website}" } }, "title": { @@ -1226,53 +1168,6 @@ } }, "question": "Was ist das für eine Kreuzung?" - }, - "crossing-right-turn-through-red": { - "mappings": { - "0": { - "then": "Ein Radfahrer kann bei roter Ampel rechts abbiegen " - }, - "1": { - "then": "Ein Radfahrer kann bei roter Ampel rechts abbiegen" - }, - "2": { - "then": "Ein Radfahrer kann bei roter Ampel nicht rechts abbiegen" - } - }, - "question": "Kann ein Radfahrer bei roter Ampel rechts abbiegen?" - }, - "crossing-has-island": { - "mappings": { - "0": { - "then": "Der Übergang hat eine Verkehrsinsel" - }, - "1": { - "then": "Diese Ampel hat eine Taste, um ein grünes Signal anzufordern" - } - }, - "question": "Gibt es an diesem Übergang eine Verkehrsinsel?" - }, - "crossing-continue-through-red": { - "mappings": { - "0": { - "then": "Ein Radfahrer kann bei roter Ampel geradeaus fahren " - }, - "1": { - "then": "Ein Radfahrer kann bei roter Ampel geradeaus fahren" - }, - "2": { - "then": "Ein Radfahrer kann bei roter Ampel nicht geradeaus fahren" - } - }, - "question": "Kann ein Radfahrer bei roter Ampel geradeaus fahren?" - }, - "crossing-button": { - "mappings": { - "1": { - "then": "Diese Ampel hat keine Taste, um ein grünes Signal anzufordern." - } - }, - "question": "Hat diese Ampel eine Taste, um ein grünes Signal anzufordern?" } }, "title": { @@ -1292,15 +1187,6 @@ "tagRenderings": { "Cycleway type for a road": { "mappings": { - "0": { - "then": "Es gibt eine geteilte Fahrspur" - }, - "1": { - "then": "Es gibt eine Spur neben der Straße (getrennt durch eine Straßenmarkierung)" - }, - "2": { - "then": "Es gibt einen Weg, aber keinen Radweg, der auf der Karte getrennt von dieser Straße eingezeichnet ist." - }, "3": { "then": "Hier ist ein getrennter Radweg vorhanden" }, @@ -1321,18 +1207,6 @@ "1": { "then": "Geeignet für dünne Reifen: Rennrad" }, - "2": { - "then": "Geeignet für normale Reifen: Fahrrad, Rollstuhl, Scooter" - }, - "3": { - "then": "Geeignet für breite Reifen: Trekkingfahrrad, Auto, Rikscha" - }, - "4": { - "then": "Geeignet für Fahrzeuge mit großer Bodenfreiheit: leichte Geländewagen" - }, - "5": { - "then": "Geeignet für Geländefahrzeuge: schwerer Geländewagen" - }, "6": { "then": "Geeignet für Geländefahrzeuge: Traktor, ATV" }, @@ -1353,21 +1227,9 @@ "2": { "then": "Der Radweg ist aus Asphalt" }, - "3": { - "then": "Dieser Fahrradweg besteht aus ebenen Pflastersteinen" - }, "4": { "then": "Der Radweg ist aus Beton" }, - "5": { - "then": "Dieser Radweg besteht aus Kopfsteinpflaster" - }, - "6": { - "then": "Dieser Fahrradweg besteht aus unregelmäßigem, unbehauenem Kopfsteinpflaster" - }, - "7": { - "then": "Dieser Fahrradweg besteht aus regelmäßigem, behauenem Kopfsteinpflaster" - }, "8": { "then": "Der Radweg ist aus Holz" }, @@ -1384,14 +1246,10 @@ "then": "Dieser Radweg besteht aus Rohboden" } }, - "render": "Der Radweg ist aus {cycleway:surface}", - "question": "Was ist der Belag dieses Radwegs?" + "render": "Der Radweg ist aus {cycleway:surface}" }, "Is this a cyclestreet? (For a road)": { "mappings": { - "0": { - "then": "Dies ist eine Fahrradstraße in einer 30km/h Zone." - }, "1": { "then": "Dies ist eine Fahrradstraße" }, @@ -1418,36 +1276,19 @@ "4": { "then": "Die Höchstgeschwindigkeit ist 90 km/h" } - }, - "render": "Die Höchstgeschwindigkeit auf dieser Straße beträgt {maxspeed} km/h", - "question": "Was ist die Höchstgeschwindigkeit auf dieser Straße?" + } }, "Surface of the road": { "mappings": { - "0": { - "then": "Dieser Radweg ist nicht befestigt" - }, "1": { "then": "Dieser Radweg hat einen festen Belag" }, "2": { "then": "Der Radweg ist aus Asphalt" }, - "3": { - "then": "Dieser Fahrradweg besteht aus ebenen Pflastersteinen" - }, "4": { "then": "Der Radweg ist aus Beton" }, - "5": { - "then": "Dieser Radweg besteht aus Kopfsteinpflaster" - }, - "6": { - "then": "Dieser Fahrradweg besteht aus unregelmäßigem, unbehauenem Kopfsteinpflaster" - }, - "7": { - "then": "Dieser Fahrradweg besteht aus regelmäßigem, behauenem Kopfsteinpflaster" - }, "8": { "then": "Der Radweg ist aus Holz" }, @@ -1464,8 +1305,7 @@ "then": "Dieser Radweg besteht aus Rohboden" } }, - "render": "Der Radweg ist aus {surface}", - "question": "Was ist der Belag dieser Straße?" + "render": "Der Radweg ist aus {surface}" }, "Surface of the street": { "mappings": { @@ -1475,18 +1315,6 @@ "1": { "then": "Geeignet für dünne Reifen: Rennrad" }, - "2": { - "then": "Geeignet für normale Reifen: Fahrrad, Rollstuhl, Scooter" - }, - "3": { - "then": "Geeignet für breite Reifen: Trekkingfahrrad, Auto, Rikscha" - }, - "4": { - "then": "Geeignet für Fahrzeuge mit großer Bodenfreiheit: leichte Geländewagen" - }, - "5": { - "then": "Geeignet für Geländefahrzeuge: schwerer Geländewagen" - }, "6": { "then": "Geeignet für spezielle Geländewagen: Traktor, ATV" }, @@ -1498,29 +1326,16 @@ }, "cyclelan-segregation": { "mappings": { - "0": { - "then": "Der Radweg ist abgegrenzt durch eine gestrichelte Linie" - }, - "1": { - "then": "Der Radweg ist abgegrenzt durch eine durchgezogene Linie" - }, - "2": { - "then": "Der Radweg ist abgegrenzt durch eine Parkspur" - }, "3": { "then": "Dieser Radweg ist getrennt durch einen Bordstein" } - }, - "question": "Wie ist der Radweg von der Straße abgegrenzt?" + } }, "cycleway-lane-track-traffic-signs": { "mappings": { "0": { "then": "Vorgeschriebener Radweg " }, - "1": { - "then": "Vorgeschriebener Radweg (mit Zusatzschild)
" - }, "2": { "then": "Getrennter Fuß-/Radweg " }, @@ -1535,29 +1350,16 @@ }, "cycleway-segregation": { "mappings": { - "0": { - "then": "Der Radweg ist abgegrenzt durch eine gestrichelte Linie" - }, - "1": { - "then": "Der Radweg ist abgegrenzt durch eine durchgezogene Linie" - }, - "2": { - "then": "Der Radweg ist abgegrenzt durch eine Parkspur" - }, "3": { "then": "Dieser Radweg ist getrennt durch einen Bordstein" } - }, - "question": "Wie ist der Radweg von der Straße abgegrenzt?" + } }, "cycleway-traffic-signs": { "mappings": { "0": { "then": "Vorgeschriebener Radweg " }, - "1": { - "then": "Vorgeschriebener Radweg (mit Zusatzschild)
" - }, "2": { "then": "Getrennter Fuß-/Radweg " }, @@ -1589,9 +1391,6 @@ }, "is lit?": { "mappings": { - "0": { - "then": "Diese Straße ist beleuchtet" - }, "1": { "then": "Diese Straße ist nicht beleuchtet" }, @@ -1603,10 +1402,6 @@ } }, "question": "Ist diese Straße beleuchtet?" - }, - "cycleways_and_roads-cycleway:buffer": { - "question": "Wie breit ist der Abstand zwischen Radweg und Straße?", - "render": "Der Sicherheitsabstand zu diesem Radweg beträgt {cycleway:buffer} m" } }, "title": { @@ -1628,6 +1423,24 @@ } }, "defibrillator": { + "icon": { + "mappings": { + "0": { + "then": "./assets/layers/defibrillator/aed_checked.svg" + } + } + }, + "mapRendering": { + "0": { + "icon": { + "mappings": { + "0": { + "then": "./assets/layers/defibrillator/aed_checked.svg" + } + } + } + } + }, "name": "Defibrillatoren", "presets": { "0": { @@ -1665,8 +1478,7 @@ "then": "Dies ist ein normaler automatischer Defibrillator" } }, - "render": "Es gibt keine Informationen über den Gerätetyp", - "question": "Ist dies ein normaler automatischer Defibrillator oder ein manueller Defibrillator nur für Profis?" + "render": "Es gibt keine Informationen über den Gerätetyp" }, "defibrillator-defibrillator:location": { "question": "Bitte geben Sie einige Erläuterungen dazu, wo der Defibrillator zu finden ist (in der lokalen Sprache)", @@ -1754,7 +1566,7 @@ "name": "Trinkwasserstelle", "presets": { "0": { - "title": "Trinkwasserstelle" + "title": "trinkwasser" } }, "tagRenderings": { @@ -1771,9 +1583,6 @@ }, "Still in use?": { "mappings": { - "0": { - "then": "Diese Trinkwasserstelle funktioniert" - }, "1": { "then": "Diese Trinkwasserstelle ist kaputt" }, @@ -1785,7 +1594,7 @@ "render": "Der Betriebsstatus ist {operational_status" }, "render-closest-drinking-water": { - "render": "Eine weitere Trinkwasserstelle liegt {_closest_other_drinking_water_distance} Meter entfernt" + "render": "Ein weiterer Trinkwasserbrunnen befindet sich in {_closest_other_drinking_water_distance} Meter" } }, "title": { @@ -1796,17 +1605,7 @@ "description": "Alle Objekte, die eine bekannte Namensherkunft haben", "tagRenderings": { "simple etymology": { - "render": "Benannt nach {name:etymology}", - "question": "Wonach ist dieses Objekt benannt?
Das könnte auf einem Straßenschild stehen", - "mappings": { - "0": { - "then": "Der Ursprung dieses Namens ist in der gesamten Literatur unbekannt" - } - } - }, - "wikipedia-etymology": { - "question": "Was ist das Wikidata-Element, nach dem dieses Objekt benannt ist?", - "render": "

Wikipedia Artikel zur Namensherkunft

{wikipedia(name:etymology:wikidata):max-height:20rem}" + "render": "Benannt nach {name:etymology}" } } }, @@ -1819,32 +1618,18 @@ } } }, - "1": { - "options": { - "0": { - "question": "Hat vegetarische Speisen" - } - } - }, "2": { "options": { "0": { "question": "Bietet vegan Speisen an" } } - }, - "3": { - "options": { - "0": { - "question": "Hat halal Speisen" - } - } } }, + "name": "Restaurants und Fast Food", "presets": { "0": { - "title": "Restaurant", - "description": "Ein klassisches Speiselokal mit Sitzgelegenheiten, in dem vollständige Mahlzeiten von Kellnern serviert werden" + "title": "Restaurant" }, "1": { "title": "Schnellimbiss" @@ -1856,12 +1641,6 @@ "tagRenderings": { "Cuisine": { "mappings": { - "0": { - "then": "Dies ist eine Pizzeria" - }, - "1": { - "then": "Dies ist eine Pommesbude" - }, "2": { "then": "Bietet vorwiegend Pastagerichte an" } @@ -1904,19 +1683,7 @@ "3": { "then": "Es gibt ausschließlich halal Speisen" } - }, - "question": "Gibt es im das Restaurant halal Speisen?" - }, - "friture-take-your-container": { - "mappings": { - "0": { - "then": "Sie können ihre eigenen Behälter mitbringen, um Ihre Bestellung zu erhalten, was Einwegverpackungsmaterial und damit Abfall spart" - } - }, - "question": "Wenn Sie Ihr eigenes Behältnis mitbringen (z. B. einen Kochtopf und kleine Töpfe), wird es dann zum Verpacken Ihrer Bestellung verwendet?
" - }, - "Vegetarian (no friture)": { - "question": "Gibt es im das Restaurant vegetarische Speisen?" + } } }, "title": { @@ -1928,8 +1695,7 @@ "then": "Schnellrestaurant{name}" } } - }, - "name": "Restaurants und Fast Food" + } }, "ghost_bike": { "name": "Geisterräder", @@ -1940,7 +1706,7 @@ }, "tagRenderings": { "ghost-bike-explanation": { - "render": "Ein Geisterrad ist ein weißes Fahrrad, dass zum Gedenken eines tödlich verunglückten Radfahrers vor Ort aufgestellt wurde." + "render": "Ein Geisterrad ist ein Denkmal für einen Radfahrer, der bei einem Verkehrsunfall ums Leben kam, in Form eines weißen Fahrrades, das dauerhaft in der Nähe des Unfallortes aufgestellt wird." }, "ghost_bike-inscription": { "question": "Wie lautet die Inschrift auf diesem Geisterrad?", @@ -1960,8 +1726,7 @@ "render": "Mehr Informationen" }, "ghost_bike-start_date": { - "question": "Wann wurde dieses Geisterrad aufgestellt?", - "render": "Aufgestellt am {start_date}" + "question": "Wann wurde dieses Geisterrad aufgestellt?" } }, "title": { @@ -2052,12 +1817,6 @@ }, "phone": { "render": "{phone}" - }, - "Surface area": { - "render": "Grundfläche: {_surface:ha}ha" - }, - "Curator": { - "question": "Wer ist der Verwalter dieses Naturschutzgebietes?
Respektieren Sie die Privatsphäre - geben Sie nur dann einen Namen an, wenn dieser allgemein bekannt ist" } } }, @@ -2076,8 +1835,7 @@ "then": "Eintritt kostenlos" } }, - "render": "Der Besuch des Turms kostet {charge}", - "question": "Was kostet der Zugang zu diesem Turm?" + "render": "Der Besuch des Turms kostet {charge}" }, "Height": { "question": "Wie hoch ist dieser Turm?", @@ -2200,12 +1958,10 @@ "question": "Ist dieser Spielplatz nachts beleuchtet?" }, "playground-max_age": { - "render": "Zugang nur für Kinder bis maximal {max_age}", - "question": "Bis zu welchem Alter dürfen Kinder auf diesem Spielplatz spielen?" + "render": "Zugang nur für Kinder bis maximal {max_age}" }, "playground-min_age": { - "render": "Zugang nur für Kinder ab {min_age} Jahren", - "question": "Ab welchem Alter dürfen Kinder auf diesem Spielplatz spielen?" + "render": "Zugang nur für Kinder ab {min_age} Jahren" }, "playground-opening_hours": { "mappings": { @@ -2226,8 +1982,7 @@ "render": "Betrieben von {operator}" }, "playground-phone": { - "render": "{phone}", - "question": "Wie lautet die Telefonnummer vom Betreiber des Spielplatzes?" + "render": "{phone}" }, "playground-surface": { "mappings": { @@ -2271,15 +2026,6 @@ }, "public_bookcase": { "description": "Ein Bücherschrank am Straßenrand mit Büchern, für jedermann zugänglich", - "filter": { - "2": { - "options": { - "0": { - "question": "Innen oder Außen" - } - } - } - }, "name": "Bücherschränke", "presets": { "0": { @@ -2383,16 +2129,16 @@ } }, "shops": { + "description": "Ein Geschäft", + "name": "Geschäft", "presets": { "0": { - "description": "Ein neues Geschäft hinzufügen", - "title": "Geschäft" + "description": "Ein neues Geschäft hinzufügen" } }, "tagRenderings": { "shops-phone": { - "render": "{phone}", - "question": "Wie ist die Telefonnummer?" + "render": "{phone}" }, "shops-shop": { "mappings": { @@ -2424,15 +2170,6 @@ "shops-website": { "question": "Wie lautet die Webseite dieses Geschäfts?", "render": "{website}" - }, - "shops-opening_hours": { - "question": "Wie sind die Öffnungszeiten dieses Geschäfts?" - }, - "shops-name": { - "question": "Wie ist der Name dieses Geschäfts?" - }, - "shops-email": { - "question": "Wie ist die Email-Adresse dieses Geschäfts?" } }, "title": { @@ -2445,41 +2182,6 @@ } }, "render": "Geschäft" - }, - "name": "Geschäft", - "description": "Ein Geschäft" - }, - "slow_roads": { - "tagRenderings": { - "slow_roads-surface": { - "mappings": { - "0": { - "then": "Die Oberfläche ist Gras" - }, - "1": { - "then": "Die Oberfläche ist Erde" - }, - "2": { - "then": "Die Oberfläche ist ohne festen Belag" - }, - "3": { - "then": "Die Oberfläche ist Sand" - }, - "4": { - "then": "Die Oberfläche ist aus Pflastersteinen" - }, - "5": { - "then": "Die Oberfläche ist Asphalt" - }, - "6": { - "then": "Die Oberfläche ist Beton" - }, - "7": { - "then": "Die Oberfläche ist gepflastert" - } - }, - "render": "Die Oberfläche ist {surface}" - } } }, "sport_pitch": { @@ -2499,9 +2201,6 @@ "0": { "then": "Öffentlicher Zugang" }, - "1": { - "then": "Eingeschränkter Zugang (z. B. nur mit Termin, zu bestimmten Zeiten, ...)" - }, "2": { "then": "Zugang nur für Vereinsmitglieder" }, @@ -2513,20 +2212,10 @@ }, "sport-pitch-reservation": { "mappings": { - "0": { - "then": "Für die Nutzung des Sportplatzes ist eine Voranmeldung erforderlich" - }, - "1": { - "then": "Für die Nutzung des Sportplatzes wird eine Voranmeldung empfohlen" - }, - "2": { - "then": "Eine Voranmeldung ist möglich, aber nicht notwendig, um diesen Sportplatz zu nutzen" - }, "3": { "then": "Termine nach Vereinbarung nicht möglich" } - }, - "question": "Muss man einen Termin vereinbaren, um diesen Sportplatz zu benutzen?" + } }, "sport_pitch-opening_hours": { "mappings": { @@ -2578,14 +2267,7 @@ "then": "Die Oberfläche ist Beton" } }, - "render": "Die Oberfläche ist {surface}", - "question": "Was ist die Oberfläche dieses Sportplatzes?" - }, - "sport_pitch-phone": { - "question": "Wie ist die Telefonnummer des Betreibers?" - }, - "sport_pitch-email": { - "question": "Wie ist die Email-Adresse des Betreibers?" + "render": "Die Oberfläche ist {surface}" } }, "title": { @@ -2620,8 +2302,7 @@ "2": { "then": "Diese Kamera ist möglicherweise im Freien" } - }, - "question": "Handelt es sich bei dem von dieser Kamera überwachten öffentlichen Raum um einen Innen- oder Außenbereich?" + } }, "Level": { "question": "Auf welcher Ebene befindet sich diese Kamera?", @@ -2657,32 +2338,7 @@ }, "camera:mount": { "question": "Wie ist diese Kamera montiert?", - "render": "Montageart: {mount}", - "mappings": { - "0": { - "then": "Diese Kamera ist an einer Wand montiert" - }, - "1": { - "then": "Diese Kamera ist an einer Stange montiert" - }, - "2": { - "then": "Diese Kamera ist an der Decke montiert" - } - } - }, - "direction. We don't ask this for a dome on a pole or ceiling as it has a 360° view": { - "question": "In welche Himmelsrichtung ist diese Kamera ausgerichtet?" - }, - "Surveillance type: public, outdoor, indoor": { - "mappings": { - "1": { - "then": "Ein privater Außenbereich wird überwacht (z. B. ein Parkplatz, eine Tankstelle, ein Innenhof, ein Eingang, eine private Einfahrt, ...)" - }, - "2": { - "then": "Ein privater Innenbereich wird überwacht, z. B. ein Geschäft, eine private Tiefgarage, ..." - } - }, - "question": "Um was für eine Überwachungskamera handelt es sich" + "render": "Montageart: {mount}" } }, "title": { @@ -2817,25 +2473,6 @@ } }, "question": "Gibt es eine Toilette für Rollstuhlfahrer?" - }, - "toilet-has-paper": { - "mappings": { - "1": { - "then": "Für diese Toilette müssen Sie Ihr eigenes Toilettenpapier mitbringen" - } - }, - "question": "Muss man für diese Toilette sein eigenes Toilettenpapier mitbringen?" - }, - "toilet-handwashing": { - "mappings": { - "0": { - "then": "Diese Toilette verfügt über ein Waschbecken" - }, - "1": { - "then": "Diese Toilette verfügt über kein Waschbecken" - } - }, - "question": "Verfügt diese Toilette über ein Waschbecken?" } }, "title": { @@ -2844,27 +2481,6 @@ }, "trail": { "name": "Wanderwege", - "tagRenderings": { - "Color": { - "mappings": { - "0": { - "then": "Blauer Weg" - }, - "1": { - "then": "Roter Weg" - }, - "2": { - "then": "Grüner Weg" - }, - "3": { - "then": "Gelber Weg" - } - } - }, - "trail-length": { - "render": "Der Wanderweg ist {_length:km} Kilometer lang" - } - }, "title": { "render": "Wanderweg" } @@ -2888,9 +2504,6 @@ "tagRenderings": { "tree-decidouous": { "mappings": { - "0": { - "then": "Laubabwerfend: Der Baum verliert für eine gewisse Zeit des Jahres seine Blätter." - }, "1": { "then": "immergrüner Baum." } @@ -2907,12 +2520,6 @@ }, "tree-heritage": { "mappings": { - "0": { - "then": "\"\"/ Als Denkmal registriert von der Onroerend Erfgoed Flandern" - }, - "1": { - "then": "Als Denkmal registriert von der Direction du Patrimoine culturel Brüssel" - }, "3": { "then": "Nicht als Denkmal registriert" } @@ -2930,8 +2537,7 @@ "2": { "then": "\"\"/ Dauerhaft blattlos" } - }, - "question": "Ist dies ein Laub- oder Nadelbaum?" + } }, "tree_node-name": { "mappings": { @@ -2941,35 +2547,6 @@ }, "question": "Hat der Baum einen Namen?", "render": "Name: {name}" - }, - "tree_node-wikidata": { - "question": "Was ist das passende Wikidata Element zu diesem Baum?" - }, - "tree_node-ref:OnroerendErfgoed": { - "question": "Wie lautet die Kennung der Onroerend Erfgoed Flanders?" - }, - "tree-denotation": { - "mappings": { - "0": { - "then": "Der Baum ist aufgrund seiner Größe oder seiner markanten Lage bedeutsam. Er ist nützlich zur Orientierung." - }, - "1": { - "then": "Der Baum ist ein Naturdenkmal, z. B. weil er besonders alt ist oder zu einer wertvollen Art gehört." - }, - "2": { - "then": "Der Baum wird für landwirtschaftliche Zwecke genutzt, z. B. in einer Obstplantage." - }, - "3": { - "then": "Der Baum steht in einem Park oder ähnlichem (Friedhof, Schulgelände, ...)." - }, - "5": { - "then": "Dieser Baum steht entlang einer Straße." - }, - "7": { - "then": "Dieser Baum steht außerhalb eines städtischen Gebiets." - } - }, - "question": "Wie bedeutsam ist dieser Baum? Wählen Sie die erste Antwort, die zutrifft." } }, "title": { @@ -2999,6 +2576,7 @@ } }, "visitor_information_centre": { + "description": "Ein Besucherzentrum bietet Informationen über eine bestimmte Attraktion oder Sehenswürdigkeit, an der es sich befindet.", "name": "Besucherinformationszentrum", "title": { "mappings": { @@ -3007,8 +2585,7 @@ } }, "render": "{name}" - }, - "description": "Ein Besucherzentrum bietet Informationen über eine bestimmte Attraktion oder Sehenswürdigkeit, an der es sich befindet." + } }, "waste_basket": { "description": "Dies ist ein öffentlicher Abfalleimer, in den Sie Ihren Müll entsorgen können.", @@ -3019,6 +2596,17 @@ } } }, + "mapRendering": { + "0": { + "iconSize": { + "mappings": { + "0": { + "then": "Abfalleimer" + } + } + } + } + }, "name": "Abfalleimer", "presets": { "0": { @@ -3043,22 +2631,7 @@ "4": { "then": "Mülleimer für Drogen" } - }, - "question": "Um was für einen Abfalleimer handelt es sich?" - }, - "dispensing_dog_bags": { - "mappings": { - "0": { - "then": "Dieser Abfalleimer verfügt über einen Spender für (Hunde-)Kotbeutel" - }, - "1": { - "then": "Dieser Abfalleimer hat keinen Spender für (Hunde-)Kotbeutel" - }, - "2": { - "then": "Dieser Abfalleimer hat keinen Spender für (Hunde-)Kotbeutel" - } - }, - "question": "Verfügt dieser Abfalleimer über einen Spender für (Hunde-)Kotbeutel?" + } } }, "title": { diff --git a/langs/layers/en.json b/langs/layers/en.json index 58544df74..4eb331c74 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -690,13 +690,6 @@ } }, "render": "Bike station (pump & repair)" - }, - "mapRendering": { - "0": { - "icon": { - "render": "./assets/layers/bike_repair_station/repair_station.svg" - } - } } }, "bike_shop": { @@ -1021,7 +1014,7 @@ "title": "charging station for cars" }, "3": { - "title": "charging station" + "title": "Charging station" } }, "tagRenderings": { @@ -1118,52 +1111,23 @@ }, "question": "Does one have to pay a parking fee while charging?" }, - "Type": { - "mappings": { - "0": { - "then": "bicycles can be charged here" - }, - "1": { - "then": "Cars can be charged here" - }, - "2": { - "then": "Scooters can be charged here" - }, - "3": { - "then": "Heavy good vehicles (such as trucks) can be charged here" - }, - "4": { - "then": "Buses can be charged here" - } - }, - "question": "Which vehicles are allowed to charge here?" - }, - "access": { - "question": "Who is allowed to use this charging station?", - "render": "Access is {access}", - "mappings": { - "0": { - "then": "Anyone can use this charging station (payment might be needed)" - }, - "1": { - "then": "Anyone can use this charging station (payment might be needed)" - }, - "2": { - "then": "Only customers of the place this station belongs to can use this charging station
E.g. a charging station operated by hotel which is only usable by their guests" - }, - "3": { - "then": "Not accessible to the general public (e.g. only accessible to the owners, employees, ...)" - } - } - }, - "capacity": { - "question": "How much vehicles can be charged here at the same time?", - "render": "{capacity} vehicles can be charged here at the same time" + "current-15": { + "question": "What current do the plugs with
Bosch Active Connect with 5 pins and cable
offer?", + "render": "
Bosch Active Connect with 5 pins and cable
outputs at most {socket:bosch_5pin:current}A" }, "email": { "question": "What is the email address of the operator?", "render": "In case of problems, send an email to {email}" }, + "fee/charge": { + "mappings": { + "0": { + "then": "Free to use" + } + }, + "question": "How much does one have to pay to use this charging station?", + "render": "Using this charging station costs {charge}" + }, "maxstay": { "mappings": { "0": { @@ -1189,37 +1153,25 @@ "question": "What number can one call if there is a problem with this charging station?", "render": "In case of problems, call {phone}" }, + "plugs-15": { + "question": "How much plugs of type
Bosch Active Connect with 5 pins and cable
are available here?", + "render": "There are {socket:bosch_5pin} plugs of type
Bosch Active Connect with 5 pins and cable
available here" + }, + "power-output-15": { + "question": "What power output does a single plug of type
Bosch Active Connect with 5 pins and cable
offer?", + "render": "
Bosch Active Connect with 5 pins and cable
outputs at most {socket:bosch_5pin:output}" + }, "ref": { "question": "What is the reference number of this charging station?", "render": "Reference number is {ref}" }, + "voltage-15": { + "question": "What voltage do the plugs with
Bosch Active Connect with 5 pins and cable
offer?", + "render": "
Bosch Active Connect with 5 pins and cable
outputs {socket:bosch_5pin:voltage} volt" + }, "website": { "question": "What is the website of the operator?", "render": "More info on {website}" - }, - "fee": { - "mappings": { - "0": { - "then": "Free to use" - }, - "1": { - "then": "Free to use (without authenticating)" - }, - "2": { - "then": "Free to use, but one has to authenticate" - }, - "3": { - "then": "Paid use, but free for customers of the hotel/pub/hospital/... who operates the charging station" - }, - "4": { - "then": "Paid use" - } - }, - "question": "Does one have to pay to use this charging station?" - }, - "charge": { - "question": "How much does one have to pay to use this charging station?", - "render": "Using this charging station costs {charge}" } }, "title": { @@ -1778,6 +1730,24 @@ } }, "defibrillator": { + "icon": { + "mappings": { + "0": { + "then": "./assets/layers/defibrillator/aed_checked.svg" + } + } + }, + "mapRendering": { + "0": { + "icon": { + "mappings": { + "0": { + "then": "./assets/layers/defibrillator/aed_checked.svg" + } + } + } + } + }, "name": "Defibrillators", "presets": { "0": { @@ -1894,17 +1864,6 @@ }, "title": { "render": "Defibrillator" - }, - "mapRendering": { - "0": { - "icon": { - "mappings": { - "0": { - "then": "./assets/layers/defibrillator/aed_checked.svg" - } - } - } - } } }, "direction": { @@ -3228,6 +3187,17 @@ } } }, + "mapRendering": { + "0": { + "iconSize": { + "mappings": { + "0": { + "then": "Waste Basket" + } + } + } + } + }, "name": "Waste Basket", "presets": { "0": { @@ -3275,17 +3245,6 @@ }, "title": { "render": "Waste Basket" - }, - "mapRendering": { - "0": { - "iconSize": { - "mappings": { - "0": { - "then": "Waste Basket" - } - } - } - } } }, "watermill": { diff --git a/langs/layers/fi.json b/langs/layers/fi.json index 75369382b..91972fd66 100644 --- a/langs/layers/fi.json +++ b/langs/layers/fi.json @@ -94,13 +94,6 @@ "0": { "title": "Pyöräpumppu" } - }, - "mapRendering": { - "0": { - "icon": { - "render": "./assets/layers/bike_repair_station/repair_station.svg" - } - } } }, "ghost_bike": { diff --git a/langs/layers/fr.json b/langs/layers/fr.json index 48a307bbb..13055833a 100644 --- a/langs/layers/fr.json +++ b/langs/layers/fr.json @@ -590,13 +590,6 @@ } }, "render": "Point station velo avec pompe" - }, - "mapRendering": { - "0": { - "icon": { - "render": "./assets/layers/bike_repair_station/repair_station.svg" - } - } } }, "bike_shop": { @@ -753,6 +746,24 @@ } }, "defibrillator": { + "icon": { + "mappings": { + "0": { + "then": "./assets/layers/defibrillator/aed_checked.svg" + } + } + }, + "mapRendering": { + "0": { + "icon": { + "mappings": { + "0": { + "then": "./assets/layers/defibrillator/aed_checked.svg" + } + } + } + } + }, "name": "Défibrillateurs", "presets": { "0": { @@ -869,17 +880,6 @@ }, "title": { "render": "Défibrillateur" - }, - "mapRendering": { - "0": { - "icon": { - "mappings": { - "0": { - "then": "./assets/layers/defibrillator/aed_checked.svg" - } - } - } - } } }, "direction": { diff --git a/langs/layers/it.json b/langs/layers/it.json index 6833d85e5..fb09fe146 100644 --- a/langs/layers/it.json +++ b/langs/layers/it.json @@ -590,13 +590,6 @@ } }, "render": "Stazione bici (gonfiaggio & riparazione)" - }, - "mapRendering": { - "0": { - "icon": { - "render": "./assets/layers/bike_repair_station/repair_station.svg" - } - } } }, "bike_shop": { @@ -756,12 +749,12 @@ "description": "Una stazione di ricarica", "name": "Stazioni di ricarica", "tagRenderings": { - "OH": { - "question": "Quali sono gli orari di apertura di questa stazione di ricarica?" - }, - "Network": { + "Auth phone": { "question": "A quale rete appartiene questa stazione di ricarica?", "render": "{network}" + }, + "Authentication": { + "question": "Quali sono gli orari di apertura di questa stazione di ricarica?" } }, "title": { @@ -769,6 +762,24 @@ } }, "defibrillator": { + "icon": { + "mappings": { + "0": { + "then": "./assets/layers/defibrillator/aed_checked.svg" + } + } + }, + "mapRendering": { + "0": { + "icon": { + "mappings": { + "0": { + "then": "./assets/layers/defibrillator/aed_checked.svg" + } + } + } + } + }, "name": "Defibrillatori", "presets": { "0": { @@ -885,17 +896,6 @@ }, "title": { "render": "Defibrillatore" - }, - "mapRendering": { - "0": { - "icon": { - "mappings": { - "0": { - "then": "./assets/layers/defibrillator/aed_checked.svg" - } - } - } - } } }, "direction": { diff --git a/langs/layers/ja.json b/langs/layers/ja.json index b3385cb20..5c5b0d1d6 100644 --- a/langs/layers/ja.json +++ b/langs/layers/ja.json @@ -76,11 +76,11 @@ "description": "充電ステーション", "name": "充電ステーション", "tagRenderings": { - "Network": { + "Auth phone": { "question": "この充電ステーションの運営チェーンはどこですか?", "render": "{network}" }, - "OH": { + "Authentication": { "question": "この充電ステーションはいつオープンしますか?" } }, diff --git a/langs/layers/nb_NO.json b/langs/layers/nb_NO.json index 0ca42f3e8..47a02bc8e 100644 --- a/langs/layers/nb_NO.json +++ b/langs/layers/nb_NO.json @@ -174,10 +174,10 @@ "description": "En ladestasjon", "name": "Ladestasjoner", "tagRenderings": { - "Network": { + "Auth phone": { "render": "{network}" }, - "OH": { + "Authentication": { "question": "Når åpnet denne ladestasjonen?" } }, diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 5c71366f9..20850aaaa 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -896,6 +896,13 @@ "icon": { "render": "./assets/layers/birdhide/birdhide.svg" }, + "mapRendering": { + "0": { + "icon": { + "render": "./assets/layers/birdhide/birdhide.svg" + } + } + }, "name": "Vogelkijkhutten", "presets": { "0": { @@ -974,13 +981,6 @@ } }, "render": "Vogelkijkplaats" - }, - "mapRendering": { - "0": { - "icon": { - "render": "./assets/layers/birdhide/birdhide.svg" - } - } } }, "cafe_pub": { @@ -1044,6 +1044,7 @@ } }, "charging_station": { + "description": "Oplaadpunten", "filter": { "0": { "options": { @@ -1058,13 +1059,6 @@ } } }, - "1": { - "options": { - "0": { - "question": "Enkel werkende oplaadpunten" - } - } - }, "2": { "options": { "0": { @@ -1121,6 +1115,18 @@ } } }, + "name": "Oplaadpunten", + "presets": { + "0": { + "title": "gewone stekker (bedoeld om electrische fietsen op te laden)" + }, + "1": { + "title": "oplaadpunt voor elektrische fietsen" + }, + "2": { + "title": "oplaadstation voor elektrische auto's" + } + }, "tagRenderings": { "Operational status": { "mappings": { @@ -1142,9 +1148,18 @@ }, "question": "Is dit oplaadpunt operationeel?" }, - "capacity": { - "question": "Hoeveel voertuigen kunnen hier opgeladen worden?", - "render": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden" + "current-15": { + "question": "Welke stroom levert de stekker van type
Bosch Active Connect met 5 pinnen aan een kabel
?", + "render": "
Bosch Active Connect met 5 pinnen aan een kabel
levert een stroom van maximaal {socket:bosch_5pin:current}A" + }, + "fee/charge": { + "mappings": { + "0": { + "then": "Gratis te gebruiken" + } + }, + "question": "Hoeveel kost het gebruik van dit oplaadpunt?", + "render": "Dit oplaadpunt gebruiken kost {charge}" }, "maxstay": { "mappings": { @@ -1167,158 +1182,22 @@ } } }, - "website": { - "question": "Wat is de website waar men meer info kan vinden over dit oplaadpunt?", - "render": "Meer informatie op {website}" + "plugs-15": { + "question": "Hoeveel stekkers van type
Bosch Active Connect met 5 pinnen aan een kabel
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:bosch_5pin} stekkers van het type
Bosch Active Connect met 5 pinnen aan een kabel
" }, - "ref": { - "question": "Wat is het referentienummer van dit oplaadstation?", - "render": "Het referentienummer van dit oplaadpunt is {ref}" + "power-output-15": { + "question": "Welk vermogen levert een enkele stekker van type
Bosch Active Connect met 5 pinnen aan een kabel
?", + "render": "
Bosch Active Connect met 5 pinnen aan een kabel
levert een vermogen van maximaal {socket:bosch_5pin:output}" }, - "phone": { - "question": "Wat is het telefoonnummer van de beheerder van dit oplaadpunt?", - "render": "Bij problemen, bel naar {phone}" - }, - "fee": { - "mappings": { - "0": { - "then": "Gratis te gebruiken" - }, - "1": { - "then": "Gratis te gebruiken (zonder aan te melden)" - }, - "2": { - "then": "Gratis te gebruiken, maar aanmelden met een applicatie is verplicht" - }, - "3": { - "then": "Betalend te gebruiken, maar gratis voor klanten van het bijhorende hotel/café/ziekenhuis/..." - }, - "4": { - "then": "Betalend" - } - }, - "question": "Moet men betalen om dit oplaadpunt te gebruiken?" - }, - "email": { - "question": "Wat is het email-adres van de operator?", - "render": "Bij problemen, email naar {email}" - }, - "charge": { - "question": "Hoeveel moet men betalen om dit oplaadpunt te gebruiken?", - "render": "Dit oplaadpunt gebruiken kost {charge}" - }, - "access": { - "mappings": { - "0": { - "then": "Toegankelijk voor iedereen (mogelijks met aanmelden en/of te betalen)" - }, - "1": { - "then": "Toegankelijk voor iedereen (mogelijks met aanmelden en/of te betalen)" - }, - "2": { - "then": "Enkel klanten van de bijhorende plaats mogen dit oplaadpunt gebruiken
Bijvoorbeeld een oplaadpunt op de parking van een restaurant dat enkel door klanten van het restaurant gebruikt mag worden" - }, - "3": { - "then": "Niet toegankelijk voor het publiek Enkel toegankelijk voor de eigenaar, medewerkers ,... " - } - }, - "question": "Wie mag er dit oplaadpunt gebruiken?", - "render": "Toegang voor {access}" - }, - "Type": { - "mappings": { - "0": { - "then": "Fietsen kunnen hier opgeladen worden" - }, - "1": { - "then": "Elektrische auto's kunnen hier opgeladen worden" - }, - "2": { - "then": "Electrische scooters (snorfiets of bromfiets) kunnen hier opgeladen worden" - }, - "3": { - "then": "Vrachtwagens kunnen hier opgeladen worden" - }, - "4": { - "then": "Bussen kunnen hier opgeladen worden" - } - }, - "question": "Welke voertuigen kunnen hier opgeladen worden?" - }, - "Parking:fee": { - "mappings": { - "0": { - "then": "Geen extra parkeerkost tijdens het opladen" - }, - "1": { - "then": "Tijdens het opladen moet er parkeergeld betaald worden" - } - }, - "question": "Moet men parkeergeld betalen tijdens het opladen?" - }, - "Operator": { - "mappings": { - "0": { - "then": "Eigenlijk is {operator} het netwerk waarvan het deel uitmaakt" - } - }, - "question": "Wie beheert dit oplaadpunt?", - "render": "Wordt beheerd door {operator}" - }, - "OH": { - "mappings": { - "0": { - "then": "24/7 open - ook tijdens vakanties" - } - }, - "question": "Wanneer is dit oplaadpunt beschikbaar??" - }, - "Network": { - "mappings": { - "0": { - "then": "Maakt geen deel uit van een groter netwerk" - }, - "1": { - "then": "Maakt geen deel uit van een groter netwerk" - } - }, - "question": "Is dit oplaadpunt deel van een groter netwerk?", - "render": "Maakt deel uit van het {network}-netwerk" - }, - "Authentication": { - "mappings": { - "0": { - "then": "Aanmelden met een lidkaart is mogelijk" - }, - "1": { - "then": "Aanmelden via een applicatie is mogelijk" - }, - "2": { - "then": "Aanmelden door te bellen naar een telefoonnummer is mogelijk" - }, - "3": { - "then": "Aanmelden via SMS is mogelijk" - }, - "4": { - "then": "Aanmelden via NFC is mogelijk" - }, - "5": { - "then": "Aanmelden met Money Card is mogelijk" - }, - "6": { - "then": "Aanmelden met een betaalkaart is mogelijk" - }, - "7": { - "then": "Hier opladen is (ook) mogelijk zonder aan te melden" - } - }, - "question": "Hoe kan men zich aanmelden aan dit oplaadstation?" - }, - "Auth phone": { - "question": "Wat is het telefoonnummer dat men moet bellen of SMS'en om zich aan te melden?", - "render": "Aanmelden door te bellen of te SMS'en naar {authentication:phone_call:number}" + "voltage-15": { + "question": "Welke spanning levert de stekker van type
Bosch Active Connect met 5 pinnen aan een kabel
", + "render": "
Bosch Active Connect met 5 pinnen aan een kabel
heeft een spanning van {socket:bosch_5pin:voltage} volt" } }, + "title": { + "render": "Oplaadpunten" + }, "units": { "0": { "applicableUnits": { @@ -1360,26 +1239,7 @@ } } } - }, - "title": { - "render": "Oplaadpunten" - }, - "presets": { - "0": { - "title": "gewone stekker (bedoeld om electrische fietsen op te laden)" - }, - "1": { - "title": "oplaadpunt voor elektrische fietsen" - }, - "2": { - "title": "oplaadstation voor elektrische auto's" - }, - "3": { - "title": "oplaadstation" - } - }, - "name": "Oplaadpunten", - "description": "Oplaadpunten" + } }, "crossings": { "description": "Oversteekplaatsen voor voetgangers en fietsers", @@ -3641,6 +3501,17 @@ } } }, + "mapRendering": { + "0": { + "iconSize": { + "mappings": { + "0": { + "then": "Vuilnisbak" + } + } + } + } + }, "name": "Vuilnisbak", "presets": { "0": { @@ -3670,35 +3541,10 @@ } }, "question": "Wat voor soort vuilnisbak is dit?" - }, - "dispensing_dog_bags": { - "mappings": { - "0": { - "then": "Deze vuilnisbak heeft een verdeler voor hondenpoepzakjes" - }, - "1": { - "then": "Deze vuilbak heeft geen verdeler voor hondenpoepzakjes" - }, - "2": { - "then": "Deze vuilnisbak heeft geen verdeler voor hondenpoepzakjes" - } - }, - "question": "Heeft deze vuilnisbak een verdeler voor hondenpoepzakjes?" } }, "title": { "render": "Vuilnisbak" - }, - "mapRendering": { - "0": { - "iconSize": { - "mappings": { - "0": { - "then": "Vuilnisbak" - } - } - } - } } }, "watermill": { diff --git a/langs/layers/pt.json b/langs/layers/pt.json index 095ef57a7..461c8c541 100644 --- a/langs/layers/pt.json +++ b/langs/layers/pt.json @@ -425,13 +425,6 @@ "then": "Estação de reparo de bicicletas" } } - }, - "mapRendering": { - "0": { - "icon": { - "render": "./assets/layers/bike_repair_station/repair_station.svg" - } - } } }, "bike_shop": { diff --git a/langs/layers/pt_BR.json b/langs/layers/pt_BR.json index 4725b0cd2..1bea7cdf8 100644 --- a/langs/layers/pt_BR.json +++ b/langs/layers/pt_BR.json @@ -437,13 +437,6 @@ } }, "render": "Estação de bicicletas (bomba e reparo)" - }, - "mapRendering": { - "0": { - "icon": { - "render": "./assets/layers/bike_repair_station/repair_station.svg" - } - } } }, "bike_shop": { diff --git a/langs/layers/ru.json b/langs/layers/ru.json index cd4142df4..9249572a1 100644 --- a/langs/layers/ru.json +++ b/langs/layers/ru.json @@ -473,13 +473,6 @@ "then": "Велосипедный насос" } } - }, - "mapRendering": { - "0": { - "icon": { - "render": "./assets/layers/bike_repair_station/repair_station.svg" - } - } } }, "bike_shop": { @@ -644,15 +637,18 @@ "presets": { "0": { "title": "Зарядная станция" + }, + "3": { + "title": "Зарядная станция" } }, "tagRenderings": { - "OH": { - "question": "В какое время работает эта зарядная станция?" - }, - "Network": { + "Auth phone": { "question": "К какой сети относится эта станция?", "render": "{network}" + }, + "Authentication": { + "question": "В какое время работает эта зарядная станция?" } }, "title": { @@ -719,6 +715,24 @@ } }, "defibrillator": { + "icon": { + "mappings": { + "0": { + "then": "./assets/layers/defibrillator/aed_checked.svg" + } + } + }, + "mapRendering": { + "0": { + "icon": { + "mappings": { + "0": { + "then": "./assets/layers/defibrillator/aed_checked.svg" + } + } + } + } + }, "name": "Дефибрилляторы", "presets": { "0": { @@ -766,17 +780,6 @@ }, "title": { "render": "Дефибриллятор" - }, - "mapRendering": { - "0": { - "icon": { - "mappings": { - "0": { - "then": "./assets/layers/defibrillator/aed_checked.svg" - } - } - } - } } }, "direction": { @@ -1447,15 +1450,13 @@ } }, "waste_basket": { - "name": "Контейнер для мусора", - "presets": { - "0": { - "title": "Контейнер для мусора" + "iconSize": { + "mappings": { + "0": { + "then": "Контейнер для мусора" + } } }, - "title": { - "render": "Контейнер для мусора" - }, "mapRendering": { "0": { "iconSize": { @@ -1466,6 +1467,15 @@ } } } + }, + "name": "Контейнер для мусора", + "presets": { + "0": { + "title": "Контейнер для мусора" + } + }, + "title": { + "render": "Контейнер для мусора" } }, "watermill": { diff --git a/langs/layers/zh_Hant.json b/langs/layers/zh_Hant.json index e5e5f65bf..b0afffb78 100644 --- a/langs/layers/zh_Hant.json +++ b/langs/layers/zh_Hant.json @@ -449,11 +449,11 @@ "description": "充電站", "name": "充電站", "tagRenderings": { - "Network": { + "Auth phone": { "question": "充電站所屬的網路是?", "render": "{network}" }, - "OH": { + "Authentication": { "question": "何時是充電站開放使用的時間?" } }, diff --git a/langs/themes/de.json b/langs/themes/de.json index c0ba85915..de7d8075b 100644 --- a/langs/themes/de.json +++ b/langs/themes/de.json @@ -36,7 +36,7 @@ "name": "Wohnmobilstellplätze", "presets": { "0": { - "description": "Fügen Sie einen neuen offiziellen Wohnmobilstellplatz hinzu. Dies sind ausgewiesene Plätze, an denen Sie in Ihrem Wohnmobil übernachten können. Sie können wie ein richtiger Campingplatz oder nur wie ein Parkplatz aussehen. Möglicherweise sind sie gar nicht ausgeschildert, sondern nur in einem Gemeindebeschluss festgelegt. Ein normaler Parkplatz für Wohnmobile, auf dem Übernachten nicht zulässig ist, ist kein Wohnmobilstellplatz. ", + "description": "Fügen Sie einen neuen offiziellen Wohnmobilstellplatz hinzu. Dies sind ausgewiesene Plätze, an denen Sie in Ihrem Wohnmobil übernachten können. Sie können wie ein richtiger Campingplatz oder nur wie ein Parkplatz aussehen. Möglicherweise sind sie gar nicht ausgeschildert, sondern nur in einem Gemeindebeschluss festgelegt. Ein normaler Parkplatz für Wohnmobile, auf dem übernachten nicht zulässig ist, ist kein Wohnmobilstellplatz. ", "title": "Wohnmobilstellplatz" } }, @@ -334,18 +334,6 @@ }, "question": "Wie heißt diese Kletterroute?", "render": "{name}" - }, - "Bolts": { - "mappings": { - "0": { - "then": "Auf dieser Kletterroute sind keine Haken vorhanden" - }, - "1": { - "then": "Auf dieser Kletterroute sind keine Haken vorhanden" - } - }, - "question": "Wie viele Haken gibt es auf dieser Kletterroute bevor der Umlenker bzw. Standhaken erreicht ist?", - "render": "Diese Kletterroute hat {climbing:bolts} Haken" } }, "title": { @@ -646,8 +634,6 @@ }, "etymology": { "description": "Auf dieser Karte können Sie sehen, wonach ein Objekt benannt ist. Die Straßen, Gebäude, ... stammen von OpenStreetMap, das mit Wikidata verknüpft wurde. In dem Popup sehen Sie den Wikipedia-Artikel (falls vorhanden) oder ein Wikidata-Feld, nach dem das Objekt benannt ist. Wenn das Objekt selbst eine Wikipedia-Seite hat, wird auch diese angezeigt.

Sie können auch einen Beitrag leisten!Zoomen Sie genug hinein und alle Straßen werden angezeigt. Wenn Sie auf eine Straße klicken, öffnet sich ein Wikidata-Suchfeld. Mit ein paar Klicks können Sie einen Etymologie-Link hinzufügen. Beachten Sie, dass Sie dazu ein kostenloses OpenStreetMap-Konto benötigen.", - "shortDescription": "Was ist der Ursprung eines Ortsnamens?", - "title": "Open Etymology Map", "layers": { "1": { "override": { @@ -659,7 +645,9 @@ "name": "Parks und Waldflächen ohne Informationen zur Namensherkunft" } } - } + }, + "shortDescription": "Was ist der Ursprung eines Ortsnamens?", + "title": "Open Etymology Map" }, "facadegardens": { "layers": { @@ -775,6 +763,17 @@ } } }, + "mapRendering": { + "0": { + "icon": { + "mappings": { + "0": { + "then": "./assets/themes/hackerspaces/led.png" + } + } + } + } + }, "name": "Hackerspace", "presets": { "0": { @@ -834,17 +833,6 @@ } }, "render": "Hackerspace" - }, - "mapRendering": { - "0": { - "icon": { - "mappings": { - "0": { - "then": "./assets/themes/hackerspaces/led.png" - } - } - } - } } } }, @@ -960,9 +948,6 @@ "station-name": { "question": "Wie lautet der Name dieser Feuerwache?" } - }, - "title": { - "render": "Feuerwache" } }, "3": { @@ -974,66 +959,37 @@ } } }, + "maps": { + "title": "Eine Karte der Karten" + }, + "natuurpunt": { + "shortDescription": "Diese Karte zeigt Naturschutzgebiete des flämischen Naturverbands Natuurpunt", + "title": "Naturschutzgebiete" + }, + "observation_towers": { + "description": "Öffentlich zugänglicher Aussichtsturm", + "shortDescription": "Öffentlich zugänglicher Aussichtsturm", + "title": "Aussichtstürme" + }, "openwindpowermap": { "description": "Eine Karte zum Anzeigen und Bearbeiten von Windkraftanlagen.", - "title": "OpenWindPowerMap", - "layers": { - "0": { - "name": "Windrad", - "presets": { - "0": { - "title": "Windrad" - } - }, - "title": { - "render": "Windrad" - }, - "units": { - "0": { - "applicableUnits": { - "0": { - "human": " Megawatt" - }, - "1": { - "human": " Kilowatt" - }, - "2": { - "human": " Watt" - }, - "3": { - "human": " Gigawatt" - } - } - }, - "1": { - "applicableUnits": { - "0": { - "human": " Meter" - } - } - } - } - } - } + "title": "OpenWindPowerMap" + }, + "parkings": { + "description": "Diese Karte zeigt Parkplätze", + "shortDescription": "Diese Karte zeigt Parkplätze", + "title": "Parken" }, "personal": { "description": "Erstellen Sie ein persönliches Thema auf der Grundlage aller verfügbaren Ebenen aller Themen", "title": "Persönliches Thema" }, + "playgrounds": { + "shortDescription": "Eine Karte mit Spielplätzen", + "title": "Spielpläzte" + }, "postboxes": { "layers": { - "0": { - "description": "Die Ebene zeigt Briefkästen.", - "name": "Brieflästen", - "presets": { - "0": { - "title": "Briefkasten" - } - }, - "title": { - "render": "Briefkasten" - } - }, "1": { "description": "Eine Ebene mit Postämtern.", "tagRenderings": { @@ -1044,29 +1000,11 @@ } } } - }, - "title": { - "render": "Poststelle" - }, - "presets": { - "0": { - "title": "Poststelle" - } - }, - "name": "Poststellen", - "filter": { - "0": { - "options": { - "0": { - "question": "Aktuell geöffnet" - } - } - } } } }, - "title": "Karte mit Briefkästen und Poststellen", - "shortDescription": "Eine Karte die Briefkästen und Poststellen anzeigt" + "shortDescription": "Eine Karte die Briefkästen und Poststellen anzeigt", + "title": "Karte mit Briefkästen und Poststellen" }, "shops": { "shortDescription": "Eine bearbeitbare Karte mit grundlegenden Geschäftsinformationen", @@ -1121,37 +1059,11 @@ } }, "shortDescription": "Helfen Sie beim Aufbau eines offenen Datensatzes britischer Adressen", - "title": "Adressen in Großbritannien", - "tileLayerSources": { - "0": { - "name": "Grenzverläufe gemäß osmuk.org" - } - } + "title": "Adressen in Großbritannien" }, "waste_basket": { "description": "Auf dieser Karte finden Sie Abfalleimer in Ihrer Nähe. Wenn ein Abfalleimer auf dieser Karte fehlt, können Sie ihn selbst hinzufügen", "shortDescription": "Eine Karte mit Abfalleimern", "title": "Abfalleimer" - }, - "playgrounds": { - "shortDescription": "Eine Karte mit Spielplätzen", - "title": "Spielpläzte" - }, - "parkings": { - "description": "Diese Karte zeigt Parkplätze", - "shortDescription": "Diese Karte zeigt Parkplätze", - "title": "Parken" - }, - "observation_towers": { - "description": "Öffentlich zugänglicher Aussichtsturm", - "shortDescription": "Öffentlich zugänglicher Aussichtsturm", - "title": "Aussichtstürme" - }, - "natuurpunt": { - "shortDescription": "Diese Karte zeigt Naturschutzgebiete des flämischen Naturverbands Natuurpunt", - "title": "Naturschutzgebiete" - }, - "maps": { - "title": "Eine Karte der Karten" } } \ No newline at end of file diff --git a/langs/themes/en.json b/langs/themes/en.json index 824ca51c8..283c144aa 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -1243,8 +1243,13 @@ "0": { "description": "Layer showing sidewalks of highways", "name": "Sidewalks", + "tagRenderings": { + "streetname": { + "render": "This street is named {name}" + } + }, "title": { - "render": "Street {name}" + "render": "{name}" } } }, diff --git a/langs/themes/nl.json b/langs/themes/nl.json index 34fef4ae8..b8431d4f5 100644 --- a/langs/themes/nl.json +++ b/langs/themes/nl.json @@ -175,25 +175,11 @@ "0": { "description": "camperplaatsen", "name": "Camperplaatsen", - "presets": { - "0": { - "description": "Voeg een nieuwe officiële camperplaats toe. Dit zijn speciaal aangeduide plaatsen waar het toegestaan is om te overnachten met een camper. Ze kunnen er uitzien als een parking, of soms eerder als een camping. Soms staan ze niet ter plaatse aangeduid, maar heeft de gemeente wel degelijk beslist dat dit een camperplaats is. Een parking voor campers waar je niet mag overnachten is géén camperplaats. ", - "title": "camperplaats" - } - }, "tagRenderings": { - "caravansites-capacity": { - "question": "Hoeveel campers kunnen hier overnachten? (sla dit over als er geen duidelijk aantal plaatsen of aangeduid maximum is)", - "render": "{capacity} campers kunnen deze plaats tegelijk gebruiken" - }, "caravansites-charge": { "question": "Hoeveel kost deze plaats?", "render": "Deze plaats vraagt {charge}" }, - "caravansites-description": { - "question": "Wil je graag een algemene beschrijving toevoegen van deze plaats? (Herhaal hier niet de antwoorden op de vragen die reeds gesteld zijn. Hou het objectief - je kan je mening geven via een review)", - "render": "Meer details over deze plaats: {description}" - }, "caravansites-fee": { "mappings": { "0": { @@ -205,16 +191,6 @@ }, "question": "Moet men betalen om deze camperplaats te gebruiken?" }, - "caravansites-internet": { - "mappings": { - "0": { - "then": "Er is internettoegang" - }, - "1": { - "then": "Er is internettoegang" - } - } - }, "caravansites-name": { "question": "Wat is de naam van deze plaats?", "render": "Deze plaats heet {name}" diff --git a/scripts/lint.ts b/scripts/lint.ts index 31261d6b1..63d8ff63a 100644 --- a/scripts/lint.ts +++ b/scripts/lint.ts @@ -32,7 +32,7 @@ function fixLayerConfig(config: LayerConfigJson): void { } } - if (config.mapRendering === undefined) { + if (config.mapRendering === undefined || config.id !== "sidewalks") { // This is a legacy format, lets create a pointRendering let location: ("point" | "centroid")[] = ["point"] let wayHandling: number = config["wayHandling"] ?? 0 From 5a66b14544f0eab1813855af7a037f6db22ab097 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 26 Oct 2021 01:52:41 +0200 Subject: [PATCH 29/95] Reset translations --- .../charging_station/charging_station.json | 3309 +++++++++++------ .../charging_station.protojson | 30 +- .../layers/defibrillator/defibrillator.json | 8 +- langs/layers/de.json | 238 +- langs/layers/en.json | 303 +- langs/layers/fr.json | 11 - langs/layers/it.json | 27 - langs/layers/ja.json | 16 - langs/layers/nb_NO.json | 15 - langs/layers/nl.json | 377 +- langs/layers/ru.json | 108 +- langs/layers/zh_Hant.json | 16 - 12 files changed, 2820 insertions(+), 1638 deletions(-) diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index be3599ea0..28e97ecd1 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -1,1150 +1,2195 @@ { - "id": "charging_station", - "name": { - "en": "Charging stations", - "nl": "Oplaadpunten", - "de": "Ladestationen", - "it": "Stazioni di ricarica", - "ja": "充電ステーション", - "nb_NO": "Ladestasjoner", - "ru": "Зарядные станции", - "zh_Hant": "充電站" + "id": "charging_station", + "name": { + "en": "Charging stations", + "nl": "Oplaadpunten" + }, + "minzoom": 10, + "source": { + "osmTags": { + "or": [ + "amenity=charging_station", + "disused:amenity=charging_station", + "planned:amenity=charging_station", + "construction:amenity=charging_station" + ] + } + }, + "title": { + "render": { + "en": "Charging station", + "nl": "Oplaadpunten" + } + }, + "description": { + "en": "A charging station", + "nl": "Oplaadpunten" + }, + "tagRenderings": [ + "images", + { + "id": "Type", + "#": "Allowed vehicle types", + "question": { + "en": "Which vehicles are allowed to charge here?", + "nl": "Welke voertuigen kunnen hier opgeladen worden?" + }, + "multiAnswer": true, + "mappings": [ + { + "if": "bicycle=yes", + "ifnot": "bicycle=no", + "then": { + "en": "Bcycles can be charged here", + "nl": "Fietsen kunnen hier opgeladen worden" + } + }, + { + "if": "motorcar=yes", + "ifnot": "motorcar=no", + "then": { + "en": "Cars can be charged here", + "nl": "Elektrische auto's kunnen hier opgeladen worden" + } + }, + { + "if": "scooter=yes", + "ifnot": "scooter=no", + "then": { + "en": "Scooters can be charged here", + "nl": "Electrische scooters (snorfiets of bromfiets) kunnen hier opgeladen worden" + } + }, + { + "if": "hgv=yes", + "ifnot": "hgv=no", + "then": { + "en": "Heavy good vehicles (such as trucks) can be charged here", + "nl": "Vrachtwagens kunnen hier opgeladen worden" + } + }, + { + "if": "bus=yes", + "ifnot": "bus=no", + "then": { + "en": "Buses can be charged here", + "nl": "Bussen kunnen hier opgeladen worden" + } + } + ] }, - "minzoom": 10, - "source": { - "osmTags": { + { + "id": "access", + "question": { + "en": "Who is allowed to use this charging station?", + "nl": "Wie mag er dit oplaadpunt gebruiken?" + }, + "render": { + "en": "Access is {access}", + "nl": "Toegang voor {access}" + }, + "freeform": { + "key": "access", + "addExtraTags": [ + "fixme=Freeform field used for access - doublecheck the value" + ] + }, + "mappings": [ + { + "if": "access=yes", + "then": { + "en": "Anyone can use this charging station (payment might be needed)", + "nl": "Toegankelijk voor iedereen (mogelijks met aanmelden en/of te betalen)" + } + }, + { + "if": { "or": [ - "amenity=charging_station", - "disused:amenity=charging_station", - "planned:amenity=charging_station", - "construction:amenity=charging_station" + "access=permissive", + "access=public" ] + }, + "then": { + "en": "Anyone can use this charging station (payment might be needed)", + "nl": "Toegankelijk voor iedereen (mogelijks met aanmelden en/of te betalen)" + }, + "hideInAnswer": true + }, + { + "if": "access=customers", + "then": { + "en": "Only customers of the place this station belongs to can use this charging station
E.g. a charging station operated by hotel which is only usable by their guests", + "nl": "Enkel klanten van de bijhorende plaats mogen dit oplaadpunt gebruiken
Bv. op de parking van een hotel en enkel toegankelijk voor klanten van dit hotel" + } + }, + { + "if": "access=private", + "then": { + "en": "Not accessible to the general public (e.g. only accessible to the owners, employees, ...)", + "nl": "Niet toegankelijk voor het publiek
Bv. enkel toegankelijk voor de eigenaar, medewerkers ,... " + } } + ] }, - "title": { - "render": { - "en": "Charging station", - "nl": "Oplaadpunten", - "de": "Ladestation", - "it": "Stazione di ricarica", - "ja": "充電ステーション", - "nb_NO": "Ladestasjon", - "ru": "Зарядная станция", - "zh_Hant": "充電站" - } + { + "id": "capacity", + "render": { + "en": "{capacity} vehicles can be charged here at the same time", + "nl": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden" + }, + "question": { + "en": "How much vehicles can be charged here at the same time?", + "nl": "Hoeveel voertuigen kunnen hier opgeladen worden?" + }, + "freeform": { + "key": "capacity", + "type": "pnat" + } }, - "description": { - "en": "A charging station", - "nl": "Oplaadpunten", - "de": "Eine Ladestation", - "it": "Una stazione di ricarica", - "ja": "充電ステーション", - "nb_NO": "En ladestasjon", - "ru": "Зарядная станция", - "zh_Hant": "充電站" - }, - "tagRenderings": [ - "images", + { + "id": "Available_charging_stations (generated)", + "question": { + "en": "Which charging connections are available here?", + "nl": "Welke aansluitingen zijn hier beschikbaar?" + }, + "multiAnswer": true, + "mappings": [ { - "id": "plugs-13", - "question": { - "en": "How much plugs of type
USB to charge phones and small electronics
are available here?", - "nl": "Hoeveel stekkers van type
USB om GSMs en kleine electronica op te laden
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:USB-A} plugs of type
USB to charge phones and small electronics
available here", - "nl": "Hier zijn {socket:USB-A} stekkers van het type
USB om GSMs en kleine electronica op te laden
" - }, - "freeform": { - "key": "socket:USB-A", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "plugs-14", - "question": { - "en": "How much plugs of type
Bosch Active Connect with 3 pins and cable
are available here?", - "nl": "Hoeveel stekkers van type
Bosch Active Connect met 3 pinnen aan een kabel
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:bosch_3pin} plugs of type
Bosch Active Connect with 3 pins and cable
available here", - "nl": "Hier zijn {socket:bosch_3pin} stekkers van het type
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "freeform": { - "key": "socket:bosch_3pin", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:bosch_3pin~*", - "socket:bosch_3pin!=0" - ] - } - }, - { - "id": "plugs-15", - "question": { - "en": "How much plugs of type
Bosch Active Connect with 5 pins and cable
are available here?", - "nl": "Hoeveel stekkers van type
Bosch Active Connect met 5 pinnen aan een kabel
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:bosch_5pin} plugs of type
Bosch Active Connect with 5 pins and cable
available here", - "nl": "Hier zijn {socket:bosch_5pin} stekkers van het type
Bosch Active Connect met 5 pinnen aan een kabel
" - }, - "freeform": { - "key": "socket:bosch_5pin", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:bosch_5pin~*", - "socket:bosch_5pin!=0" - ] - } - }, - { - "id": "OH", - "render": "{opening_hours_table(opening_hours)}", - "freeform": { - "key": "opening_hours", - "type": "opening_hours" - }, - "question": { - "en": "When is this charging station opened?", - "nl": "Wanneer is dit oplaadpunt beschikbaar??", - "de": "Wann ist diese Ladestation geöffnet?", - "it": "Quali sono gli orari di apertura di questa stazione di ricarica?", - "ja": "この充電ステーションはいつオープンしますか?", - "nb_NO": "Når åpnet denne ladestasjonen?", - "ru": "В какое время работает эта зарядная станция?", - "zh_Hant": "何時是充電站開放使用的時間?" - }, - "mappings": [ - { - "if": "opening_hours=24/7", - "then": { - "en": "24/7 opened (including holidays)", - "nl": "24/7 open - ook tijdens vakanties", - "de": "durchgehend geöffnet (auch an Feiertagen)" - } - } + "if": "socket:schuko=1", + "ifnot": "socket:schuko=", + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "hideInAnswer": { + "or": [ + "_country!=be", + "_country!=fr", + "_country!=ma", + "_country!=tn", + "_country!=pl", + "_country!=cs", + "_country!=sk", + "_country!=mo" ] + } }, { - "id": "fee", - "question": { - "en": "Does one have to pay to use this charging station?", - "nl": "Moet men betalen om dit oplaadpunt te gebruiken?" - }, - "mappings": [ - { - "if": { - "and": [ - "fee=no" - ] - }, - "then": { - "nl": "Gratis te gebruiken", - "en": "Free to use" - }, - "hideInAnswer": true - }, - { - "if": { - "and": [ - "fee=no", - "fee:conditional=", - "charge=", - "authentication:none=yes" - ] - }, - "then": { - "nl": "Gratis te gebruiken (zonder aan te melden)", - "en": "Free to use (without authenticating)" - } - }, - { - "if": { - "and": [ - "fee=no", - "fee:conditional=", - "charge=", - "authentication:none=no" - ] - }, - "then": { - "nl": "Gratis te gebruiken, maar aanmelden met een applicatie is verplicht", - "en": "Free to use, but one has to authenticate" - } - }, - { - "if": { - "and": [ - "fee=yes", - "fee:conditional=no @ customers" - ] - }, - "then": { - "nl": "Betalend te gebruiken, maar gratis voor klanten van het bijhorende hotel/café/ziekenhuis/...", - "en": "Paid use, but free for customers of the hotel/pub/hospital/... who operates the charging station" - } - }, - { - "if": { - "and": [ - "fee=yes", - "fee:conditional=" - ] - }, - "then": { - "nl": "Betalend", - "en": "Paid use" - } - } - ] - }, - { - "id": "charge", - "question": { - "en": "How much does one have to pay to use this charging station?", - "nl": "Hoeveel moet men betalen om dit oplaadpunt te gebruiken?" - }, - "render": { - "en": "Using this charging station costs {charge}", - "nl": "Dit oplaadpunt gebruiken kost {charge}" - }, - "freeform": { - "key": "charge" - }, - "condition": "fee=yes" - }, - { - "id": "payment-options", - "builtin": "payment-options", - "override": { - "condition": { - "or": [ - "fee=yes", - "charge~*" - ] - }, - "mappings+": [ - { - "if": "payment:app=yes", - "ifnot": "payment:app=no", - "then": { - "en": "Payment is done using a dedicated app", - "nl": "Betalen via een app van het netwerk", - "de": "Bezahlung mit einer speziellen App" - } - }, - { - "if": "payment:membership_card=yes", - "ifnot": "payment:membership_card=no", - "then": { - "en": "Payment is done using a membership card", - "nl": "Betalen via een lidkaart van het netwerk", - "de": "Bezahlung mit einer Mitgliedskarte" - } - } - ] - } - }, - { - "id": "Authentication", - "#": "In some cases, charging is free but one has to be authenticated. We only ask for authentication if fee is no (or unset). By default one sees the questions for either the payment options or the authentication options, but normally not both", - "question": { - "en": "What kind of authentication is available at the charging station?", - "nl": "Hoe kan men zich aanmelden aan dit oplaadstation?", - "de": "Welche Authentifizierung ist an der Ladestation möglich?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "authentication:membership_card=yes", - "ifnot": "authentication:membership_card=no", - "then": { - "en": "Authentication by a membership card", - "nl": "Aanmelden met een lidkaart is mogelijk", - "de": "Authentifizierung durch eine Mitgliedskarte" - } - }, - { - "if": "authentication:app=yes", - "ifnot": "authentication:app=no", - "then": { - "en": "Authentication by an app", - "nl": "Aanmelden via een applicatie is mogelijk", - "de": "Authentifizierung durch eine App" - } - }, - { - "if": "authentication:phone_call=yes", - "ifnot": "authentication:phone_call=no", - "then": { - "en": "Authentication via phone call is available", - "nl": "Aanmelden door te bellen naar een telefoonnummer is mogelijk", - "de": "Authentifizierung per Anruf ist möglich" - } - }, - { - "if": "authentication:short_message=yes", - "ifnot": "authentication:short_message=no", - "then": { - "en": "Authentication via phone call is available", - "nl": "Aanmelden via SMS is mogelijk", - "de": "Authentifizierung per Anruf ist möglich" - } - }, - { - "if": "authentication:nfc=yes", - "ifnot": "authentication:nfc=no", - "then": { - "en": "Authentication via NFC is available", - "nl": "Aanmelden via NFC is mogelijk", - "de": "Authentifizierung über NFC ist möglich" - } - }, - { - "if": "authentication:money_card=yes", - "ifnot": "authentication:money_card=no", - "then": { - "en": "Authentication via Money Card is available", - "nl": "Aanmelden met Money Card is mogelijk", - "de": "Authentifizierung über Geldkarte ist möglich" - } - }, - { - "if": "authentication:debit_card=yes", - "ifnot": "authentication:debit_card=no", - "then": { - "en": "Authentication via debit card is available", - "nl": "Aanmelden met een betaalkaart is mogelijk", - "de": "Authentifizierung per Debitkarte ist möglich" - } - }, - { - "if": "authentication:none=yes", - "ifnot": "authentication:none=no", - "then": { - "en": "Charging here is (also) possible without authentication", - "nl": "Hier opladen is (ook) mogelijk zonder aan te melden", - "de": "Das Aufladen ist hier (auch) ohne Authentifizierung möglich" - } - } - ], - "condition": { - "or": [ - "fee=no", - "fee=" - ] - } - }, - { - "id": "Auth phone", - "render": { - "en": "Authenticate by calling or SMS'ing to {authentication:phone_call:number}", - "nl": "Aanmelden door te bellen of te SMS'en naar {authentication:phone_call:number}", - "de": "Authentifizierung durch Anruf oder SMS an {authentication:phone_call:number}" - }, - "question": { - "en": "What's the phone number for authentication call or SMS?", - "nl": "Wat is het telefoonnummer dat men moet bellen of SMS'en om zich aan te melden?", - "de": "Wie lautet die Telefonnummer für den Authentifizierungsanruf oder die SMS?" - }, - "freeform": { - "key": "authentication:phone_call:number", - "type": "phone" - }, - "condition": { - "or": [ - "authentication:phone_call=yes", - "authentication:short_message=yes" - ] - } - }, - { - "id": "maxstay", - "question": { - "en": "What is the maximum amount of time one is allowed to stay here?", - "nl": "Hoelang mag een voertuig hier blijven staan?", - "de": "Was ist die Höchstdauer des Aufenthalts hier?" - }, - "freeform": { - "key": "maxstay" - }, - "render": { - "en": "One can stay at most {canonical(maxstay)}", - "nl": "De maximale parkeertijd hier is {canonical(maxstay)}", - "de": "Die maximale Parkzeit beträgt {canonical(maxstay)}" - }, - "mappings": [ - { - "if": "maxstay=unlimited", - "then": { - "en": "No timelimit on leaving your vehicle here", - "nl": "Geen maximum parkeertijd", - "de": "Keine Höchstparkdauer" - } - } - ], - "condition": { - "or": [ - "maxstay~*", - "motorcar=yes", - "hgv=yes", - "bus=yes" - ] - } - }, - { - "id": "Network", - "render": { - "en": "Part of the network {network}", - "nl": "Maakt deel uit van het {network}-netwerk", - "de": "Teil des Netzwerks {network}", - "it": "{network}", - "ja": "{network}", - "nb_NO": "{network}", - "ru": "{network}", - "zh_Hant": "{network}" - }, - "question": { - "en": "Is this charging station part of a network?", - "nl": "Is dit oplaadpunt deel van een groter netwerk?", - "de": "Ist diese Ladestation Teil eines Netzwerks?", - "it": "A quale rete appartiene questa stazione di ricarica?", - "ja": "この充電ステーションの運営チェーンはどこですか?", - "ru": "К какой сети относится эта станция?", - "zh_Hant": "充電站所屬的網路是?" - }, - "freeform": { - "key": "network" - }, - "mappings": [ - { - "if": "no:network=yes", - "then": { - "en": "Not part of a bigger network", - "nl": "Maakt geen deel uit van een groter netwerk", - "de": "Nicht Teil eines größeren Netzwerks" - } - }, - { - "if": "network=none", - "then": { - "en": "Not part of a bigger network", - "nl": "Maakt geen deel uit van een groter netwerk", - "de": "Nicht Teil eines größeren Netzwerks" - }, - "hideInAnswer": true - }, - { - "if": "network=AeroVironment", - "then": "AeroVironment" - }, - { - "if": "network=Blink", - "then": "Blink" - }, - { - "if": "network=eVgo", - "then": "eVgo" - } - ] - }, - { - "id": "Operator", - "question": { - "en": "Who is the operator of this charging station?", - "nl": "Wie beheert dit oplaadpunt?", - "de": "Wer ist der Betreiber dieser Ladestation?" - }, - "render": { - "en": "This charging station is operated by {operator}", - "nl": "Wordt beheerd door {operator}", - "de": "Diese Ladestation wird betrieben von {operator}" - }, - "freeform": { - "key": "operator" - }, - "mappings": [ - { - "if": { - "and": [ - "network:={operator}" - ] - }, - "then": { - "en": "Actually, {operator} is the network", - "nl": "Eigenlijk is {operator} het netwerk waarvan het deel uitmaakt", - "de": "Eigentlich ist {operator} das Netzwerk" - }, - "addExtraTags": [ - "operator=" - ], - "hideInAnswer": "operator=" - } - ] - }, - { - "id": "phone", - "question": { - "en": "What number can one call if there is a problem with this charging station?", - "nl": "Wat is het telefoonnummer van de beheerder van dit oplaadpunt?", - "de": "Welche Nummer kann man anrufen, wenn es ein Problem mit dieser Ladestation gibt?" - }, - "render": { - "en": "In case of problems, call {phone}", - "nl": "Bij problemen, bel naar {phone}", - "de": "Bei Problemen, anrufen unter {phone}" - }, - "freeform": { - "key": "phone", - "type": "phone" - } - }, - { - "id": "email", - "question": { - "en": "What is the email address of the operator?", - "nl": "Wat is het email-adres van de operator?", - "de": "Wie ist die Email-Adresse des Betreibers?" - }, - "render": { - "en": "In case of problems, send an email to {email}", - "nl": "Bij problemen, email naar {email}", - "de": "Bei Problemen senden Sie eine E-Mail an {email}" - }, - "freeform": { - "key": "email", - "type": "email" - } - }, - { - "id": "website", - "question": { - "en": "What is the website of the operator?", - "nl": "Wat is de website waar men meer info kan vinden over dit oplaadpunt?", - "de": "Wie ist die Webseite des Betreibers?" - }, - "render": { - "en": "More info on {website}", - "nl": "Meer informatie op {website}", - "de": "Weitere Informationen auf {website}" - }, - "freeform": { - "key": "website", - "type": "url" - } - }, - "level", - { - "id": "ref", - "question": { - "en": "What is the reference number of this charging station?", - "nl": "Wat is het referentienummer van dit oplaadstation?", - "de": "Wie lautet die Kennung dieser Ladestation?" - }, - "render": { - "en": "Reference number is {ref}", - "nl": "Het referentienummer van dit oplaadpunt is {ref}", - "de": "Die Kennziffer ist {ref}" - }, - "freeform": { - "key": "ref" - }, - "#": "Only asked if part of a bigger network. Small operators typically don't have a reference number", - "condition": "network!=" - }, - { - "id": "Operational status", - "question": { - "en": "Is this charging point in use?", - "nl": "Is dit oplaadpunt operationeel?", - "de": "Ist dieser Ladepunkt in Betrieb?" - }, - "mappings": [ - { - "if": { - "and": [ - "planned:amenity=", - "construction:amenity=", - "disused:amenity=", - "operational_status=", - "amenity=charging_station" - ] - }, - "then": { - "en": "This charging station is broken", - "nl": "Dit oplaadpunt is kapot", - "de": "Diese Ladestation ist kaputt" - } - }, - { - "if": { - "and": [ - "planned:amenity=", - "construction:amenity=", - "disused:amenity=", - "operational_status=broken", - "amenity=charging_station" - ] - }, - "then": { - "en": "A charging station is planned here", - "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden", - "de": "Hier ist eine Ladestation geplant" - } - }, - { - "if": { - "and": [ - "planned:amenity=charging_station", - "construction:amenity=", - "disused:amenity=", - "operational_status=", - "amenity=" - ] - }, - "then": { - "en": "A charging station is constructed here", - "nl": "Hier wordt op dit moment een oplaadpunt gebouwd", - "de": "Hier wird eine Ladestation gebaut" - } - }, - { - "if": { - "and": [ - "planned:amenity=", - "construction:amenity=charging_station", - "disused:amenity=", - "operational_status=broken", - "amenity=" - ] - }, - "then": { - "en": "This charging station has beed permanently disabled and is not in use anymore but is still visible", - "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig", - "de": "Diese Ladestation wurde dauerhaft deaktiviert und wird nicht mehr benutzt, ist aber noch sichtbar" - } - }, - { - "if": { - "and": [ - "planned:amenity=", - "construction:amenity=", - "disused:amenity=charging_station", - "operational_status=broken", - "amenity=" - ] - }, - "then": { - "en": "This charging station works", - "nl": "Dit oplaadpunt werkt", - "de": "Diese Ladestation funktioniert" - } - } - ] - }, - { - "id": "Parking:fee", - "question": { - "en": "Does one have to pay a parking fee while charging?", - "nl": "Moet men parkeergeld betalen tijdens het opladen?", - "de": "Muss man beim Laden eine Parkgebühr bezahlen?" - }, - "mappings": [ - { - "if": "parking:fee=no", - "then": { - "en": "No additional parking cost while charging", - "nl": "Geen extra parkeerkost tijdens het opladen", - "de": "Keine zusätzlichen Parkgebühren beim Laden" - } - }, - { - "if": "parking:fee=yes", - "then": { - "en": "An additional parking fee should be paid while charging", - "nl": "Tijdens het opladen moet er parkeergeld betaald worden", - "de": "Beim Laden ist eine zusätzliche Parkgebühr zu entrichten" - } - } - ], - "condition": { - "or": [ - "motor_vehicle=yes", - "hgv=yes", - "bus=yes", - "bicycle=no", - "bicycle=" - ] - } - } - ], - "presets": [ - { - "tags": [ - "amenity=charging_station", - "motorcar=no", - "bicycle=yes", - "socket:typee=1" - ], - "title": { - "en": "Charging station", - "nl": "gewone stekker (bedoeld om electrische fietsen op te laden)", - "de": "Ladestation", - "ru": "Зарядная станция" - }, - "preciseInput": { - "preferredBackground": "map" - } - }, - { - "tags": [ - "amenity=charging_station", - "motorcar=no", - "bicycle=yes" - ], - "title": { - "en": "charging station for e-bikes", - "nl": "oplaadpunt voor elektrische fietsen" - }, - "preciseInput": { - "preferredBackground": "map" - } - }, - { - "tags": [ - "amenity=charging_station", - "motorcar=yes", - "bicycle=no" - ], - "title": { - "en": "charging station for cars", - "nl": "oplaadstation voor elektrische auto's" - }, - "preciseInput": { - "preferredBackground": "map" - } - }, - { - "tags": [ - "amenity=charging_station" - ], - "title": { - "en": "charging station", - "nl": "oplaadstation" - }, - "preciseInput": { - "preferredBackground": "map" - } - } - ], - "wayHandling": 1, - "filter": [ - { - "id": "vehicle-type", - "options": [ - { - "question": { - "en": "All vehicle types", - "nl": "Alle voertuigen", - "de": "Alle Fahrzeugtypen" - } - }, - { - "question": { - "en": "Charging station for bicycles", - "nl": "Oplaadpunten voor fietsen", - "de": "Ladestation für Fahrräder" - }, - "osmTags": "bicycle=yes" - }, - { - "question": { - "en": "Charging station for cars", - "nl": "Oplaadpunten voor auto's", - "de": "Ladestation für Autos" - }, - "osmTags": { - "or": [ - "car=yes", - "motorcar=yes" - ] - } - } - ] - }, - { - "id": "working", - "options": [ - { - "question": { - "en": "Only working charging stations", - "nl": "Enkel werkende oplaadpunten", - "de": "Nur funktionierende Ladestationen" - }, - "osmTags": { - "and": [ - "operational_status!=broken", - "amenity=charging_station" - ] - } - } - ] - }, - { - "id": "connection_type", - "options": [ - { - "question": { - "en": "All connectors", - "nl": "Alle types", - "de": "Alle Anschlüsse" - } - }, - { - "question": { - "en": "Has a
Schuko wall plug without ground pin (CEE7/4 type F)
connector", - "nl": "Heeft een
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "osmTags": "socket:schuko~*" - }, - { - "question": { - "en": "Has a
European wall plug with ground pin (CEE7/4 type E)
connector", - "nl": "Heeft een
Europese stekker met aardingspin (CEE7/4 type E)
" - }, - "osmTags": "socket:typee~*" - }, - { - "question": { - "en": "Has a
Chademo
connector", - "nl": "Heeft een
Chademo
", - "de": "Hat einen
Chademo
Stecker" - }, - "osmTags": "socket:chademo~*" - }, - { - "question": { - "en": "Has a
Type 1 with cable (J1772)
connector", - "nl": "Heeft een
Type 1 met kabel (J1772)
" - }, - "osmTags": "socket:type1_cable~*" - }, - { - "question": { - "en": "Has a
Type 1 without cable (J1772)
connector", - "nl": "Heeft een
Type 1 zonder kabel (J1772)
" - }, - "osmTags": "socket:type1~*" - }, - { - "question": { - "en": "Has a
Type 1 CCS (aka Type 1 Combo)
connector", - "nl": "Heeft een
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "osmTags": "socket:type1_combo~*" - }, - { - "question": { - "en": "Has a
Tesla Supercharger
connector", - "nl": "Heeft een
Tesla Supercharger
", - "de": "Hat einen
Tesla Supercharger
Stecker" - }, - "osmTags": "socket:tesla_supercharger~*" - }, - { - "question": { - "en": "Has a
Type 2 (mennekes)
connector", - "nl": "Heeft een
Type 2 (mennekes)
" - }, - "osmTags": "socket:type2~*" - }, - { - "question": { - "en": "Has a
Type 2 CCS (mennekes)
connector", - "nl": "Heeft een
Type 2 CCS (mennekes)
" - }, - "osmTags": "socket:type2_combo~*" - }, - { - "question": { - "en": "Has a
Type 2 with cable (mennekes)
connector", - "nl": "Heeft een
Type 2 met kabel (J1772)
" - }, - "osmTags": "socket:type2_cable~*" - }, - { - "question": { - "en": "Has a
Tesla Supercharger CCS (a branded type2_css)
connector", - "nl": "Heeft een
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "osmTags": "socket:tesla_supercharger_ccs~*" - }, - { - "question": { - "en": "Has a
Tesla Supercharger (destination)
connector", - "nl": "Heeft een
Tesla Supercharger (destination)
" - }, - "osmTags": "socket:tesla_destination~*" - }, - { - "question": { - "en": "Has a
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
connector", - "nl": "Heeft een
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "osmTags": "socket:tesla_destination~*" - }, - { - "question": { - "en": "Has a
USB to charge phones and small electronics
connector", - "nl": "Heeft een
USB om GSMs en kleine electronica op te laden
" - }, - "osmTags": "socket:USB-A~*" - }, - { - "question": { - "en": "Has a
Bosch Active Connect with 3 pins and cable
connector", - "nl": "Heeft een
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "osmTags": "socket:bosch_3pin~*" - }, - { - "question": { - "en": "Has a
Bosch Active Connect with 5 pins and cable
connector", - "nl": "Heeft een
Bosch Active Connect met 5 pinnen aan een kabel
" - }, - "osmTags": "socket:bosch_5pin~*" - } - ] - } - ], - "units": [ - { - "appliesToKey": [ - "maxstay" - ], - "applicableUnits": [ - { - "canonicalDenomination": "minutes", - "canonicalDenominationSingular": "minute", - "alternativeDenomination": [ - "m", - "min", - "mins", - "minuten", - "mns" - ], - "human": { - "en": " minutes", - "nl": " minuten", - "de": " Minuten", - "ru": " минут" - }, - "humanSingular": { - "en": " minute", - "nl": " minuut", - "de": " Minute", - "ru": " минута" - } - }, - { - "canonicalDenomination": "hours", - "canonicalDenominationSingular": "hour", - "alternativeDenomination": [ - "h", - "hrs", - "hours", - "u", - "uur", - "uren" - ], - "human": { - "en": " hours", - "nl": " uren", - "de": " Stunden", - "ru": " часов" - }, - "humanSingular": { - "en": " hour", - "nl": " uur", - "de": " Stunde", - "ru": " час" - } - }, - { - "canonicalDenomination": "days", - "canonicalDenominationSingular": "day", - "alternativeDenomination": [ - "dys", - "dagen", - "dag" - ], - "human": { - "en": " days", - "nl": " day", - "de": " Tage", - "ru": " дней" - }, - "humanSingular": { - "en": " day", - "nl": " dag", - "de": " Tag", - "ru": " день" - } - } - ] - }, - { - "appliesToKey": [ - "socket:schuko:voltage", - "socket:typee:voltage", - "socket:chademo:voltage", - "socket:type1_cable:voltage", - "socket:type1:voltage", - "socket:type1_combo:voltage", - "socket:tesla_supercharger:voltage", - "socket:type2:voltage", - "socket:type2_combo:voltage", - "socket:type2_cable:voltage", - "socket:tesla_supercharger_ccs:voltage", - "socket:tesla_destination:voltage", - "socket:tesla_destination:voltage", - "socket:USB-A:voltage", - "socket:bosch_3pin:voltage", - "socket:bosch_5pin:voltage" - ], - "applicableUnits": [ - { - "canonicalDenomination": "V", - "alternativeDenomination": [ - "v", - "volt", - "voltage", - "V", - "Volt" - ], - "human": { - "en": "Volts", - "nl": "volt", - "de": "Volt", - "ru": "Вольт" - } - } - ], - "eraseInvalidValues": true - }, - { - "appliesToKey": [ - "socket:schuko:current", - "socket:typee:current", - "socket:chademo:current", - "socket:type1_cable:current", - "socket:type1:current", - "socket:type1_combo:current", - "socket:tesla_supercharger:current", - "socket:type2:current", - "socket:type2_combo:current", - "socket:type2_cable:current", - "socket:tesla_supercharger_ccs:current", - "socket:tesla_destination:current", - "socket:tesla_destination:current", - "socket:USB-A:current", - "socket:bosch_3pin:current", - "socket:bosch_5pin:current" - ], - "applicableUnits": [ - { - "canonicalDenomination": "A", - "alternativeDenomination": [ - "a", - "amp", - "amperage", - "A" - ], - "human": { - "en": "A", - "nl": "A" - } - } - ], - "eraseInvalidValues": true - }, - { - "appliesToKey": [ - "socket:schuko:output", - "socket:typee:output", - "socket:chademo:output", - "socket:type1_cable:output", - "socket:type1:output", - "socket:type1_combo:output", - "socket:tesla_supercharger:output", - "socket:type2:output", - "socket:type2_combo:output", - "socket:type2_cable:output", - "socket:tesla_supercharger_ccs:output", - "socket:tesla_destination:output", - "socket:tesla_destination:output", - "socket:USB-A:output", - "socket:bosch_3pin:output", - "socket:bosch_5pin:output" - ], - "applicableUnits": [ - { - "canonicalDenomination": "kW", - "alternativeDenomination": [ - "kilowatt" - ], - "human": { - "en": "kilowatt", - "nl": "kilowatt", - "de": "Kilowatt", - "ru": "киловатт" - } - }, - { - "canonicalDenomination": "mW", - "alternativeDenomination": [ - "megawatt" - ], - "human": { - "en": "megawatt", - "nl": "megawatt", - "de": "Megawatt", - "ru": "мегаватт" - } - } - ], - "eraseInvalidValues": true - } - ], - "allowMove": { - "enableRelocation": false, - "enableImproveAccuracy": true - }, - "deletion": { - "softDeletionTags": { + "if": { "and": [ - "amenity=", - "disused:amenity=charging_station" + "socket:schuko~*", + "socket:schuko!=1" ] + }, + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "hideInAnswer": true }, - "neededChangesets": 10 - }, - "mapRendering": [ { - "location": [ - "point" + "if": "socket:typee=1", + "ifnot": "socket:typee=", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" + } + }, + { + "if": { + "and": [ + "socket:typee~*", + "socket:typee!=1" ] + }, + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:chademo=1", + "ifnot": "socket:chademo=", + "then": { + "en": "
Chademo
", + "nl": "
Chademo
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:chademo~*", + "socket:chademo!=1" + ] + }, + "then": { + "en": "
Chademo
", + "nl": "
Chademo
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1_cable=1", + "ifnot": "socket:type1_cable=", + "then": { + "en": "
Type 1 with cable (J1772)
", + "nl": "
Type 1 met kabel (J1772)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=1" + ] + }, + "then": { + "en": "
Type 1 with cable (J1772)
", + "nl": "
Type 1 met kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1=1", + "ifnot": "socket:type1=", + "then": { + "en": "
Type 1 without cable (J1772)
", + "nl": "
Type 1 zonder kabel (J1772)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type1~*", + "socket:type1!=1" + ] + }, + "then": { + "en": "
Type 1 without cable (J1772)
", + "nl": "
Type 1 zonder kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1_combo=1", + "ifnot": "socket:type1_combo=", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=1" + ] + }, + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_supercharger=1", + "ifnot": "socket:tesla_supercharger=", + "then": { + "en": "
Tesla Supercharger
", + "nl": "
Tesla Supercharger
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger
", + "nl": "
Tesla Supercharger
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2=1", + "ifnot": "socket:type2=", + "then": { + "en": "
Type 2 (mennekes)
", + "nl": "
Type 2 (mennekes)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type2~*", + "socket:type2!=1" + ] + }, + "then": { + "en": "
Type 2 (mennekes)
", + "nl": "
Type 2 (mennekes)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2_combo=1", + "ifnot": "socket:type2_combo=", + "then": { + "en": "
Type 2 CCS (mennekes)
", + "nl": "
Type 2 CCS (mennekes)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=1" + ] + }, + "then": { + "en": "
Type 2 CCS (mennekes)
", + "nl": "
Type 2 CCS (mennekes)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2_cable=1", + "ifnot": "socket:type2_cable=", + "then": { + "en": "
Type 2 with cable (mennekes)
", + "nl": "
Type 2 met kabel (J1772)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=1" + ] + }, + "then": { + "en": "
Type 2 with cable (mennekes)
", + "nl": "
Type 2 met kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_supercharger_ccs=1", + "ifnot": "socket:tesla_supercharger_ccs=", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_destination=1", + "ifnot": "socket:tesla_destination=", + "then": { + "en": "
Tesla Supercharger (destination)
", + "nl": "
Tesla Supercharger (destination)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + }, + { + "or": [ + "_country!=us" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger (destination)
", + "nl": "
Tesla Supercharger (destination)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_destination=1", + "ifnot": "socket:tesla_destination=", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + }, + { + "or": [ + "_country=us" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=1" + ] + }, + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:USB-A=1", + "ifnot": "socket:USB-A=", + "then": { + "en": "
USB to charge phones and small electronics
", + "nl": "
USB om GSMs en kleine electronica op te laden
" + } + }, + { + "if": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=1" + ] + }, + "then": { + "en": "
USB to charge phones and small electronics
", + "nl": "
USB om GSMs en kleine electronica op te laden
" + }, + "hideInAnswer": true + }, + { + "if": "socket:bosch_3pin=1", + "ifnot": "socket:bosch_3pin=", + "then": { + "en": "
Bosch Active Connect with 3 pins and cable
", + "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "bicycle=no" + ] + }, + { + "and": [ + { + "or": [ + "car=yes", + "motorcar=yes", + "hgv=yes", + "bus=yes" + ] + }, + "bicycle!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:bosch_3pin~*", + "socket:bosch_3pin!=1" + ] + }, + "then": { + "en": "
Bosch Active Connect with 3 pins and cable
", + "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
" + }, + "hideInAnswer": true + }, + { + "if": "socket:bosch_5pin=1", + "ifnot": "socket:bosch_5pin=", + "then": { + "en": "
Bosch Active Connect with 5 pins and cable
", + "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "bicycle=no" + ] + }, + { + "and": [ + { + "or": [ + "car=yes", + "motorcar=yes", + "hgv=yes", + "bus=yes" + ] + }, + "bicycle!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:bosch_5pin~*", + "socket:bosch_5pin!=1" + ] + }, + "then": { + "en": "
Bosch Active Connect with 5 pins and cable
", + "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
" + }, + "hideInAnswer": true } - ] + ] + }, + { + "id": "plugs-0", + "question": { + "en": "How much plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
are available here?", + "nl": "Hoeveel stekkers van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:schuko} plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
available here", + "nl": "Hier zijn {socket:schuko} stekkers van het type
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "freeform": { + "key": "socket:schuko", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "plugs-1", + "question": { + "en": "How much plugs of type
European wall plug with ground pin (CEE7/4 type E)
are available here?", + "nl": "Hoeveel stekkers van type
Europese stekker met aardingspin (CEE7/4 type E)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:typee} plugs of type
European wall plug with ground pin (CEE7/4 type E)
available here", + "nl": "Hier zijn {socket:typee} stekkers van het type
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "freeform": { + "key": "socket:typee", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "plugs-2", + "question": { + "en": "How much plugs of type
Chademo
are available here?", + "nl": "Hoeveel stekkers van type
Chademo
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:chademo} plugs of type
Chademo
available here", + "nl": "Hier zijn {socket:chademo} stekkers van het type
Chademo
" + }, + "freeform": { + "key": "socket:chademo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "plugs-3", + "question": { + "en": "How much plugs of type
Type 1 with cable (J1772)
are available here?", + "nl": "Hoeveel stekkers van type
Type 1 met kabel (J1772)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type1_cable} plugs of type
Type 1 with cable (J1772)
available here", + "nl": "Hier zijn {socket:type1_cable} stekkers van het type
Type 1 met kabel (J1772)
" + }, + "freeform": { + "key": "socket:type1_cable", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "plugs-4", + "question": { + "en": "How much plugs of type
Type 1 without cable (J1772)
are available here?", + "nl": "Hoeveel stekkers van type
Type 1 zonder kabel (J1772)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type1} plugs of type
Type 1 without cable (J1772)
available here", + "nl": "Hier zijn {socket:type1} stekkers van het type
Type 1 zonder kabel (J1772)
" + }, + "freeform": { + "key": "socket:type1", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "plugs-5", + "question": { + "en": "How much plugs of type
Type 1 CCS (aka Type 1 Combo)
are available here?", + "nl": "Hoeveel stekkers van type
Type 1 CCS (ook gekend als Type 1 Combo)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type1_combo} plugs of type
Type 1 CCS (aka Type 1 Combo)
available here", + "nl": "Hier zijn {socket:type1_combo} stekkers van het type
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "freeform": { + "key": "socket:type1_combo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "plugs-6", + "question": { + "en": "How much plugs of type
Tesla Supercharger
are available here?", + "nl": "Hoeveel stekkers van type
Tesla Supercharger
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:tesla_supercharger} plugs of type
Tesla Supercharger
available here", + "nl": "Hier zijn {socket:tesla_supercharger} stekkers van het type
Tesla Supercharger
" + }, + "freeform": { + "key": "socket:tesla_supercharger", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "plugs-7", + "question": { + "en": "How much plugs of type
Type 2 (mennekes)
are available here?", + "nl": "Hoeveel stekkers van type
Type 2 (mennekes)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type2} plugs of type
Type 2 (mennekes)
available here", + "nl": "Hier zijn {socket:type2} stekkers van het type
Type 2 (mennekes)
" + }, + "freeform": { + "key": "socket:type2", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "plugs-8", + "question": { + "en": "How much plugs of type
Type 2 CCS (mennekes)
are available here?", + "nl": "Hoeveel stekkers van type
Type 2 CCS (mennekes)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type2_combo} plugs of type
Type 2 CCS (mennekes)
available here", + "nl": "Hier zijn {socket:type2_combo} stekkers van het type
Type 2 CCS (mennekes)
" + }, + "freeform": { + "key": "socket:type2_combo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "plugs-9", + "question": { + "en": "How much plugs of type
Type 2 with cable (mennekes)
are available here?", + "nl": "Hoeveel stekkers van type
Type 2 met kabel (J1772)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type2_cable} plugs of type
Type 2 with cable (mennekes)
available here", + "nl": "Hier zijn {socket:type2_cable} stekkers van het type
Type 2 met kabel (J1772)
" + }, + "freeform": { + "key": "socket:type2_cable", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "plugs-10", + "question": { + "en": "How much plugs of type
Tesla Supercharger CCS (a branded type2_css)
are available here?", + "nl": "Hoeveel stekkers van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:tesla_supercharger_ccs} plugs of type
Tesla Supercharger CCS (a branded type2_css)
available here", + "nl": "Hier zijn {socket:tesla_supercharger_ccs} stekkers van het type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "plugs-11", + "question": { + "en": "How much plugs of type
Tesla Supercharger (destination)
are available here?", + "nl": "Hoeveel stekkers van type
Tesla Supercharger (destination)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:tesla_destination} plugs of type
Tesla Supercharger (destination)
available here", + "nl": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla Supercharger (destination)
" + }, + "freeform": { + "key": "socket:tesla_destination", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "plugs-12", + "question": { + "en": "How much plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
are available here?", + "nl": "Hoeveel stekkers van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:tesla_destination} plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
available here", + "nl": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "freeform": { + "key": "socket:tesla_destination", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "plugs-13", + "question": { + "en": "How much plugs of type
USB to charge phones and small electronics
are available here?", + "nl": "Hoeveel stekkers van type
USB om GSMs en kleine electronica op te laden
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:USB-A} plugs of type
USB to charge phones and small electronics
available here", + "nl": "Hier zijn {socket:USB-A} stekkers van het type
USB om GSMs en kleine electronica op te laden
" + }, + "freeform": { + "key": "socket:USB-A", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "plugs-14", + "question": { + "en": "How much plugs of type
Bosch Active Connect with 3 pins and cable
are available here?", + "nl": "Hoeveel stekkers van type
Bosch Active Connect met 3 pinnen aan een kabel
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:bosch_3pin} plugs of type
Bosch Active Connect with 3 pins and cable
available here", + "nl": "Hier zijn {socket:bosch_3pin} stekkers van het type
Bosch Active Connect met 3 pinnen aan een kabel
" + }, + "freeform": { + "key": "socket:bosch_3pin", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:bosch_3pin~*", + "socket:bosch_3pin!=0" + ] + } + }, + { + "id": "plugs-15", + "question": { + "en": "How much plugs of type
Bosch Active Connect with 5 pins and cable
are available here?", + "nl": "Hoeveel stekkers van type
Bosch Active Connect met 5 pinnen aan een kabel
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:bosch_5pin} plugs of type
Bosch Active Connect with 5 pins and cable
available here", + "nl": "Hier zijn {socket:bosch_5pin} stekkers van het type
Bosch Active Connect met 5 pinnen aan een kabel
" + }, + "freeform": { + "key": "socket:bosch_5pin", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:bosch_5pin~*", + "socket:bosch_5pin!=0" + ] + } + }, + { + "id": "OH", + "render": "{opening_hours_table(opening_hours)}", + "freeform": { + "key": "opening_hours", + "type": "opening_hours" + }, + "question": { + "en": "When is this charging station opened?", + "nl": "Wanneer is dit oplaadpunt beschikbaar??" + }, + "mappings": [ + { + "if": "opening_hours=24/7", + "then": { + "en": "24/7 opened (including holidays)", + "nl": "24/7 open - ook tijdens vakanties" + } + } + ] + }, + { + "id": "fee", + "question": { + "en": "Does one have to pay to use this charging station?", + "nl": "Moet men betalen om dit oplaadpunt te gebruiken?" + }, + "mappings": [ + { + "if": { + "and": [ + "fee=no" + ] + }, + "then": { + "nl": "Gratis te gebruiken", + "en": "Free to use" + }, + "hideInAnswer": true + }, + { + "if": { + "and": [ + "fee=no", + "fee:conditional=", + "charge=", + "authentication:none=yes" + ] + }, + "then": { + "nl": "Gratis te gebruiken (zonder aan te melden)", + "en": "Free to use (without authenticating)" + } + }, + { + "if": { + "and": [ + "fee=no", + "fee:conditional=", + "charge=", + "authentication:none=no" + ] + }, + "then": { + "nl": "Gratis te gebruiken, maar aanmelden met een applicatie is verplicht", + "en": "Free to use, but one has to authenticate" + } + }, + { + "if": { + "and": [ + "fee=yes", + "fee:conditional=no @ customers" + ] + }, + "then": { + "nl": "Betalend te gebruiken, maar gratis voor klanten van het bijhorende hotel/café/ziekenhuis/...", + "en": "Paid use, but free for customers of the hotel/pub/hospital/... who operates the charging station" + } + }, + { + "if": { + "and": [ + "fee=yes", + "fee:conditional=" + ] + }, + "then": { + "nl": "Betalend", + "en": "Paid use" + } + } + ] + }, + { + "id": "charge", + "question": { + "en": "How much does one have to pay to use this charging station?", + "nl": "Hoeveel moet men betalen om dit oplaadpunt te gebruiken?" + }, + "render": { + "en": "Using this charging station costs {charge}", + "nl": "Dit oplaadpunt gebruiken kost {charge}" + }, + "freeform": { + "key": "charge" + }, + "condition": "fee=yes" + }, + { + "id": "payment-options", + "builtin": "payment-options", + "override": { + "condition": { + "or": [ + "fee=yes", + "charge~*" + ] + }, + "mappings+": [ + { + "if": "payment:app=yes", + "ifnot": "payment:app=no", + "then": { + "en": "Payment is done using a dedicated app", + "nl": "Betalen via een app van het netwerk" + } + }, + { + "if": "payment:membership_card=yes", + "ifnot": "payment:membership_card=no", + "then": { + "en": "Payment is done using a membership card", + "nl": "Betalen via een lidkaart van het netwerk" + } + } + ] + } + }, + { + "id": "Authentication", + "#": "In some cases, charging is free but one has to be authenticated. We only ask for authentication if fee is no (or unset). By default one sees the questions for either the payment options or the authentication options, but normally not both", + "question": { + "en": "What kind of authentication is available at the charging station?", + "nl": "Hoe kan men zich aanmelden aan dit oplaadstation?" + }, + "multiAnswer": true, + "mappings": [ + { + "if": "authentication:membership_card=yes", + "ifnot": "authentication:membership_card=no", + "then": { + "en": "Authentication by a membership card", + "nl": "Aanmelden met een lidkaart is mogelijk" + } + }, + { + "if": "authentication:app=yes", + "ifnot": "authentication:app=no", + "then": { + "en": "Authentication by an app", + "nl": "Aanmelden via een applicatie is mogelijk" + } + }, + { + "if": "authentication:phone_call=yes", + "ifnot": "authentication:phone_call=no", + "then": { + "en": "Authentication via phone call is available", + "nl": "Aanmelden door te bellen naar een telefoonnummer is mogelijk" + } + }, + { + "if": "authentication:short_message=yes", + "ifnot": "authentication:short_message=no", + "then": { + "en": "Authentication via SMS is available", + "nl": "Aanmelden via SMS is mogelijk" + } + }, + { + "if": "authentication:nfc=yes", + "ifnot": "authentication:nfc=no", + "then": { + "en": "Authentication via NFC is available", + "nl": "Aanmelden via NFC is mogelijk" + } + }, + { + "if": "authentication:money_card=yes", + "ifnot": "authentication:money_card=no", + "then": { + "en": "Authentication via Money Card is available", + "nl": "Aanmelden met Money Card is mogelijk" + } + }, + { + "if": "authentication:debit_card=yes", + "ifnot": "authentication:debit_card=no", + "then": { + "en": "Authentication via debit card is available", + "nl": "Aanmelden met een betaalkaart is mogelijk" + } + }, + { + "if": "authentication:none=yes", + "ifnot": "authentication:none=no", + "then": { + "en": "Charging here is (also) possible without authentication", + "nl": "Hier opladen is (ook) mogelijk zonder aan te melden" + } + } + ], + "condition": { + "or": [ + "fee=no", + "fee=" + ] + } + }, + { + "id": "Auth phone", + "render": { + "en": "Authenticate by calling or SMS'ing to {authentication:phone_call:number}", + "nl": "Aanmelden door te bellen of te SMS'en naar {authentication:phone_call:number}" + }, + "question": { + "en": "What's the phone number for authentication call or SMS?", + "nl": "Wat is het telefoonnummer dat men moet bellen of SMS'en om zich aan te melden?" + }, + "freeform": { + "key": "authentication:phone_call:number", + "type": "phone" + }, + "condition": { + "or": [ + "authentication:phone_call=yes", + "authentication:short_message=yes" + ] + } + }, + { + "id": "maxstay", + "question": { + "en": "What is the maximum amount of time one is allowed to stay here?", + "nl": "Hoelang mag een voertuig hier blijven staan?" + }, + "freeform": { + "key": "maxstay" + }, + "render": { + "en": "One can stay at most {canonical(maxstay)}", + "nl": "De maximale parkeertijd hier is {canonical(maxstay)}" + }, + "mappings": [ + { + "if": "maxstay=unlimited", + "then": { + "en": "No timelimit on leaving your vehicle here", + "nl": "Geen maximum parkeertijd" + } + } + ], + "condition": { + "or": [ + "maxstay~*", + "motorcar=yes", + "hgv=yes", + "bus=yes" + ] + } + }, + { + "id": "Network", + "render": { + "en": "Part of the network {network}", + "nl": "Maakt deel uit van het {network}-netwerk" + }, + "question": { + "en": "Is this charging station part of a network?", + "nl": "Is dit oplaadpunt deel van een groter netwerk?" + }, + "freeform": { + "key": "network" + }, + "mappings": [ + { + "if": "no:network=yes", + "then": { + "en": "Not part of a bigger network", + "nl": "Maakt geen deel uit van een groter netwerk" + } + }, + { + "if": "network=none", + "then": { + "en": "Not part of a bigger network", + "nl": "Maakt geen deel uit van een groter netwerk" + }, + "hideInAnswer": true + }, + { + "if": "network=AeroVironment", + "then": "AeroVironment" + }, + { + "if": "network=Blink", + "then": "Blink" + }, + { + "if": "network=eVgo", + "then": "eVgo" + } + ] + }, + { + "id": "Operator", + "question": { + "en": "Who is the operator of this charging station?", + "nl": "Wie beheert dit oplaadpunt?" + }, + "render": { + "en": "This charging station is operated by {operator}", + "nl": "Wordt beheerd door {operator}" + }, + "freeform": { + "key": "operator" + }, + "mappings": [ + { + "if": { + "and": [ + "network:={operator}" + ] + }, + "then": { + "en": "Actually, {operator} is the network", + "nl": "Eigenlijk is {operator} het netwerk waarvan het deel uitmaakt" + }, + "addExtraTags": [ + "operator=" + ], + "hideInAnswer": "operator=" + } + ] + }, + { + "id": "phone", + "question": { + "en": "What number can one call if there is a problem with this charging station?", + "nl": "Wat is het telefoonnummer van de beheerder van dit oplaadpunt?" + }, + "render": { + "en": "In case of problems, call {phone}", + "nl": "Bij problemen, bel naar {phone}" + }, + "freeform": { + "key": "phone", + "type": "phone" + } + }, + { + "id": "email", + "question": { + "en": "What is the email address of the operator?", + "nl": "Wat is het email-adres van de operator?" + }, + "render": { + "en": "In case of problems, send an email to {email}", + "nl": "Bij problemen, email naar {email}" + }, + "freeform": { + "key": "email", + "type": "email" + } + }, + { + "id": "website", + "question": { + "en": "What is the website where one can find more information about this charging station?", + "nl": "Wat is de website waar men meer info kan vinden over dit oplaadpunt?" + }, + "render": { + "en": "More info on {website}", + "nl": "Meer informatie op {website}" + }, + "freeform": { + "key": "website", + "type": "url" + } + }, + "level", + { + "id": "ref", + "question": { + "en": "What is the reference number of this charging station?", + "nl": "Wat is het referentienummer van dit oplaadstation?" + }, + "render": { + "en": "Reference number is {ref}", + "nl": "Het referentienummer van dit oplaadpunt is {ref}" + }, + "freeform": { + "key": "ref" + }, + "#": "Only asked if part of a bigger network. Small operators typically don't have a reference number", + "condition": "network!=" + }, + { + "id": "Operational status", + "question": { + "en": "Is this charging point in use?", + "nl": "Is dit oplaadpunt operationeel?" + }, + "mappings": [ + { + "if": { + "and": [ + "planned:amenity=", + "construction:amenity=", + "disused:amenity=", + "operational_status=", + "amenity=charging_station" + ] + }, + "then": { + "en": "This charging station works", + "nl": "Dit oplaadpunt werkt" + } + }, + { + "if": { + "and": [ + "planned:amenity=", + "construction:amenity=", + "disused:amenity=", + "operational_status=broken", + "amenity=charging_station" + ] + }, + "then": { + "en": "This charging station is broken", + "nl": "Dit oplaadpunt is kapot" + } + }, + { + "if": { + "and": [ + "planned:amenity=charging_station", + "construction:amenity=", + "disused:amenity=", + "operational_status=", + "amenity=" + ] + }, + "then": { + "en": "A charging station is planned here", + "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden" + } + }, + { + "if": { + "and": [ + "planned:amenity=", + "construction:amenity=charging_station", + "disused:amenity=", + "operational_status=", + "amenity=" + ] + }, + "then": { + "en": "A charging station is constructed here", + "nl": "Hier wordt op dit moment een oplaadpunt gebouwd" + } + }, + { + "if": { + "and": [ + "planned:amenity=", + "construction:amenity=", + "disused:amenity=charging_station", + "operational_status=", + "amenity=" + ] + }, + "then": { + "en": "This charging station has beed permanently disabled and is not in use anymore but is still visible", + "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig" + } + } + ] + }, + { + "id": "Parking:fee", + "question": { + "en": "Does one have to pay a parking fee while charging?", + "nl": "Moet men parkeergeld betalen tijdens het opladen?" + }, + "mappings": [ + { + "if": "parking:fee=no", + "then": { + "en": "No additional parking cost while charging", + "nl": "Geen extra parkeerkost tijdens het opladen" + } + }, + { + "if": "parking:fee=yes", + "then": { + "en": "An additional parking fee should be paid while charging", + "nl": "Tijdens het opladen moet er parkeergeld betaald worden" + } + } + ], + "condition": { + "or": [ + "motor_vehicle=yes", + "hgv=yes", + "bus=yes", + "bicycle=no", + "bicycle=" + ] + } + } + ], + "mapRendering": [ + { + "location": [ + "point", + "centroid" + ], + "icon": { + "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", + "mappings": [ + { + "if": "bicycle=yes", + "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" + }, + { + "if": { + "or": [ + "car=yes", + "motorcar=yes" + ] + }, + "then": "pin:#fff;./assets/themes/charging_stations/car.svg" + } + ] + }, + "iconBadges": [ + { + "if": { + "or": [ + "disused:amenity=charging_station", + "operational_status=broken" + ] + }, + "then": "cross:#c22;" + }, + { + "if": { + "or": [ + "proposed:amenity=charging_station", + "planned:amenity=charging_station" + ] + }, + "then": "./assets/layers/charging_station/under_construction.svg" + }, + { + "if": { + "and": [ + "bicycle=yes", + { + "or": [ + "motorcar=yes", + "car=yes" + ] + } + ] + }, + "then": "circle:#fff;./assets/themes/charging_stations/car.svg" + } + ], + "iconSize": { + "render": "50,50,bottom" + } + } + ], + "presets": [ + { + "tags": [ + "amenity=charging_station", + "motorcar=no", + "bicycle=yes", + "socket:typee=1" + ], + "title": { + "en": "electrical outlet to charge e-bikes", + "nl": "laadpunt met gewone stekker(s) (bedoeld om electrische fietsen op te laden)" + }, + "preciseInput": { + "preferredBackground": "map" + } + }, + { + "tags": [ + "amenity=charging_station", + "motorcar=no", + "bicycle=yes" + ], + "title": { + "en": "charging station for e-bikes", + "nl": "oplaadpunt voor elektrische fietsen" + }, + "preciseInput": { + "preferredBackground": "map" + } + }, + { + "tags": [ + "amenity=charging_station", + "motorcar=yes", + "bicycle=no" + ], + "title": { + "en": "charging station for cars", + "nl": "oplaadstation voor elektrische auto's" + }, + "preciseInput": { + "preferredBackground": "map" + } + }, + { + "tags": [ + "amenity=charging_station" + ], + "title": { + "en": "charging station", + "nl": "oplaadstation" + }, + "preciseInput": { + "preferredBackground": "map" + } + } + ], + "wayHandling": 1, + "filter": [ + { + "id": "vehicle-type", + "options": [ + { + "question": { + "en": "All vehicle types", + "nl": "Alle voertuigen" + } + }, + { + "question": { + "en": "Charging station for bicycles", + "nl": "Oplaadpunten voor fietsen" + }, + "osmTags": "bicycle=yes" + }, + { + "question": { + "en": "Charging station for cars", + "nl": "Oplaadpunten voor auto's" + }, + "osmTags": { + "or": [ + "car=yes", + "motorcar=yes" + ] + } + } + ] + }, + { + "id": "working", + "options": [ + { + "question": { + "en": "Only working charging stations", + "nl": "Enkel werkende oplaadpunten" + }, + "osmTags": { + "and": [ + "operational_status!=broken", + "amenity=charging_station" + ] + } + } + ] + }, + { + "id": "connection_type", + "options": [ + { + "question": { + "en": "All connectors", + "nl": "Alle types" + } + }, + { + "question": { + "en": "Has a
Schuko wall plug without ground pin (CEE7/4 type F)
connector", + "nl": "Heeft een
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "osmTags": "socket:schuko~*" + }, + { + "question": { + "en": "Has a
European wall plug with ground pin (CEE7/4 type E)
connector", + "nl": "Heeft een
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "osmTags": "socket:typee~*" + }, + { + "question": { + "en": "Has a
Chademo
connector", + "nl": "Heeft een
Chademo
" + }, + "osmTags": "socket:chademo~*" + }, + { + "question": { + "en": "Has a
Type 1 with cable (J1772)
connector", + "nl": "Heeft een
Type 1 met kabel (J1772)
" + }, + "osmTags": "socket:type1_cable~*" + }, + { + "question": { + "en": "Has a
Type 1 without cable (J1772)
connector", + "nl": "Heeft een
Type 1 zonder kabel (J1772)
" + }, + "osmTags": "socket:type1~*" + }, + { + "question": { + "en": "Has a
Type 1 CCS (aka Type 1 Combo)
connector", + "nl": "Heeft een
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "osmTags": "socket:type1_combo~*" + }, + { + "question": { + "en": "Has a
Tesla Supercharger
connector", + "nl": "Heeft een
Tesla Supercharger
" + }, + "osmTags": "socket:tesla_supercharger~*" + }, + { + "question": { + "en": "Has a
Type 2 (mennekes)
connector", + "nl": "Heeft een
Type 2 (mennekes)
" + }, + "osmTags": "socket:type2~*" + }, + { + "question": { + "en": "Has a
Type 2 CCS (mennekes)
connector", + "nl": "Heeft een
Type 2 CCS (mennekes)
" + }, + "osmTags": "socket:type2_combo~*" + }, + { + "question": { + "en": "Has a
Type 2 with cable (mennekes)
connector", + "nl": "Heeft een
Type 2 met kabel (J1772)
" + }, + "osmTags": "socket:type2_cable~*" + }, + { + "question": { + "en": "Has a
Tesla Supercharger CCS (a branded type2_css)
connector", + "nl": "Heeft een
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "osmTags": "socket:tesla_supercharger_ccs~*" + }, + { + "question": { + "en": "Has a
Tesla Supercharger (destination)
connector", + "nl": "Heeft een
Tesla Supercharger (destination)
" + }, + "osmTags": "socket:tesla_destination~*" + }, + { + "question": { + "en": "Has a
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
connector", + "nl": "Heeft een
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "osmTags": "socket:tesla_destination~*" + }, + { + "question": { + "en": "Has a
USB to charge phones and small electronics
connector", + "nl": "Heeft een
USB om GSMs en kleine electronica op te laden
" + }, + "osmTags": "socket:USB-A~*" + }, + { + "question": { + "en": "Has a
Bosch Active Connect with 3 pins and cable
connector", + "nl": "Heeft een
Bosch Active Connect met 3 pinnen aan een kabel
" + }, + "osmTags": "socket:bosch_3pin~*" + }, + { + "question": { + "en": "Has a
Bosch Active Connect with 5 pins and cable
connector", + "nl": "Heeft een
Bosch Active Connect met 5 pinnen aan een kabel
" + }, + "osmTags": "socket:bosch_5pin~*" + } + ] + } + ], + "units": [ + { + "appliesToKey": [ + "maxstay" + ], + "applicableUnits": [ + { + "canonicalDenomination": "minutes", + "canonicalDenominationSingular": "minute", + "alternativeDenomination": [ + "m", + "min", + "mins", + "minuten", + "mns" + ], + "human": { + "en": " minutes", + "nl": " minuten" + }, + "humanSingular": { + "en": " minute", + "nl": " minuut" + } + }, + { + "canonicalDenomination": "hours", + "canonicalDenominationSingular": "hour", + "alternativeDenomination": [ + "h", + "hrs", + "hours", + "u", + "uur", + "uren" + ], + "human": { + "en": " hours", + "nl": " uren" + }, + "humanSingular": { + "en": " hour", + "nl": " uur" + } + }, + { + "canonicalDenomination": "days", + "canonicalDenominationSingular": "day", + "alternativeDenomination": [ + "dys", + "dagen", + "dag" + ], + "human": { + "en": " days", + "nl": " day" + }, + "humanSingular": { + "en": " day", + "nl": " dag" + } + } + ] + }, + { + "appliesToKey": [ + "socket:schuko:voltage", + "socket:typee:voltage", + "socket:chademo:voltage", + "socket:type1_cable:voltage", + "socket:type1:voltage", + "socket:type1_combo:voltage", + "socket:tesla_supercharger:voltage", + "socket:type2:voltage", + "socket:type2_combo:voltage", + "socket:type2_cable:voltage", + "socket:tesla_supercharger_ccs:voltage", + "socket:tesla_destination:voltage", + "socket:tesla_destination:voltage", + "socket:USB-A:voltage", + "socket:bosch_3pin:voltage", + "socket:bosch_5pin:voltage" + ], + "applicableUnits": [ + { + "canonicalDenomination": "V", + "alternativeDenomination": [ + "v", + "volt", + "voltage", + "V", + "Volt" + ], + "human": { + "en": "Volts", + "nl": "volt" + } + } + ], + "eraseInvalidValues": true + }, + { + "appliesToKey": [ + "socket:schuko:current", + "socket:typee:current", + "socket:chademo:current", + "socket:type1_cable:current", + "socket:type1:current", + "socket:type1_combo:current", + "socket:tesla_supercharger:current", + "socket:type2:current", + "socket:type2_combo:current", + "socket:type2_cable:current", + "socket:tesla_supercharger_ccs:current", + "socket:tesla_destination:current", + "socket:tesla_destination:current", + "socket:USB-A:current", + "socket:bosch_3pin:current", + "socket:bosch_5pin:current" + ], + "applicableUnits": [ + { + "canonicalDenomination": "A", + "alternativeDenomination": [ + "a", + "amp", + "amperage", + "A" + ], + "human": { + "en": "A", + "nl": "A" + } + } + ], + "eraseInvalidValues": true + }, + { + "appliesToKey": [ + "socket:schuko:output", + "socket:typee:output", + "socket:chademo:output", + "socket:type1_cable:output", + "socket:type1:output", + "socket:type1_combo:output", + "socket:tesla_supercharger:output", + "socket:type2:output", + "socket:type2_combo:output", + "socket:type2_cable:output", + "socket:tesla_supercharger_ccs:output", + "socket:tesla_destination:output", + "socket:tesla_destination:output", + "socket:USB-A:output", + "socket:bosch_3pin:output", + "socket:bosch_5pin:output" + ], + "applicableUnits": [ + { + "canonicalDenomination": "kW", + "alternativeDenomination": [ + "kilowatt" + ], + "human": { + "en": "kilowatt", + "nl": "kilowatt" + } + }, + { + "canonicalDenomination": "mW", + "alternativeDenomination": [ + "megawatt" + ], + "human": { + "en": "megawatt", + "nl": "megawatt" + } + } + ], + "eraseInvalidValues": true + } + ], + "allowMove": { + "enableRelocation": false, + "enableImproveAccuracy": true + }, + "deletion": { + "softDeletionTags": { + "and": [ + "amenity=", + "disused:amenity=charging_station" + ] + }, + "neededChangesets": 10 + } } \ No newline at end of file diff --git a/assets/layers/charging_station/charging_station.protojson b/assets/layers/charging_station/charging_station.protojson index 873488a12..e670ad798 100644 --- a/assets/layers/charging_station/charging_station.protojson +++ b/assets/layers/charging_station/charging_station.protojson @@ -558,21 +558,6 @@ "nl": "Is dit oplaadpunt operationeel?" }, "mappings": [ - { - "if": { - "and": [ - "planned:amenity=", - "construction:amenity=", - "disused:amenity=", - "operational_status=", - "amenity=charging_station" - ] - }, - "then": { - "en": "This charging station works", - "nl": "Dit oplaadpunt werkt" - } - }, { "if": { "and": [ @@ -632,6 +617,21 @@ "en": "This charging station has beed permanently disabled and is not in use anymore but is still visible", "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig" } + }, + { + "if": { + "and": [ + "planned:amenity=", + "construction:amenity=", + "disused:amenity=", + "operational_status=", + "amenity=charging_station" + ] + }, + "then": { + "en": "This charging station works", + "nl": "Dit oplaadpunt werkt" + } } ] }, diff --git a/assets/layers/defibrillator/defibrillator.json b/assets/layers/defibrillator/defibrillator.json index 699ac7446..8239166c2 100644 --- a/assets/layers/defibrillator/defibrillator.json +++ b/assets/layers/defibrillator/defibrillator.json @@ -564,13 +564,7 @@ "mappings": [ { "if": "_recently_surveyed=true", - "then": { - "en": "./assets/layers/defibrillator/aed_checked.svg", - "ru": "./assets/layers/defibrillator/aed_checked.svg", - "it": "./assets/layers/defibrillator/aed_checked.svg", - "fr": "./assets/layers/defibrillator/aed_checked.svg", - "de": "./assets/layers/defibrillator/aed_checked.svg" - } + "then": "./assets/layers/defibrillator/aed_checked.svg" } ] }, diff --git a/langs/layers/de.json b/langs/layers/de.json index 723351fe9..5706fd43e 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -917,197 +917,6 @@ } } }, - "charging_station": { - "description": "Eine Ladestation", - "filter": { - "0": { - "options": { - "0": { - "question": "Alle Fahrzeugtypen" - }, - "1": { - "question": "Ladestation für Fahrräder" - }, - "2": { - "question": "Ladestation für Autos" - } - } - }, - "1": { - "options": { - "0": { - "question": "Nur funktionierende Ladestationen" - } - } - }, - "2": { - "options": { - "0": { - "question": "Alle Anschlüsse" - } - } - } - }, - "name": "Ladestationen", - "presets": { - "0": { - "title": "Ladestation" - }, - "3": { - "title": "Ladestation" - } - }, - "tagRenderings": { - "Auth phone": { - "question": "Wie lautet die Telefonnummer für den Authentifizierungsanruf oder die SMS?", - "render": "Authentifizierung durch Anruf oder SMS an {authentication:phone_call:number}" - }, - "Authentication": { - "mappings": { - "0": { - "then": "Authentifizierung durch eine Mitgliedskarte" - }, - "1": { - "then": "Authentifizierung durch eine App" - }, - "2": { - "then": "Authentifizierung per Anruf ist möglich" - }, - "3": { - "then": "Authentifizierung per Anruf ist möglich" - }, - "4": { - "then": "Authentifizierung über NFC ist möglich" - }, - "5": { - "then": "Authentifizierung über Geldkarte ist möglich" - }, - "6": { - "then": "Authentifizierung per Debitkarte ist möglich" - }, - "7": { - "then": "Keine Authentifizierung erforderlich" - } - }, - "question": "Welche Authentifizierung ist an der Ladestation möglich?" - }, - "Network": { - "mappings": { - "0": { - "then": "Nicht Teil eines größeren Netzwerks" - }, - "1": { - "then": "Nicht Teil eines größeren Netzwerks" - } - }, - "question": "Ist diese Ladestation Teil eines Netzwerks?", - "render": "Teil des Netzwerks {network}" - }, - "OH": { - "mappings": { - "0": { - "then": "durchgehend geöffnet (auch an Feiertagen)" - } - }, - "question": "Wann ist diese Ladestation geöffnet?" - }, - "Operational status": { - "mappings": { - "0": { - "then": "Diese Ladestation funktioniert" - }, - "1": { - "then": "Diese Ladestation ist kaputt" - }, - "2": { - "then": "Hier ist eine Ladestation geplant" - }, - "3": { - "then": "Hier wird eine Ladestation gebaut" - }, - "4": { - "then": "Diese Ladestation wurde dauerhaft deaktiviert und wird nicht mehr benutzt, ist aber noch sichtbar" - } - }, - "question": "Ist dieser Ladepunkt in Betrieb?" - }, - "Operator": { - "mappings": { - "0": { - "then": "Eigentlich ist {operator} das Netzwerk" - } - }, - "question": "Wer ist der Betreiber dieser Ladestation?", - "render": "Diese Ladestation wird betrieben von {operator}" - }, - "Parking:fee": { - "mappings": { - "0": { - "then": "Keine zusätzlichen Parkgebühren beim Laden" - }, - "1": { - "then": "Beim Laden ist eine zusätzliche Parkgebühr zu entrichten" - } - }, - "question": "Muss man beim Laden eine Parkgebühr bezahlen?" - }, - "maxstay": { - "render": "Die maximale Parkzeit beträgt {canonical(maxstay)}" - }, - "ref": { - "render": "Die Kennziffer ist {ref}" - }, - "website": { - "render": "Weitere Informationen auf {website}" - }, - "fee/charge": { - "mappings": { - "0": { - "then": "Nutzung kostenlos" - } - }, - "render": "Die Nutzung dieser Ladestation kostet {charge}" - } - }, - "title": { - "render": "Ladestation" - }, - "units": { - "0": { - "applicableUnits": { - "0": { - "human": " Minuten", - "humanSingular": " Minute" - }, - "1": { - "human": " Stunden", - "humanSingular": " Stunde" - }, - "2": { - "human": " Tage", - "humanSingular": " Tag" - } - } - }, - "1": { - "applicableUnits": { - "0": { - "human": "Volt" - } - } - }, - "3": { - "applicableUnits": { - "0": { - "human": "Kilowatt" - }, - "1": { - "human": "Megawatt" - } - } - } - } - }, "crossings": { "description": "Übergänge für Fußgänger und Radfahrer", "name": "Kreuzungen", @@ -1423,6 +1232,13 @@ } }, "defibrillator": { + "icon": { + "mappings": { + "0": { + "then": "./assets/layers/defibrillator/aed_checked.svg" + } + } + }, "name": "Defibrillatoren", "presets": { "0": { @@ -1538,24 +1354,6 @@ }, "title": { "render": "Defibrillator" - }, - "mapRendering": { - "0": { - "icon": { - "mappings": { - "0": { - "then": "./assets/layers/defibrillator/aed_checked.svg" - } - } - } - } - }, - "icon": { - "mappings": { - "0": { - "then": "./assets/layers/defibrillator/aed_checked.svg" - } - } } }, "direction": { @@ -2596,6 +2394,17 @@ } } }, + "mapRendering": { + "0": { + "iconSize": { + "mappings": { + "0": { + "then": "Abfalleimer" + } + } + } + } + }, "name": "Abfalleimer", "presets": { "0": { @@ -2625,17 +2434,6 @@ }, "title": { "render": "Abfalleimer" - }, - "mapRendering": { - "0": { - "iconSize": { - "mappings": { - "0": { - "then": "Abfalleimer" - } - } - } - } } }, "watermill": { diff --git a/langs/layers/en.json b/langs/layers/en.json index 3fa8fddd1..634222f8b 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -1005,7 +1005,7 @@ "name": "Charging stations", "presets": { "0": { - "title": "Charging station" + "title": "electrical outlet to charge e-bikes" }, "1": { "title": "charging station for e-bikes" @@ -1014,7 +1014,7 @@ "title": "charging station for cars" }, "3": { - "title": "Charging station" + "title": "charging station" } }, "tagRenderings": { @@ -1034,7 +1034,7 @@ "then": "Authentication via phone call is available" }, "3": { - "then": "Authentication via phone call is available" + "then": "Authentication via SMS is available" }, "4": { "then": "Authentication via NFC is available" @@ -1051,6 +1051,107 @@ }, "question": "What kind of authentication is available at the charging station?" }, + "Available_charging_stations (generated)": { + "mappings": { + "0": { + "then": "
Schuko wall plug without ground pin (CEE7/4 type F)
" + }, + "1": { + "then": "
Schuko wall plug without ground pin (CEE7/4 type F)
" + }, + "2": { + "then": "
European wall plug with ground pin (CEE7/4 type E)
" + }, + "3": { + "then": "
European wall plug with ground pin (CEE7/4 type E)
" + }, + "4": { + "then": "
Chademo
" + }, + "5": { + "then": "
Chademo
" + }, + "6": { + "then": "
Type 1 with cable (J1772)
" + }, + "7": { + "then": "
Type 1 with cable (J1772)
" + }, + "8": { + "then": "
Type 1 without cable (J1772)
" + }, + "9": { + "then": "
Type 1 without cable (J1772)
" + }, + "10": { + "then": "
Type 1 CCS (aka Type 1 Combo)
" + }, + "11": { + "then": "
Type 1 CCS (aka Type 1 Combo)
" + }, + "12": { + "then": "
Tesla Supercharger
" + }, + "13": { + "then": "
Tesla Supercharger
" + }, + "14": { + "then": "
Type 2 (mennekes)
" + }, + "15": { + "then": "
Type 2 (mennekes)
" + }, + "16": { + "then": "
Type 2 CCS (mennekes)
" + }, + "17": { + "then": "
Type 2 CCS (mennekes)
" + }, + "18": { + "then": "
Type 2 with cable (mennekes)
" + }, + "19": { + "then": "
Type 2 with cable (mennekes)
" + }, + "20": { + "then": "
Tesla Supercharger CCS (a branded type2_css)
" + }, + "21": { + "then": "
Tesla Supercharger CCS (a branded type2_css)
" + }, + "22": { + "then": "
Tesla Supercharger (destination)
" + }, + "23": { + "then": "
Tesla Supercharger (destination)
" + }, + "24": { + "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
" + }, + "25": { + "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
" + }, + "26": { + "then": "
USB to charge phones and small electronics
" + }, + "27": { + "then": "
USB to charge phones and small electronics
" + }, + "28": { + "then": "
Bosch Active Connect with 3 pins and cable
" + }, + "29": { + "then": "
Bosch Active Connect with 3 pins and cable
" + }, + "30": { + "then": "
Bosch Active Connect with 5 pins and cable
" + }, + "31": { + "then": "
Bosch Active Connect with 5 pins and cable
" + } + }, + "question": "Which charging connections are available here?" + }, "Network": { "mappings": { "0": { @@ -1111,10 +1212,76 @@ }, "question": "Does one have to pay a parking fee while charging?" }, + "Type": { + "mappings": { + "0": { + "then": "Bcycles can be charged here" + }, + "1": { + "then": "Cars can be charged here" + }, + "2": { + "then": "Scooters can be charged here" + }, + "3": { + "then": "Heavy good vehicles (such as trucks) can be charged here" + }, + "4": { + "then": "Buses can be charged here" + } + }, + "question": "Which vehicles are allowed to charge here?" + }, + "access": { + "mappings": { + "0": { + "then": "Anyone can use this charging station (payment might be needed)" + }, + "1": { + "then": "Anyone can use this charging station (payment might be needed)" + }, + "2": { + "then": "Only customers of the place this station belongs to can use this charging station
E.g. a charging station operated by hotel which is only usable by their guests" + }, + "3": { + "then": "Not accessible to the general public (e.g. only accessible to the owners, employees, ...)" + } + }, + "question": "Who is allowed to use this charging station?", + "render": "Access is {access}" + }, + "capacity": { + "question": "How much vehicles can be charged here at the same time?", + "render": "{capacity} vehicles can be charged here at the same time" + }, + "charge": { + "question": "How much does one have to pay to use this charging station?", + "render": "Using this charging station costs {charge}" + }, "email": { "question": "What is the email address of the operator?", "render": "In case of problems, send an email to {email}" }, + "fee": { + "mappings": { + "0": { + "then": "Free to use" + }, + "1": { + "then": "Free to use (without authenticating)" + }, + "2": { + "then": "Free to use, but one has to authenticate" + }, + "3": { + "then": "Paid use, but free for customers of the hotel/pub/hospital/... who operates the charging station" + }, + "4": { + "then": "Paid use" + } + }, + "question": "Does one have to pay to use this charging station?" + }, "maxstay": { "mappings": { "0": { @@ -1140,38 +1307,77 @@ "question": "What number can one call if there is a problem with this charging station?", "render": "In case of problems, call {phone}" }, + "plugs-0": { + "question": "How much plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
are available here?", + "render": "There are {socket:schuko} plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
available here" + }, + "plugs-1": { + "question": "How much plugs of type
European wall plug with ground pin (CEE7/4 type E)
are available here?", + "render": "There are {socket:typee} plugs of type
European wall plug with ground pin (CEE7/4 type E)
available here" + }, + "plugs-10": { + "question": "How much plugs of type
Tesla Supercharger CCS (a branded type2_css)
are available here?", + "render": "There are {socket:tesla_supercharger_ccs} plugs of type
Tesla Supercharger CCS (a branded type2_css)
available here" + }, + "plugs-11": { + "question": "How much plugs of type
Tesla Supercharger (destination)
are available here?", + "render": "There are {socket:tesla_destination} plugs of type
Tesla Supercharger (destination)
available here" + }, + "plugs-12": { + "question": "How much plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
are available here?", + "render": "There are {socket:tesla_destination} plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
available here" + }, + "plugs-13": { + "question": "How much plugs of type
USB to charge phones and small electronics
are available here?", + "render": "There are {socket:USB-A} plugs of type
USB to charge phones and small electronics
available here" + }, + "plugs-14": { + "question": "How much plugs of type
Bosch Active Connect with 3 pins and cable
are available here?", + "render": "There are {socket:bosch_3pin} plugs of type
Bosch Active Connect with 3 pins and cable
available here" + }, "plugs-15": { "question": "How much plugs of type
Bosch Active Connect with 5 pins and cable
are available here?", "render": "There are {socket:bosch_5pin} plugs of type
Bosch Active Connect with 5 pins and cable
available here" }, + "plugs-2": { + "question": "How much plugs of type
Chademo
are available here?", + "render": "There are {socket:chademo} plugs of type
Chademo
available here" + }, + "plugs-3": { + "question": "How much plugs of type
Type 1 with cable (J1772)
are available here?", + "render": "There are {socket:type1_cable} plugs of type
Type 1 with cable (J1772)
available here" + }, + "plugs-4": { + "question": "How much plugs of type
Type 1 without cable (J1772)
are available here?", + "render": "There are {socket:type1} plugs of type
Type 1 without cable (J1772)
available here" + }, + "plugs-5": { + "question": "How much plugs of type
Type 1 CCS (aka Type 1 Combo)
are available here?", + "render": "There are {socket:type1_combo} plugs of type
Type 1 CCS (aka Type 1 Combo)
available here" + }, + "plugs-6": { + "question": "How much plugs of type
Tesla Supercharger
are available here?", + "render": "There are {socket:tesla_supercharger} plugs of type
Tesla Supercharger
available here" + }, + "plugs-7": { + "question": "How much plugs of type
Type 2 (mennekes)
are available here?", + "render": "There are {socket:type2} plugs of type
Type 2 (mennekes)
available here" + }, + "plugs-8": { + "question": "How much plugs of type
Type 2 CCS (mennekes)
are available here?", + "render": "There are {socket:type2_combo} plugs of type
Type 2 CCS (mennekes)
available here" + }, + "plugs-9": { + "question": "How much plugs of type
Type 2 with cable (mennekes)
are available here?", + "render": "There are {socket:type2_cable} plugs of type
Type 2 with cable (mennekes)
available here" + }, "ref": { "question": "What is the reference number of this charging station?", "render": "Reference number is {ref}" }, "website": { - "question": "What is the website of the operator?", + "question": "What is the website where one can find more information about this charging station?", "render": "More info on {website}" - }, - "voltage-15": { - "question": "What voltage do the plugs with
Bosch Active Connect with 5 pins and cable
offer?", - "render": "
Bosch Active Connect with 5 pins and cable
outputs {socket:bosch_5pin:voltage} volt" - }, - "power-output-15": { - "question": "What power output does a single plug of type
Bosch Active Connect with 5 pins and cable
offer?", - "render": "
Bosch Active Connect with 5 pins and cable
outputs at most {socket:bosch_5pin:output}" - }, - "fee/charge": { - "mappings": { - "0": { - "then": "Free to use" - } - }, - "question": "How much does one have to pay to use this charging station?", - "render": "Using this charging station costs {charge}" - }, - "current-15": { - "question": "What current do the plugs with
Bosch Active Connect with 5 pins and cable
offer?", - "render": "
Bosch Active Connect with 5 pins and cable
outputs at most {socket:bosch_5pin:current}A" } }, "title": { @@ -1730,6 +1936,13 @@ } }, "defibrillator": { + "icon": { + "mappings": { + "0": { + "then": "./assets/layers/defibrillator/aed_checked.svg" + } + } + }, "name": "Defibrillators", "presets": { "0": { @@ -1846,24 +2059,6 @@ }, "title": { "render": "Defibrillator" - }, - "mapRendering": { - "0": { - "icon": { - "mappings": { - "0": { - "then": "./assets/layers/defibrillator/aed_checked.svg" - } - } - } - } - }, - "icon": { - "mappings": { - "0": { - "then": "./assets/layers/defibrillator/aed_checked.svg" - } - } } }, "direction": { @@ -3187,6 +3382,17 @@ } } }, + "mapRendering": { + "0": { + "iconSize": { + "mappings": { + "0": { + "then": "Waste Basket" + } + } + } + } + }, "name": "Waste Basket", "presets": { "0": { @@ -3234,17 +3440,6 @@ }, "title": { "render": "Waste Basket" - }, - "mapRendering": { - "0": { - "iconSize": { - "mappings": { - "0": { - "then": "Waste Basket" - } - } - } - } } }, "watermill": { diff --git a/langs/layers/fr.json b/langs/layers/fr.json index 13055833a..0179765fd 100644 --- a/langs/layers/fr.json +++ b/langs/layers/fr.json @@ -753,17 +753,6 @@ } } }, - "mapRendering": { - "0": { - "icon": { - "mappings": { - "0": { - "then": "./assets/layers/defibrillator/aed_checked.svg" - } - } - } - } - }, "name": "Défibrillateurs", "presets": { "0": { diff --git a/langs/layers/it.json b/langs/layers/it.json index fb09fe146..95957591a 100644 --- a/langs/layers/it.json +++ b/langs/layers/it.json @@ -745,22 +745,6 @@ "render": "Oggetto relativo alle bici" } }, - "charging_station": { - "description": "Una stazione di ricarica", - "name": "Stazioni di ricarica", - "tagRenderings": { - "Auth phone": { - "question": "A quale rete appartiene questa stazione di ricarica?", - "render": "{network}" - }, - "Authentication": { - "question": "Quali sono gli orari di apertura di questa stazione di ricarica?" - } - }, - "title": { - "render": "Stazione di ricarica" - } - }, "defibrillator": { "icon": { "mappings": { @@ -769,17 +753,6 @@ } } }, - "mapRendering": { - "0": { - "icon": { - "mappings": { - "0": { - "then": "./assets/layers/defibrillator/aed_checked.svg" - } - } - } - } - }, "name": "Defibrillatori", "presets": { "0": { diff --git a/langs/layers/ja.json b/langs/layers/ja.json index 5c5b0d1d6..7a35bef11 100644 --- a/langs/layers/ja.json +++ b/langs/layers/ja.json @@ -72,22 +72,6 @@ "render": "アートワーク" } }, - "charging_station": { - "description": "充電ステーション", - "name": "充電ステーション", - "tagRenderings": { - "Auth phone": { - "question": "この充電ステーションの運営チェーンはどこですか?", - "render": "{network}" - }, - "Authentication": { - "question": "この充電ステーションはいつオープンしますか?" - } - }, - "title": { - "render": "充電ステーション" - } - }, "food": { "tagRenderings": { "friture-take-your-container": { diff --git a/langs/layers/nb_NO.json b/langs/layers/nb_NO.json index 47a02bc8e..f4f9920c0 100644 --- a/langs/layers/nb_NO.json +++ b/langs/layers/nb_NO.json @@ -170,21 +170,6 @@ } } }, - "charging_station": { - "description": "En ladestasjon", - "name": "Ladestasjoner", - "tagRenderings": { - "Auth phone": { - "render": "{network}" - }, - "Authentication": { - "question": "Når åpnet denne ladestasjonen?" - } - }, - "title": { - "render": "Ladestasjon" - } - }, "ghost_bike": { "name": "Spøkelsessykler", "title": { diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 2be34df3c..070092b83 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -896,6 +896,13 @@ "icon": { "render": "./assets/layers/birdhide/birdhide.svg" }, + "mapRendering": { + "0": { + "icon": { + "render": "./assets/layers/birdhide/birdhide.svg" + } + } + }, "name": "Vogelkijkhutten", "presets": { "0": { @@ -974,13 +981,6 @@ } }, "render": "Vogelkijkplaats" - }, - "mapRendering": { - "0": { - "icon": { - "render": "./assets/layers/birdhide/birdhide.svg" - } - } } }, "cafe_pub": { @@ -1059,6 +1059,13 @@ } } }, + "1": { + "options": { + "0": { + "question": "Enkel werkende oplaadpunten" + } + } + }, "2": { "options": { "0": { @@ -1118,16 +1125,173 @@ "name": "Oplaadpunten", "presets": { "0": { - "title": "gewone stekker (bedoeld om electrische fietsen op te laden)" + "title": "laadpunt met gewone stekker(s) (bedoeld om electrische fietsen op te laden)" }, "1": { "title": "oplaadpunt voor elektrische fietsen" }, "2": { "title": "oplaadstation voor elektrische auto's" + }, + "3": { + "title": "oplaadstation" } }, "tagRenderings": { + "Auth phone": { + "question": "Wat is het telefoonnummer dat men moet bellen of SMS'en om zich aan te melden?", + "render": "Aanmelden door te bellen of te SMS'en naar {authentication:phone_call:number}" + }, + "Authentication": { + "mappings": { + "0": { + "then": "Aanmelden met een lidkaart is mogelijk" + }, + "1": { + "then": "Aanmelden via een applicatie is mogelijk" + }, + "2": { + "then": "Aanmelden door te bellen naar een telefoonnummer is mogelijk" + }, + "3": { + "then": "Aanmelden via SMS is mogelijk" + }, + "4": { + "then": "Aanmelden via NFC is mogelijk" + }, + "5": { + "then": "Aanmelden met Money Card is mogelijk" + }, + "6": { + "then": "Aanmelden met een betaalkaart is mogelijk" + }, + "7": { + "then": "Hier opladen is (ook) mogelijk zonder aan te melden" + } + }, + "question": "Hoe kan men zich aanmelden aan dit oplaadstation?" + }, + "Available_charging_stations (generated)": { + "mappings": { + "0": { + "then": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "1": { + "then": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "2": { + "then": "
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "3": { + "then": "
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "4": { + "then": "
Chademo
" + }, + "5": { + "then": "
Chademo
" + }, + "6": { + "then": "
Type 1 met kabel (J1772)
" + }, + "7": { + "then": "
Type 1 met kabel (J1772)
" + }, + "8": { + "then": "
Type 1 zonder kabel (J1772)
" + }, + "9": { + "then": "
Type 1 zonder kabel (J1772)
" + }, + "10": { + "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "11": { + "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "12": { + "then": "
Tesla Supercharger
" + }, + "13": { + "then": "
Tesla Supercharger
" + }, + "14": { + "then": "
Type 2 (mennekes)
" + }, + "15": { + "then": "
Type 2 (mennekes)
" + }, + "16": { + "then": "
Type 2 CCS (mennekes)
" + }, + "17": { + "then": "
Type 2 CCS (mennekes)
" + }, + "18": { + "then": "
Type 2 met kabel (J1772)
" + }, + "19": { + "then": "
Type 2 met kabel (J1772)
" + }, + "20": { + "then": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "21": { + "then": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "22": { + "then": "
Tesla Supercharger (destination)
" + }, + "23": { + "then": "
Tesla Supercharger (destination)
" + }, + "24": { + "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "25": { + "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "26": { + "then": "
USB om GSMs en kleine electronica op te laden
" + }, + "27": { + "then": "
USB om GSMs en kleine electronica op te laden
" + }, + "28": { + "then": "
Bosch Active Connect met 3 pinnen aan een kabel
" + }, + "29": { + "then": "
Bosch Active Connect met 3 pinnen aan een kabel
" + }, + "30": { + "then": "
Bosch Active Connect met 5 pinnen aan een kabel
" + }, + "31": { + "then": "
Bosch Active Connect met 5 pinnen aan een kabel
" + } + }, + "question": "Welke aansluitingen zijn hier beschikbaar?" + }, + "Network": { + "mappings": { + "0": { + "then": "Maakt geen deel uit van een groter netwerk" + }, + "1": { + "then": "Maakt geen deel uit van een groter netwerk" + } + }, + "question": "Is dit oplaadpunt deel van een groter netwerk?", + "render": "Maakt deel uit van het {network}-netwerk" + }, + "OH": { + "mappings": { + "0": { + "then": "24/7 open - ook tijdens vakanties" + } + }, + "question": "Wanneer is dit oplaadpunt beschikbaar??" + }, "Operational status": { "mappings": { "0": { @@ -1148,6 +1312,96 @@ }, "question": "Is dit oplaadpunt operationeel?" }, + "Operator": { + "mappings": { + "0": { + "then": "Eigenlijk is {operator} het netwerk waarvan het deel uitmaakt" + } + }, + "question": "Wie beheert dit oplaadpunt?", + "render": "Wordt beheerd door {operator}" + }, + "Parking:fee": { + "mappings": { + "0": { + "then": "Geen extra parkeerkost tijdens het opladen" + }, + "1": { + "then": "Tijdens het opladen moet er parkeergeld betaald worden" + } + }, + "question": "Moet men parkeergeld betalen tijdens het opladen?" + }, + "Type": { + "mappings": { + "0": { + "then": "Fietsen kunnen hier opgeladen worden" + }, + "1": { + "then": "Elektrische auto's kunnen hier opgeladen worden" + }, + "2": { + "then": "Electrische scooters (snorfiets of bromfiets) kunnen hier opgeladen worden" + }, + "3": { + "then": "Vrachtwagens kunnen hier opgeladen worden" + }, + "4": { + "then": "Bussen kunnen hier opgeladen worden" + } + }, + "question": "Welke voertuigen kunnen hier opgeladen worden?" + }, + "access": { + "mappings": { + "0": { + "then": "Toegankelijk voor iedereen (mogelijks met aanmelden en/of te betalen)" + }, + "1": { + "then": "Toegankelijk voor iedereen (mogelijks met aanmelden en/of te betalen)" + }, + "2": { + "then": "Enkel klanten van de bijhorende plaats mogen dit oplaadpunt gebruiken
Bv. op de parking van een hotel en enkel toegankelijk voor klanten van dit hotel" + }, + "3": { + "then": "Niet toegankelijk voor het publiek
Bv. enkel toegankelijk voor de eigenaar, medewerkers ,... " + } + }, + "question": "Wie mag er dit oplaadpunt gebruiken?", + "render": "Toegang voor {access}" + }, + "capacity": { + "question": "Hoeveel voertuigen kunnen hier opgeladen worden?", + "render": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden" + }, + "charge": { + "question": "Hoeveel moet men betalen om dit oplaadpunt te gebruiken?", + "render": "Dit oplaadpunt gebruiken kost {charge}" + }, + "email": { + "question": "Wat is het email-adres van de operator?", + "render": "Bij problemen, email naar {email}" + }, + "fee": { + "mappings": { + "0": { + "then": "Gratis te gebruiken" + }, + "1": { + "then": "Gratis te gebruiken (zonder aan te melden)" + }, + "2": { + "then": "Gratis te gebruiken, maar aanmelden met een applicatie is verplicht" + }, + "3": { + "then": "Betalend te gebruiken, maar gratis voor klanten van het bijhorende hotel/café/ziekenhuis/..." + }, + "4": { + "then": "Betalend" + } + }, + "question": "Moet men betalen om dit oplaadpunt te gebruiken?" + }, "maxstay": { "mappings": { "0": { @@ -1169,30 +1423,81 @@ } } }, + "phone": { + "question": "Wat is het telefoonnummer van de beheerder van dit oplaadpunt?", + "render": "Bij problemen, bel naar {phone}" + }, + "plugs-0": { + "question": "Hoeveel stekkers van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:schuko} stekkers van het type
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "plugs-1": { + "question": "Hoeveel stekkers van type
Europese stekker met aardingspin (CEE7/4 type E)
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:typee} stekkers van het type
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "plugs-10": { + "question": "Hoeveel stekkers van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:tesla_supercharger_ccs} stekkers van het type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "plugs-11": { + "question": "Hoeveel stekkers van type
Tesla Supercharger (destination)
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla Supercharger (destination)
" + }, + "plugs-12": { + "question": "Hoeveel stekkers van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "plugs-13": { + "question": "Hoeveel stekkers van type
USB om GSMs en kleine electronica op te laden
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:USB-A} stekkers van het type
USB om GSMs en kleine electronica op te laden
" + }, + "plugs-14": { + "question": "Hoeveel stekkers van type
Bosch Active Connect met 3 pinnen aan een kabel
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:bosch_3pin} stekkers van het type
Bosch Active Connect met 3 pinnen aan een kabel
" + }, "plugs-15": { "question": "Hoeveel stekkers van type
Bosch Active Connect met 5 pinnen aan een kabel
heeft dit oplaadpunt?", "render": "Hier zijn {socket:bosch_5pin} stekkers van het type
Bosch Active Connect met 5 pinnen aan een kabel
" }, - "voltage-15": { - "question": "Welke spanning levert de stekker van type
Bosch Active Connect met 5 pinnen aan een kabel
", - "render": "
Bosch Active Connect met 5 pinnen aan een kabel
heeft een spanning van {socket:bosch_5pin:voltage} volt" + "plugs-2": { + "question": "Hoeveel stekkers van type
Chademo
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:chademo} stekkers van het type
Chademo
" }, - "power-output-15": { - "question": "Welk vermogen levert een enkele stekker van type
Bosch Active Connect met 5 pinnen aan een kabel
?", - "render": "
Bosch Active Connect met 5 pinnen aan een kabel
levert een vermogen van maximaal {socket:bosch_5pin:output}" + "plugs-3": { + "question": "Hoeveel stekkers van type
Type 1 met kabel (J1772)
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:type1_cable} stekkers van het type
Type 1 met kabel (J1772)
" }, - "fee/charge": { - "mappings": { - "0": { - "then": "Gratis te gebruiken" - } - }, - "question": "Hoeveel kost het gebruik van dit oplaadpunt?", - "render": "Dit oplaadpunt gebruiken kost {charge}" + "plugs-4": { + "question": "Hoeveel stekkers van type
Type 1 zonder kabel (J1772)
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:type1} stekkers van het type
Type 1 zonder kabel (J1772)
" }, - "current-15": { - "question": "Welke stroom levert de stekker van type
Bosch Active Connect met 5 pinnen aan een kabel
?", - "render": "
Bosch Active Connect met 5 pinnen aan een kabel
levert een stroom van maximaal {socket:bosch_5pin:current}A" + "plugs-5": { + "question": "Hoeveel stekkers van type
Type 1 CCS (ook gekend als Type 1 Combo)
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:type1_combo} stekkers van het type
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "plugs-6": { + "question": "Hoeveel stekkers van type
Tesla Supercharger
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:tesla_supercharger} stekkers van het type
Tesla Supercharger
" + }, + "plugs-7": { + "question": "Hoeveel stekkers van type
Type 2 (mennekes)
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:type2} stekkers van het type
Type 2 (mennekes)
" + }, + "plugs-8": { + "question": "Hoeveel stekkers van type
Type 2 CCS (mennekes)
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:type2_combo} stekkers van het type
Type 2 CCS (mennekes)
" + }, + "plugs-9": { + "question": "Hoeveel stekkers van type
Type 2 met kabel (J1772)
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:type2_cable} stekkers van het type
Type 2 met kabel (J1772)
" + }, + "ref": { + "question": "Wat is het referentienummer van dit oplaadstation?", + "render": "Het referentienummer van dit oplaadpunt is {ref}" + }, + "website": { + "question": "Wat is de website waar men meer info kan vinden over dit oplaadpunt?", + "render": "Meer informatie op {website}" } }, "title": { @@ -3501,6 +3806,17 @@ } } }, + "mapRendering": { + "0": { + "iconSize": { + "mappings": { + "0": { + "then": "Vuilnisbak" + } + } + } + } + }, "name": "Vuilnisbak", "presets": { "0": { @@ -3534,17 +3850,6 @@ }, "title": { "render": "Vuilnisbak" - }, - "mapRendering": { - "0": { - "iconSize": { - "mappings": { - "0": { - "then": "Vuilnisbak" - } - } - } - } } }, "watermill": { diff --git a/langs/layers/ru.json b/langs/layers/ru.json index baf13ac72..0d1d963bd 100644 --- a/langs/layers/ru.json +++ b/langs/layers/ru.json @@ -631,65 +631,6 @@ } } }, - "charging_station": { - "description": "Зарядная станция", - "name": "Зарядные станции", - "presets": { - "0": { - "title": "Зарядная станция" - }, - "3": { - "title": "Зарядная станция" - } - }, - "tagRenderings": { - "Authentication": { - "question": "В какое время работает эта зарядная станция?" - }, - "Auth phone": { - "question": "К какой сети относится эта станция?", - "render": "{network}" - } - }, - "title": { - "render": "Зарядная станция" - }, - "units": { - "0": { - "applicableUnits": { - "0": { - "human": " минут", - "humanSingular": " минута" - }, - "1": { - "human": " часов", - "humanSingular": " час" - }, - "2": { - "human": " дней", - "humanSingular": " день" - } - } - }, - "1": { - "applicableUnits": { - "0": { - "human": "Вольт" - } - } - }, - "3": { - "applicableUnits": { - "0": { - "human": "киловатт" - }, - "1": { - "human": "мегаватт" - } - } - } - } - }, "crossings": { "presets": { "1": { @@ -715,6 +656,13 @@ } }, "defibrillator": { + "icon": { + "mappings": { + "0": { + "then": "./assets/layers/defibrillator/aed_checked.svg" + } + } + }, "name": "Дефибрилляторы", "presets": { "0": { @@ -762,24 +710,6 @@ }, "title": { "render": "Дефибриллятор" - }, - "mapRendering": { - "0": { - "icon": { - "mappings": { - "0": { - "then": "./assets/layers/defibrillator/aed_checked.svg" - } - } - } - } - }, - "icon": { - "mappings": { - "0": { - "then": "./assets/layers/defibrillator/aed_checked.svg" - } - } } }, "direction": { @@ -1450,15 +1380,13 @@ } }, "waste_basket": { - "name": "Контейнер для мусора", - "presets": { - "0": { - "title": "Контейнер для мусора" + "iconSize": { + "mappings": { + "0": { + "then": "Контейнер для мусора" + } } }, - "title": { - "render": "Контейнер для мусора" - }, "mapRendering": { "0": { "iconSize": { @@ -1470,12 +1398,14 @@ } } }, - "iconSize": { - "mappings": { - "0": { - "then": "Контейнер для мусора" - } + "name": "Контейнер для мусора", + "presets": { + "0": { + "title": "Контейнер для мусора" } + }, + "title": { + "render": "Контейнер для мусора" } }, "watermill": { diff --git a/langs/layers/zh_Hant.json b/langs/layers/zh_Hant.json index b0afffb78..59f59f5aa 100644 --- a/langs/layers/zh_Hant.json +++ b/langs/layers/zh_Hant.json @@ -445,22 +445,6 @@ "render": "單車停車場" } }, - "charging_station": { - "description": "充電站", - "name": "充電站", - "tagRenderings": { - "Auth phone": { - "question": "充電站所屬的網路是?", - "render": "{network}" - }, - "Authentication": { - "question": "何時是充電站開放使用的時間?" - } - }, - "title": { - "render": "充電站" - } - }, "ghost_bike": { "name": "幽靈單車", "title": { From 8acf85cc552041eed1a225584feab8dac844cea0 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 27 Oct 2021 20:19:45 +0200 Subject: [PATCH 30/95] Various bugfixes and improvements to UK_addresses and GRB theme --- Logic/Actors/SelectedFeatureHandler.ts | 1 + Logic/DetermineLayout.ts | 2 +- Logic/ExtraFunction.ts | 8 +- Logic/FeatureSource/FeaturePipeline.ts | 9 +- .../Sources/FilteringFeatureSource.ts | 98 ++++++++-------- .../NewGeometryFromChangesFeatureSource.ts | 1 - Logic/MetaTagging.ts | 7 +- UI/Base/ScrollableFullScreen.ts | 15 ++- UI/Base/TabbedComponent.ts | 3 + UI/DefaultGUI.ts | 2 - UI/Popup/QuestionBox.ts | 5 +- UI/ShowDataLayer/ShowDataLayer.ts | 4 +- assets/themes/grb_import/grb.json | 110 +++++++++++++++++- assets/themes/uk_addresses/license_info.json | 30 +++++ assets/themes/uk_addresses/uk_addresses.json | 75 +++++++++++- scripts/slice.ts | 2 +- 16 files changed, 290 insertions(+), 82 deletions(-) diff --git a/Logic/Actors/SelectedFeatureHandler.ts b/Logic/Actors/SelectedFeatureHandler.ts index 7d431c7af..44a2940d3 100644 --- a/Logic/Actors/SelectedFeatureHandler.ts +++ b/Logic/Actors/SelectedFeatureHandler.ts @@ -114,6 +114,7 @@ export default class SelectedFeatureHandler { // Hash has been cleared - we clear the selected element state.selectedElement.setData(undefined); } else { + // we search the element to select const feature = state.allElements.ContainingFeatures.get(h) if (feature === undefined) { diff --git a/Logic/DetermineLayout.ts b/Logic/DetermineLayout.ts index 476131732..563b8c1a7 100644 --- a/Logic/DetermineLayout.ts +++ b/Logic/DetermineLayout.ts @@ -87,7 +87,7 @@ export default class DetermineLayout { } } catch (e) { - console.erorr(e) + console.error(e) DetermineLayout.ShowErrorOnCustomTheme( `${link} is invalid - probably not found or invalid JSON:`, new FixedUiElement(e) diff --git a/Logic/ExtraFunction.ts b/Logic/ExtraFunction.ts index 8f0265bb1..4fc1840c1 100644 --- a/Logic/ExtraFunction.ts +++ b/Logic/ExtraFunction.ts @@ -222,7 +222,6 @@ export class ExtraFunction { const maxFeatures = options?.maxFeatures ?? 1 const maxDistance = options?.maxDistance ?? 500 const uniqueTag: string | undefined = options?.uniqueTag - console.log("Requested closestN") if (typeof features === "string") { const name = features const bbox = GeoOperations.bbox(GeoOperations.buffer(GeoOperations.bbox(feature), maxDistance)) @@ -238,7 +237,7 @@ export class ExtraFunction { let closestFeatures: { feat: any, distance: number }[] = []; for (const featureList of features) { for (const otherFeature of featureList) { - if (otherFeature === feature || otherFeature.id === feature.id) { + if (otherFeature === feature || otherFeature.properties.id === feature.properties.id) { continue; // We ignore self } const distance = GeoOperations.distanceBetween( @@ -249,6 +248,11 @@ export class ExtraFunction { console.error("Could not calculate the distance between", feature, "and", otherFeature) throw "Undefined distance!" } + + if(distance === 0){ + console.trace("Got a suspiciously zero distance between", otherFeature, "and self-feature",feature) + } + if (distance > maxDistance) { continue } diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index 8cd9aae67..858ad4283 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -266,7 +266,7 @@ export default class FeaturePipeline { // Whenever fresh data comes in, we need to update the metatagging - self.newDataLoadedSignal.stabilized(1000).addCallback(_ => { + self.newDataLoadedSignal.stabilized(250).addCallback(src => { self.updateAllMetaTagging() }) @@ -391,7 +391,7 @@ export default class FeaturePipeline { window.setTimeout( () => { const layerDef = src.layer.layerDef; - MetaTagging.addMetatags( + const somethingChanged = MetaTagging.addMetatags( src.features.data, { memberships: this.relationTracker, @@ -412,9 +412,10 @@ export default class FeaturePipeline { private updateAllMetaTagging() { const self = this; + console.debug("Updating the meta tagging of all tiles as new data got loaded") this.perLayerHierarchy.forEach(hierarchy => { - hierarchy.loadedTiles.forEach(src => { - self.applyMetaTags(src) + hierarchy.loadedTiles.forEach(tile => { + self.applyMetaTags(tile) }) }) diff --git a/Logic/FeatureSource/Sources/FilteringFeatureSource.ts b/Logic/FeatureSource/Sources/FilteringFeatureSource.ts index 0c2c9d92a..ec097acca 100644 --- a/Logic/FeatureSource/Sources/FilteringFeatureSource.ts +++ b/Logic/FeatureSource/Sources/FilteringFeatureSource.ts @@ -1,5 +1,4 @@ import {UIEventSource} from "../../UIEventSource"; -import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"; import FilteredLayer from "../../../Models/FilteredLayer"; import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; import Hash from "../../Web/Hash"; @@ -12,6 +11,8 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti public readonly layer: FilteredLayer; public readonly tileIndex: number public readonly bbox: BBox + private readonly upstream: FeatureSourceForLayer; + private readonly state: { locationControl: UIEventSource<{ zoom: number }>; selectedElement: UIEventSource }; constructor( state: { @@ -21,70 +22,63 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti tileIndex, upstream: FeatureSourceForLayer ) { - const self = this; this.name = "FilteringFeatureSource(" + upstream.name + ")" this.tileIndex = tileIndex this.bbox = BBox.fromTileIndex(tileIndex) + this.upstream = upstream + this.state = state this.layer = upstream.layer; const layer = upstream.layer; - - function update() { - - const features: { feature: any; freshness: Date }[] = upstream.features.data; - const newFeatures = features.filter((f) => { - if ( - state.selectedElement.data?.id === f.feature.id || - f.feature.id === Hash.hash.data) { - // This is the selected object - it gets a free pass even if zoom is not sufficient or it is filtered away - return true; - } - - const isShown = layer.layerDef.isShown; - const tags = f.feature.properties; - if (isShown.IsKnown(tags)) { - const result = layer.layerDef.isShown.GetRenderValue( - f.feature.properties - ).txt; - if (result !== "yes") { - return false; - } - } - - const tagsFilter = layer.appliedFilters.data; - for (const filter of tagsFilter ?? []) { - const neededTags = filter.filter.options[filter.selected].osmTags - if (!neededTags.matchesProperties(f.feature.properties)) { - // Hidden by the filter on the layer itself - we want to hide it no matter wat - return false; - } - } - - - return true; - }); - - self.features.setData(newFeatures); - } - + upstream.features.addCallback(() => { - update(); + this. update(); }); layer.appliedFilters.addCallback(_ => { - update() + this.update() }) - update(); + this.update(); + } + public update() { + + const layer = this.upstream.layer; + const features: { feature: any; freshness: Date }[] = this.upstream.features.data; + const newFeatures = features.filter((f) => { + if ( + this.state.selectedElement.data?.id === f.feature.id || + f.feature.id === Hash.hash.data) { + // This is the selected object - it gets a free pass even if zoom is not sufficient or it is filtered away + return true; + } + + const isShown = layer.layerDef.isShown; + const tags = f.feature.properties; + if (isShown.IsKnown(tags)) { + const result = layer.layerDef.isShown.GetRenderValue( + f.feature.properties + ).txt; + if (result !== "yes") { + return false; + } + } + + const tagsFilter = layer.appliedFilters.data; + for (const filter of tagsFilter ?? []) { + const neededTags = filter.filter.options[filter.selected].osmTags + if (!neededTags.matchesProperties(f.feature.properties)) { + // Hidden by the filter on the layer itself - we want to hide it no matter wat + return false; + } + } + + + return true; + }); + + this.features.setData(newFeatures); } - private static showLayer( - layer: { - isDisplayed: UIEventSource; - layerDef: LayerConfig; - }) { - return layer.isDisplayed.data; - - } } diff --git a/Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource.ts b/Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource.ts index 0a1c67f41..92138b995 100644 --- a/Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource.ts +++ b/Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource.ts @@ -31,7 +31,6 @@ export class NewGeometryFromChangesFeatureSource implements FeatureSource { // Already handled !seenChanges.has(ch))) .addCallbackAndRunD(changes => { - if (changes.length === 0) { return; } diff --git a/Logic/MetaTagging.ts b/Logic/MetaTagging.ts index 83b85f34a..a6e3cc44b 100644 --- a/Logic/MetaTagging.ts +++ b/Logic/MetaTagging.ts @@ -18,6 +18,8 @@ export default class MetaTagging { /** * This method (re)calculates all metatags and calculated tags on every given object. * The given features should be part of the given layer + * + * Returns true if at least one feature has changed properties */ public static addMetatags(features: { feature: any; freshness: Date }[], params: ExtraFuncParams, @@ -25,7 +27,7 @@ export default class MetaTagging { options?: { includeDates?: true | boolean, includeNonDates?: true | boolean - }) { + }): boolean { if (features === undefined || features.length === 0) { return; @@ -48,6 +50,7 @@ export default class MetaTagging { // The calculated functions - per layer - which add the new keys const layerFuncs = this.createRetaggingFunc(layer) + let atLeastOneFeatureChanged = false; for (let i = 0; i < features.length; i++) { const ff = features[i]; @@ -95,8 +98,10 @@ export default class MetaTagging { if (somethingChanged) { State.state?.allElements?.getEventSourceById(feature.properties.id)?.ping() + atLeastOneFeatureChanged = true } } + return atLeastOneFeatureChanged } diff --git a/UI/Base/ScrollableFullScreen.ts b/UI/Base/ScrollableFullScreen.ts index b9f134565..248f9a884 100644 --- a/UI/Base/ScrollableFullScreen.ts +++ b/UI/Base/ScrollableFullScreen.ts @@ -19,6 +19,7 @@ import Img from "./Img"; export default class ScrollableFullScreen extends UIElement { private static readonly empty = new FixedUiElement(""); private static _currentlyOpen: ScrollableFullScreen; + private hashToShow: string; public isShown: UIEventSource; private _component: BaseUIElement; private _fullscreencomponent: BaseUIElement; @@ -28,6 +29,7 @@ export default class ScrollableFullScreen extends UIElement { isShown: UIEventSource = new UIEventSource(false) ) { super(); + this.hashToShow = hashToShow; this.isShown = isShown; if (hashToShow === undefined) { @@ -45,24 +47,25 @@ export default class ScrollableFullScreen extends UIElement { self.Activate(); Hash.hash.setData(hashToShow) } else { - ScrollableFullScreen.clear(); + self.clear(); } }) Hash.hash.addCallback(hash => { - if (hash === hashToShow) { - return + if (!isShown.data) { + return; + } + if (hash === undefined || hash === "") { + isShown.setData(false) } - isShown.setData(false) }) } - private static clear() { + private clear() { ScrollableFullScreen.empty.AttachTo("fullscreen") const fs = document.getElementById("fullscreen"); ScrollableFullScreen._currentlyOpen?.isShown?.setData(false); fs.classList.add("hidden") - Hash.hash.setData(undefined); } InnerRender(): BaseUIElement { diff --git a/UI/Base/TabbedComponent.ts b/UI/Base/TabbedComponent.ts index f911c0b41..e151df6c5 100644 --- a/UI/Base/TabbedComponent.ts +++ b/UI/Base/TabbedComponent.ts @@ -21,6 +21,9 @@ export class TabbedComponent extends Combine { let element = elements[i]; const header = Translations.W(element.header).onClick(() => openedTabSrc.setData(i)) openedTabSrc.addCallbackAndRun(selected => { + if(selected >= elements.length){ + selected = 0 + } if (selected === i) { header.SetClass("tab-active") header.RemoveClass("tab-non-active") diff --git a/UI/DefaultGUI.ts b/UI/DefaultGUI.ts index e259b4f2e..f6c551663 100644 --- a/UI/DefaultGUI.ts +++ b/UI/DefaultGUI.ts @@ -114,10 +114,8 @@ export default class DefaultGUI { Utils.LoadCustomCss(state.layoutToUse.customCss); } - this.SetupUIElements(); this.SetupMap() - } diff --git a/UI/Popup/QuestionBox.ts b/UI/Popup/QuestionBox.ts index 1192ff8ee..5dfa062ea 100644 --- a/UI/Popup/QuestionBox.ts +++ b/UI/Popup/QuestionBox.ts @@ -16,6 +16,7 @@ import Lazy from "../Base/Lazy"; export default class QuestionBox extends VariableUiElement { constructor(tagsSource: UIEventSource, tagRenderings: TagRenderingConfig[], units: Unit[]) { + const skippedQuestions: UIEventSource = new UIEventSource([]) tagRenderings = tagRenderings @@ -33,7 +34,7 @@ export default class QuestionBox extends VariableUiElement { { units: units, afterSave: () => { - // We save + // We save and indicate progress by pinging and recalculating skippedQuestions.ping(); }, cancelButton: Translations.t.general.skip.Clone() @@ -45,7 +46,7 @@ export default class QuestionBox extends VariableUiElement { } ))); - const skippedQuestionsButton = Translations.t.general.skippedQuestions.Clone() + const skippedQuestionsButton = Translations.t.general.skippedQuestions .onClick(() => { skippedQuestions.setData([]); }) diff --git a/UI/ShowDataLayer/ShowDataLayer.ts b/UI/ShowDataLayer/ShowDataLayer.ts index 2f957ed15..e8cfb43da 100644 --- a/UI/ShowDataLayer/ShowDataLayer.ts +++ b/UI/ShowDataLayer/ShowDataLayer.ts @@ -6,6 +6,7 @@ import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; import FeatureInfoBox from "../Popup/FeatureInfoBox"; import {ShowDataLayerOptions} from "./ShowDataLayerOptions"; import {ElementStorage} from "../../Logic/ElementStorage"; +import Hash from "../../Logic/Web/Hash"; export default class ShowDataLayer { @@ -237,7 +238,6 @@ export default class ShowDataLayer { infobox.isShown.addCallback(isShown => { if (!isShown) { - this._selectedElement?.setData(undefined); leafletLayer.closePopup() } }); @@ -249,7 +249,7 @@ export default class ShowDataLayer { } }); - + // Add the feature to the index to open the popup when needed this.leafletLayersPerId.set(feature.properties.id + feature.geometry.type, { diff --git a/assets/themes/grb_import/grb.json b/assets/themes/grb_import/grb.json index 4f0d99d9d..ed5a02aac 100644 --- a/assets/themes/grb_import/grb.json +++ b/assets/themes/grb_import/grb.json @@ -20,13 +20,99 @@ "startZoom": 14, "widenFactor": 2, "socialImage": "", + "overpassMaxZoom": 18, + "osmApiTileSize": 17, "layers": [ + { + "id": "OSM-buildings", + "name": "All OSM-buildings", + "source": { + "osmTags": "building~*", + "maxCacheAge": 0 + }, + "minzoom": 18, + "width": { + "render": "2" + }, + "color": { + "render": "#00c", + "mappings": [ + { + "if": "building=house", + "then": "#a00" + }, + { + "if": "building=shed", + "then": "#563e02" + }, + { + "if": { + "or": ["building=garage","building=garages"] + }, + "then": "#f9bfbb" + }, + { + "if": "building=yes", + "then": "#0774f2" + } + ] + }, + "title": "OSM-gebouw", + "tagRenderings": [ + "all_tags" + ] + }, + { + "id": "All OSM objects", + "name": "All OSM Objects", + "source": { + "osmTags":{ + "and": [ + "id~*", + "landuse=", + "place=", + "disused:power=", + "power=", + "type!=boundary", + "boundary=", + { + "or": [ + "level=", + "level=0" + ] + }, + { + "or": [ + "layer=0", + "layer=" + ] + } + ] + }, + "maxCacheAge": 0 + }, + "minzoom": 18, + "color": { + "render": "#00c" + }, + "width": { + "render": "1" + }, + "title": { + "render": { + "*": "OSM-Object" + } + }, + "tagRenderings": [ + "all_tags" + ] + }, { "id": "osm-fixmes", "name": { "nl": "Fixmes op gebouwen" }, - "minzoom": 12, + "minzoom": 21, "source": { "maxCacheAge": 0, "osmTags": { @@ -232,9 +318,29 @@ "name": "GRB geometries", "title": "GRB outline", "minzoom": 19, + "calculatedTags": [ + "_overlaps_with=feat.overlapWith('OSM-buildings').filter(f => f.overlap > 1 && feat.properties._surface - f.overlap < 5)[0]", + "_osm_obj:source:ref=JSON.parse(feat.properties._overlaps_with).feat.properties['source:geometry:ref']", + "_osm_obj:source:date=JSON.parse(feat.properties._overlaps_with).feat.properties['source:geometry:date']", + "_imported_osm_object_found= feat.properties['_osm_obj:source:ref'] == feat.properties['source:geometry:entity'] + '/' + feat.properties['source:geometry:oidn']", + "_grb_date=feat.properties['source:geometry:date'].replace(/\\//g,'-')", + "_imported_osm_still_fresh= feat.properties['_osm_obj:source:date'] == feat.properties._grb_date" + + ], "tagRenderings": [ "all_tags" - ] + ], + "color": { + "render": "#00a", + "mappings": [ + { + "if": { + "and": ["_imported_osm_object_found=true","_imported_osm_still_fresh=true"] + }, + "then": "#0f0" + } + ] + } } ], "hideFromOverview": true, diff --git a/assets/themes/uk_addresses/license_info.json b/assets/themes/uk_addresses/license_info.json index 7d805cee4..4bcce8003 100644 --- a/assets/themes/uk_addresses/license_info.json +++ b/assets/themes/uk_addresses/license_info.json @@ -1,4 +1,34 @@ [ + { + "path": "Commemorative_plaque_on_Elizabeth_House_-_geograph.org.uk_-_2693028.jpg", + "license": "CC-BY-SA 2.0 Unported", + "authors": [ + "Basher Eyre" + ], + "sources": [ + "https://commons.wikimedia.org/wiki/File:Commemorative_plaque_on_Elizabeth_House_-_geograph.org.uk_-_2693028.jpg" + ] + }, + { + "path": "Plaque,_Raphoe_House_-_geograph.org.uk_-_1925685.jpg", + "license": "CC-BY-SA 2.0", + "authors": [ + "Kenneth Allen" + ], + "sources": [ + "https://commons.wikimedia.org/wiki/File:Plaque,_Raphoe_House_-_geograph.org.uk_-_1925685.jpg" + ] + }, + { + "path": "Plaque,_Séamus_Roddy_House_-_geograph.org.uk_-_2000318.jpg", + "license": "CC-BY-SA 2.0 Unported", + "authors": [ + "Kenneth Allen" + ], + "sources": [ + "https://commons.wikimedia.org/wiki/File:Plaque,_S%C3%A9amus_Roddy_House_-_geograph.org.uk_-_2000318.jpg" + ] + }, { "path": "housenumber_add.svg", "license": "CC0", diff --git a/assets/themes/uk_addresses/uk_addresses.json b/assets/themes/uk_addresses/uk_addresses.json index aa0aeed0b..88d6dec27 100644 --- a/assets/themes/uk_addresses/uk_addresses.json +++ b/assets/themes/uk_addresses/uk_addresses.json @@ -27,6 +27,9 @@ "widenFactor": 1.01, "socialImage": "", "hideFromOverview": true, + "enableShareScreen": false, + "enableMoreQuests": false, + "clustering": { "minNeededFeatures": 25, "maxZoom": 16 @@ -52,11 +55,12 @@ "#geoJson": "https://raw.githubusercontent.com/pietervdvn/MapComplete/develop/assets/themes/uk_addresses/islington_small_piece.geojson", "geoJson": "https://osm-uk-addresses.russss.dev/addresses/{z}/{x}/{y}.json", "osmTags": "inspireid~*", - "geoJsonZoomLevel": 16, - "isOsmCache": false + "geoJsonZoomLevel": 18, + "isOsmCache": false, + "maxCacheAge": 0 }, "name": "Addresses to check", - "minzoom": 14, + "minzoom": 18, "wayHandling": 1, "icon": { "render": "./assets/themes/uk_addresses/housenumber_unknown.svg", @@ -123,6 +127,7 @@ }, "minzoom": 18, "source": { + "maxCacheAge": 0, "osmTags": { "or": [ "addr:housenumber~*", @@ -188,6 +193,33 @@ } ] }, + { + "id": "uk_addresses_housename", + "question": "What is the name of this house?
This is normally indicated on a plaque.
Do NOT add names of inhabitants!
", + "render": "This house is named {addr:housename}", + "freeform": { + "key": "addr:housename", + "addExtraTags": [ + "nohousename=" + ] + }, + "mappings": [ + { + "if": "nohousename=yes", + "then": "This building has no housename" + }, + { + "if": { + "and": [ + "addr:housename=", + "nohousenumber!=yes" + ] + }, + "then": "This building has no housename", + "hideInAnswer": true + } + ] + }, { "id": "uk_addresses_street", "render": { @@ -219,10 +251,40 @@ } ], "condition": { - "and": [ - "nohousenumber!~yes" + "or": [ + "nohousenumber!=yes", + "nohousename!=yes" ] } + }, + { + "id": "fixme", + "render": "Fixme description{render}", + "question": { + "en": "What should be fixed here? Please explain" + }, + "freeform": { + "key": "fixme" + }, + "mappings": [ + { + "if": "fixme=", + "then": "No fixme - write something here to explain complicated cases" + } + ] + }, + "questions", + { + "id": "address-sign-image", + "render": { + "en": "{image_carousel(image:address)}
{image_upload(image:address, Add image of the address)}" + } + }, + { + "id": "general_images", + "render": { + "en": "{image_carousel()}" + } } ], "icon": { @@ -245,7 +307,7 @@ ] }, "width": { - "render": "8" + "render": "1" }, "iconSize": { "render": "40,40,center" @@ -274,6 +336,7 @@ "id": "named_streets", "minzoom": 18, "source": { + "maxCacheAge": 0, "osmTags": { "and": [ "highway~*", diff --git a/scripts/slice.ts b/scripts/slice.ts index 74673035b..342dc22f9 100644 --- a/scripts/slice.ts +++ b/scripts/slice.ts @@ -106,7 +106,7 @@ async function main(args: string[]) { console.log("Loaded all", allFeatures.length, "points") - const keysToRemove = ["ID", "STRAATNMID", "NISCODE", "GEMEENTE", "POSTCODE", "HERKOMST"] + const keysToRemove = ["STRAATNMID", "GEMEENTE", "POSTCODE"] for (const f of allFeatures) { for (const keyToRm of keysToRemove) { delete f.properties[keyToRm] From d4c7729f8d273ec1af6cc07b933607917e0d1d92 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 28 Oct 2021 00:10:45 +0200 Subject: [PATCH 31/95] More GRB import work --- assets/themes/grb_import/grb.json | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/assets/themes/grb_import/grb.json b/assets/themes/grb_import/grb.json index ed5a02aac..af9672582 100644 --- a/assets/themes/grb_import/grb.json +++ b/assets/themes/grb_import/grb.json @@ -309,6 +309,7 @@ }, { "id": "GRB", + "wayHandling": 1, "source": { "geoJson": "https://betadata.grbosm.site/grb?bbox={x_min},{y_min},{x_max},{y_max}", "geoJsonZoomLevel": 18, @@ -321,13 +322,22 @@ "calculatedTags": [ "_overlaps_with=feat.overlapWith('OSM-buildings').filter(f => f.overlap > 1 && feat.properties._surface - f.overlap < 5)[0]", "_osm_obj:source:ref=JSON.parse(feat.properties._overlaps_with).feat.properties['source:geometry:ref']", - "_osm_obj:source:date=JSON.parse(feat.properties._overlaps_with).feat.properties['source:geometry:date']", + "_osm_obj:source:date=JSON.parse(feat.properties._overlaps_with).feat.properties['source:geometry:date'].replace(/\\//g,'-')", "_imported_osm_object_found= feat.properties['_osm_obj:source:ref'] == feat.properties['source:geometry:entity'] + '/' + feat.properties['source:geometry:oidn']", "_grb_date=feat.properties['source:geometry:date'].replace(/\\//g,'-')", - "_imported_osm_still_fresh= feat.properties['_osm_obj:source:date'] == feat.properties._grb_date" - + "_imported_osm_still_fresh= feat.properties['_osm_obj:source:date'] == feat.properties._grb_date", + "_grb_synced=feat.properties._imported_osm_object_found == 'true' && feat.properties._imported_osm_still_fresh == 'true'" ], "tagRenderings": [ + { + "id": "sync_status", + "mappings": [ + { + "if": "_grb_synced=true", + "then": "OSM has the latest data!" + } + ] + }, "all_tags" ], "color": { @@ -335,7 +345,7 @@ "mappings": [ { "if": { - "and": ["_imported_osm_object_found=true","_imported_osm_still_fresh=true"] + "and": ["_grb_synced=true"] }, "then": "#0f0" } From 5fbc778624a419a565ecfc6bdfe5ecc05a11e053 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 28 Oct 2021 00:17:43 +0200 Subject: [PATCH 32/95] Translation reset --- langs/layers/de.json | 420 +++++++++++++++++++++++++++++++++++++- langs/layers/en.json | 10 +- langs/layers/it.json | 11 + langs/layers/ja.json | 11 + langs/layers/nb_NO.json | 10 + langs/layers/nl.json | 6 +- langs/layers/ru.json | 51 +++++ langs/layers/zh_Hant.json | 11 + langs/themes/de.json | 5 + langs/themes/nl.json | 45 +++- scripts/lint.ts | 3 +- 11 files changed, 569 insertions(+), 14 deletions(-) diff --git a/langs/layers/de.json b/langs/layers/de.json index 5706fd43e..3917d519b 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -917,6 +917,311 @@ } } }, + "charging_station": { + "filter": { + "0": { + "options": { + "0": { + "question": "Alle Fahrzeugtypen" + }, + "1": { + "question": "Ladestation für Fahrräder" + }, + "2": { + "question": "Ladestation für Autos" + } + } + }, + "1": { + "options": { + "0": { + "question": "Nur funktionierende Ladestationen" + } + } + }, + "2": { + "options": { + "0": { + "question": "Alle Anschlüsse" + }, + "3": { + "question": "Hat einen
Chademo
Stecker" + }, + "7": { + "question": "Hat einen
Tesla Supercharger
Stecker" + } + } + } + }, + "presets": { + "0": { + "title": "Ladestation" + } + }, + "tagRenderings": { + "Auth phone": { + "question": "Wie lautet die Telefonnummer für den Authentifizierungsanruf oder die SMS?", + "render": "Authentifizierung durch Anruf oder SMS an {authentication:phone_call:number}" + }, + "Authentication": { + "mappings": { + "0": { + "then": "Authentifizierung durch eine Mitgliedskarte" + }, + "1": { + "then": "Authentifizierung durch eine App" + }, + "2": { + "then": "Authentifizierung per Anruf ist möglich" + }, + "3": { + "then": "Authentifizierung per Anruf ist möglich" + }, + "4": { + "then": "Authentifizierung über NFC ist möglich" + }, + "5": { + "then": "Authentifizierung über Geldkarte ist möglich" + }, + "6": { + "then": "Authentifizierung per Debitkarte ist möglich" + }, + "7": { + "then": "Das Aufladen ist hier (auch) ohne Authentifizierung möglich" + } + }, + "question": "Welche Authentifizierung ist an der Ladestation möglich?" + }, + "Available_charging_stations (generated)": { + "mappings": { + "5": { + "then": "
Chademo
" + }, + "6": { + "then": "
Typ 1 mit Kabel (J1772)
" + }, + "7": { + "then": "
Typ 1 mit Kabel (J1772)
" + }, + "8": { + "then": "
Typ 1 ohne Kabel (J1772)
" + }, + "9": { + "then": "
Typ 1 ohne Kabel (J1772)
" + }, + "10": { + "then": "
Typ 1 CCS (auch bekannt als Typ 1 Combo)
" + }, + "11": { + "then": "
Typ 1 CCS (auch bekannt als Typ 1 Combo)
" + }, + "12": { + "then": "
Tesla Supercharger
" + }, + "13": { + "then": "
Tesla Supercharger
" + }, + "14": { + "then": "
Typ 2 (Mennekes)
" + }, + "15": { + "then": "
Typ 2 (Mennekes)
" + }, + "16": { + "then": "
Typ 2 CCS (Mennekes)
" + }, + "17": { + "then": "
Typ 2 CCS (Mennekes)
" + }, + "18": { + "then": "
Typ 2 mit Kabel (Mennekes)
" + }, + "19": { + "then": "
Typ 2 mit Kabel (Mennekes)
" + }, + "20": { + "then": "
Tesla Supercharger CCS (Typ 2 CSS)
" + }, + "21": { + "then": "
Tesla Supercharger CCS (Typ 2 CSS)
" + }, + "26": { + "then": "
USB zum Laden von Smartphones oder Elektrokleingeräten
" + }, + "27": { + "then": "
USB zum Laden von Smartphones und Elektrokleingeräten
" + }, + "30": { + "then": "
Bosch Active Connect mit 5 Pins und Kabel
" + }, + "31": { + "then": "
Bosch Active Connect mit 5 Pins und Kabel
" + } + }, + "question": "Welche Ladestationen gibt es hier?" + }, + "Network": { + "mappings": { + "0": { + "then": "Nicht Teil eines größeren Netzwerks" + }, + "1": { + "then": "Nicht Teil eines größeren Netzwerks" + } + }, + "question": "Ist diese Ladestation Teil eines Netzwerks?", + "render": "Teil des Netzwerks {network}" + }, + "OH": { + "mappings": { + "0": { + "then": "durchgehend geöffnet (auch an Feiertagen)" + } + }, + "question": "Wann ist diese Ladestation geöffnet?" + }, + "Operational status": { + "mappings": { + "0": { + "then": "Diese Ladestation funktioniert" + }, + "1": { + "then": "Diese Ladestation ist kaputt" + }, + "2": { + "then": "Hier ist eine Ladestation geplant" + }, + "3": { + "then": "Hier wird eine Ladestation gebaut" + }, + "4": { + "then": "Diese Ladestation wurde dauerhaft deaktiviert und wird nicht mehr benutzt, ist aber noch sichtbar" + } + }, + "question": "Ist dieser Ladepunkt in Betrieb?" + }, + "Operator": { + "mappings": { + "0": { + "then": "Eigentlich ist {operator} das Netzwerk" + } + }, + "question": "Wer ist der Betreiber dieser Ladestation?", + "render": "Diese Ladestation wird betrieben von {operator}" + }, + "Parking:fee": { + "mappings": { + "0": { + "then": "Keine zusätzlichen Parkgebühren beim Laden" + }, + "1": { + "then": "Beim Laden ist eine zusätzliche Parkgebühr zu entrichten" + } + }, + "question": "Muss man beim Laden eine Parkgebühr bezahlen?" + }, + "Type": { + "mappings": { + "0": { + "then": "Fahrräder können hier geladen werden" + }, + "1": { + "then": "Autos können hier geladen werden" + }, + "2": { + "then": " Roller können hier geladen werden" + }, + "3": { + "then": "Lastkraftwagen (LKW) können hier geladen werden" + }, + "4": { + "then": "Busse können hier geladen werden" + } + }, + "question": "Welche Fahrzeuge dürfen hier geladen werden?" + }, + "access": { + "question": "Wer darf diese Ladestation benutzen?", + "render": "Zugang ist {access}" + }, + "capacity": { + "question": "Wie viele Fahrzeuge können hier gleichzeitig geladen werden?", + "render": "{capacity} Fahrzeuge können hier gleichzeitig geladen werden" + }, + "email": { + "question": "Wie ist die Email-Adresse des Betreibers?", + "render": "Bei Problemen senden Sie eine E-Mail an {email}" + }, + "maxstay": { + "mappings": { + "0": { + "then": "Keine Höchstparkdauer" + } + }, + "question": "Was ist die Höchstdauer des Aufenthalts hier?", + "render": "Die maximale Parkzeit beträgt {canonical(maxstay)}" + }, + "payment-options": { + "override": { + "mappings+": { + "0": { + "then": "Bezahlung mit einer speziellen App" + }, + "1": { + "then": "Bezahlung mit einer Mitgliedskarte" + } + } + } + }, + "phone": { + "question": "Welche Nummer kann man anrufen, wenn es ein Problem mit dieser Ladestation gibt?", + "render": "Bei Problemen, anrufen unter {phone}" + }, + "ref": { + "question": "Wie lautet die Kennung dieser Ladestation?", + "render": "Die Kennziffer ist {ref}" + }, + "website": { + "question": "Wie ist die Webseite des Betreibers?", + "render": "Weitere Informationen auf {website}" + } + }, + "units": { + "0": { + "applicableUnits": { + "0": { + "human": " Minuten", + "humanSingular": " Minute" + }, + "1": { + "human": " Stunden", + "humanSingular": " Stunde" + }, + "2": { + "human": " Tage", + "humanSingular": " Tag" + } + } + }, + "1": { + "applicableUnits": { + "0": { + "human": "Volt" + } + } + }, + "3": { + "applicableUnits": { + "0": { + "human": "Kilowatt" + }, + "1": { + "human": "Megawatt" + } + } + } + } + }, "crossings": { "description": "Übergänge für Fußgänger und Radfahrer", "name": "Kreuzungen", @@ -996,6 +1301,15 @@ "tagRenderings": { "Cycleway type for a road": { "mappings": { + "0": { + "then": "Es gibt eine geteilte Fahrspur" + }, + "1": { + "then": "Es gibt eine Spur neben der Straße (getrennt durch eine Straßenmarkierung)" + }, + "2": { + "then": "Es gibt einen Weg, aber keinen Radweg, der auf der Karte getrennt von dieser Straße eingezeichnet ist." + }, "3": { "then": "Hier ist ein getrennter Radweg vorhanden" }, @@ -1016,6 +1330,18 @@ "1": { "then": "Geeignet für dünne Reifen: Rennrad" }, + "2": { + "then": "Geeignet für normale Reifen: Fahrrad, Rollstuhl, Scooter" + }, + "3": { + "then": "Geeignet für breite Reifen: Trekkingfahrrad, Auto, Rikscha" + }, + "4": { + "then": "Geeignet für Fahrzeuge mit großer Bodenfreiheit: leichte Geländewagen" + }, + "5": { + "then": "Geeignet für Geländefahrzeuge: schwerer Geländewagen" + }, "6": { "then": "Geeignet für Geländefahrzeuge: Traktor, ATV" }, @@ -1036,9 +1362,21 @@ "2": { "then": "Der Radweg ist aus Asphalt" }, + "3": { + "then": "Dieser Fahrradweg besteht aus ebenen Pflastersteinen" + }, "4": { "then": "Der Radweg ist aus Beton" }, + "5": { + "then": "Dieser Radweg besteht aus Kopfsteinpflaster" + }, + "6": { + "then": "Dieser Fahrradweg besteht aus unregelmäßigem, unbehauenem Kopfsteinpflaster" + }, + "7": { + "then": "Dieser Fahrradweg besteht aus regelmäßigem, behauenem Kopfsteinpflaster" + }, "8": { "then": "Der Radweg ist aus Holz" }, @@ -1055,10 +1393,14 @@ "then": "Dieser Radweg besteht aus Rohboden" } }, + "question": "Was ist der Belag dieses Radwegs?", "render": "Der Radweg ist aus {cycleway:surface}" }, "Is this a cyclestreet? (For a road)": { "mappings": { + "0": { + "then": "Dies ist eine Fahrradstraße in einer 30km/h Zone." + }, "1": { "then": "Dies ist eine Fahrradstraße" }, @@ -1085,19 +1427,36 @@ "4": { "then": "Die Höchstgeschwindigkeit ist 90 km/h" } - } + }, + "question": "Was ist die Höchstgeschwindigkeit auf dieser Straße?", + "render": "Die Höchstgeschwindigkeit auf dieser Straße beträgt {maxspeed} km/h" }, "Surface of the road": { "mappings": { + "0": { + "then": "Dieser Radweg ist nicht befestigt" + }, "1": { "then": "Dieser Radweg hat einen festen Belag" }, "2": { "then": "Der Radweg ist aus Asphalt" }, + "3": { + "then": "Dieser Fahrradweg besteht aus ebenen Pflastersteinen" + }, "4": { "then": "Der Radweg ist aus Beton" }, + "5": { + "then": "Dieser Radweg besteht aus Kopfsteinpflaster" + }, + "6": { + "then": "Dieser Fahrradweg besteht aus unregelmäßigem, unbehauenem Kopfsteinpflaster" + }, + "7": { + "then": "Dieser Fahrradweg besteht aus regelmäßigem, behauenem Kopfsteinpflaster" + }, "8": { "then": "Der Radweg ist aus Holz" }, @@ -1114,6 +1473,7 @@ "then": "Dieser Radweg besteht aus Rohboden" } }, + "question": "Was ist der Belag dieser Straße?", "render": "Der Radweg ist aus {surface}" }, "Surface of the street": { @@ -1124,6 +1484,18 @@ "1": { "then": "Geeignet für dünne Reifen: Rennrad" }, + "2": { + "then": "Geeignet für normale Reifen: Fahrrad, Rollstuhl, Scooter" + }, + "3": { + "then": "Geeignet für breite Reifen: Trekkingfahrrad, Auto, Rikscha" + }, + "4": { + "then": "Geeignet für Fahrzeuge mit großer Bodenfreiheit: leichte Geländewagen" + }, + "5": { + "then": "Geeignet für Geländefahrzeuge: schwerer Geländewagen" + }, "6": { "then": "Geeignet für spezielle Geländewagen: Traktor, ATV" }, @@ -1135,16 +1507,29 @@ }, "cyclelan-segregation": { "mappings": { + "0": { + "then": "Der Radweg ist abgegrenzt durch eine gestrichelte Linie" + }, + "1": { + "then": "Der Radweg ist abgegrenzt durch eine durchgezogene Linie" + }, + "2": { + "then": "Der Radweg ist abgegrenzt durch eine Parkspur" + }, "3": { "then": "Dieser Radweg ist getrennt durch einen Bordstein" } - } + }, + "question": "Wie ist der Radweg von der Straße abgegrenzt?" }, "cycleway-lane-track-traffic-signs": { "mappings": { "0": { "then": "Vorgeschriebener Radweg " }, + "1": { + "then": "Vorgeschriebener Radweg (mit Zusatzschild)
" + }, "2": { "then": "Getrennter Fuß-/Radweg " }, @@ -1159,16 +1544,29 @@ }, "cycleway-segregation": { "mappings": { + "0": { + "then": "Der Radweg ist abgegrenzt durch eine gestrichelte Linie" + }, + "1": { + "then": "Der Radweg ist abgegrenzt durch eine durchgezogene Linie" + }, + "2": { + "then": "Der Radweg ist abgegrenzt durch eine Parkspur" + }, "3": { "then": "Dieser Radweg ist getrennt durch einen Bordstein" } - } + }, + "question": "Wie ist der Radweg von der Straße abgegrenzt?" }, "cycleway-traffic-signs": { "mappings": { "0": { "then": "Vorgeschriebener Radweg " }, + "1": { + "then": "Vorgeschriebener Radweg (mit Zusatzschild)
" + }, "2": { "then": "Getrennter Fuß-/Radweg " }, @@ -1198,8 +1596,15 @@ } } }, + "cycleways_and_roads-cycleway:buffer": { + "question": "Wie breit ist der Abstand zwischen Radweg und Straße?", + "render": "Der Sicherheitsabstand zu diesem Radweg beträgt {cycleway:buffer} m" + }, "is lit?": { "mappings": { + "0": { + "then": "Diese Straße ist beleuchtet" + }, "1": { "then": "Diese Straße ist nicht beleuchtet" }, @@ -1824,6 +2229,15 @@ }, "public_bookcase": { "description": "Ein Bücherschrank am Straßenrand mit Büchern, für jedermann zugänglich", + "filter": { + "2": { + "options": { + "0": { + "question": "Innen oder Außen" + } + } + } + }, "name": "Bücherschränke", "presets": { "0": { diff --git a/langs/layers/en.json b/langs/layers/en.json index 634222f8b..2a6122f4f 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -1005,7 +1005,7 @@ "name": "Charging stations", "presets": { "0": { - "title": "electrical outlet to charge e-bikes" + "title": "Charging station" }, "1": { "title": "charging station for e-bikes" @@ -1034,7 +1034,7 @@ "then": "Authentication via phone call is available" }, "3": { - "then": "Authentication via SMS is available" + "then": "Authentication via phone call is available" }, "4": { "then": "Authentication via NFC is available" @@ -1150,7 +1150,7 @@ "then": "
Bosch Active Connect with 5 pins and cable
" } }, - "question": "Which charging connections are available here?" + "question": "Which charging stations are available here?" }, "Network": { "mappings": { @@ -1215,7 +1215,7 @@ "Type": { "mappings": { "0": { - "then": "Bcycles can be charged here" + "then": "bicycles can be charged here" }, "1": { "then": "Cars can be charged here" @@ -1376,7 +1376,7 @@ "render": "Reference number is {ref}" }, "website": { - "question": "What is the website where one can find more information about this charging station?", + "question": "What is the website of the operator?", "render": "More info on {website}" } }, diff --git a/langs/layers/it.json b/langs/layers/it.json index 95957591a..fa0df9532 100644 --- a/langs/layers/it.json +++ b/langs/layers/it.json @@ -745,6 +745,17 @@ "render": "Oggetto relativo alle bici" } }, + "charging_station": { + "tagRenderings": { + "Network": { + "question": "A quale rete appartiene questa stazione di ricarica?", + "render": "{network}" + }, + "OH": { + "question": "Quali sono gli orari di apertura di questa stazione di ricarica?" + } + } + }, "defibrillator": { "icon": { "mappings": { diff --git a/langs/layers/ja.json b/langs/layers/ja.json index 7a35bef11..a3baa6845 100644 --- a/langs/layers/ja.json +++ b/langs/layers/ja.json @@ -72,6 +72,17 @@ "render": "アートワーク" } }, + "charging_station": { + "tagRenderings": { + "Network": { + "question": "この充電ステーションの運営チェーンはどこですか?", + "render": "{network}" + }, + "OH": { + "question": "この充電ステーションはいつオープンしますか?" + } + } + }, "food": { "tagRenderings": { "friture-take-your-container": { diff --git a/langs/layers/nb_NO.json b/langs/layers/nb_NO.json index f4f9920c0..80a29ad8d 100644 --- a/langs/layers/nb_NO.json +++ b/langs/layers/nb_NO.json @@ -170,6 +170,16 @@ } } }, + "charging_station": { + "tagRenderings": { + "Network": { + "render": "{network}" + }, + "OH": { + "question": "Når åpnet denne ladestasjonen?" + } + } + }, "ghost_bike": { "name": "Spøkelsessykler", "title": { diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 070092b83..545fc977b 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -1125,7 +1125,7 @@ "name": "Oplaadpunten", "presets": { "0": { - "title": "laadpunt met gewone stekker(s) (bedoeld om electrische fietsen op te laden)" + "title": "gewone stekker (bedoeld om electrische fietsen op te laden)" }, "1": { "title": "oplaadpunt voor elektrische fietsen" @@ -1361,10 +1361,10 @@ "then": "Toegankelijk voor iedereen (mogelijks met aanmelden en/of te betalen)" }, "2": { - "then": "Enkel klanten van de bijhorende plaats mogen dit oplaadpunt gebruiken
Bv. op de parking van een hotel en enkel toegankelijk voor klanten van dit hotel" + "then": "Enkel klanten van de bijhorende plaats mogen dit oplaadpunt gebruiken
Bijvoorbeeld een oplaadpunt op de parking van een restaurant dat enkel door klanten van het restaurant gebruikt mag worden" }, "3": { - "then": "Niet toegankelijk voor het publiek
Bv. enkel toegankelijk voor de eigenaar, medewerkers ,... " + "then": "Niet toegankelijk voor het publiek Enkel toegankelijk voor de eigenaar, medewerkers ,... " } }, "question": "Wie mag er dit oplaadpunt gebruiken?", diff --git a/langs/layers/ru.json b/langs/layers/ru.json index 0d1d963bd..d05c7ef76 100644 --- a/langs/layers/ru.json +++ b/langs/layers/ru.json @@ -631,6 +631,57 @@ } } }, + "charging_station": { + "presets": { + "0": { + "title": "Зарядная станция" + } + }, + "tagRenderings": { + "Network": { + "question": "К какой сети относится эта станция?", + "render": "{network}" + }, + "OH": { + "question": "В какое время работает эта зарядная станция?" + } + }, + "units": { + "0": { + "applicableUnits": { + "0": { + "human": " минут", + "humanSingular": " минута" + }, + "1": { + "human": " часов", + "humanSingular": " час" + }, + "2": { + "human": " дней", + "humanSingular": " день" + } + } + }, + "1": { + "applicableUnits": { + "0": { + "human": "Вольт" + } + } + }, + "3": { + "applicableUnits": { + "0": { + "human": "киловатт" + }, + "1": { + "human": "мегаватт" + } + } + } + } + }, "crossings": { "presets": { "1": { diff --git a/langs/layers/zh_Hant.json b/langs/layers/zh_Hant.json index 59f59f5aa..951be71a1 100644 --- a/langs/layers/zh_Hant.json +++ b/langs/layers/zh_Hant.json @@ -445,6 +445,17 @@ "render": "單車停車場" } }, + "charging_station": { + "tagRenderings": { + "Network": { + "question": "充電站所屬的網路是?", + "render": "{network}" + }, + "OH": { + "question": "何時是充電站開放使用的時間?" + } + } + }, "ghost_bike": { "name": "幽靈單車", "title": { diff --git a/langs/themes/de.json b/langs/themes/de.json index de7d8075b..237a48a78 100644 --- a/langs/themes/de.json +++ b/langs/themes/de.json @@ -1059,6 +1059,11 @@ } }, "shortDescription": "Helfen Sie beim Aufbau eines offenen Datensatzes britischer Adressen", + "tileLayerSources": { + "0": { + "name": "Grenzverläufe gemäß osmuk.org" + } + }, "title": "Adressen in Großbritannien" }, "waste_basket": { diff --git a/langs/themes/nl.json b/langs/themes/nl.json index b8431d4f5..06fb6325a 100644 --- a/langs/themes/nl.json +++ b/langs/themes/nl.json @@ -729,7 +729,50 @@ "grb": { "description": "GRB Fixup", "layers": { - "0": { + "2": { + "description": "Dit gebouw heeft een foutmelding", + "name": "Fixmes op gebouwen", + "tagRenderings": { + "grb-fixme": { + "mappings": { + "0": { + "then": "Geen fixme" + } + }, + "question": "Wat zegt de fixme?", + "render": "De fixme is {fixme}" + }, + "grb-housenumber": { + "mappings": { + "0": { + "then": "Geen huisnummer" + } + }, + "question": "Wat is het huisnummer?", + "render": "Het huisnummer is {addr:housenumber}" + }, + "grb-min-level": { + "question": "Hoeveel verdiepingen ontbreken?", + "render": "Dit gebouw begint maar op de {building:min_level} verdieping" + }, + "grb-street": { + "question": "Wat is de straat?", + "render": "De straat is {addr:street}" + }, + "grb-unit": { + "render": "De wooneenheid-aanduiding is {addr:unit} " + } + }, + "title": { + "mappings": { + "0": { + "then": "{fixme}" + } + }, + "render": "{addr:street} {addr:housenumber}" + } + }, + "4": { "description": "Dit gebouw heeft een foutmelding", "name": "Fixmes op gebouwen", "tagRenderings": { diff --git a/scripts/lint.ts b/scripts/lint.ts index 63d8ff63a..92ec2facc 100644 --- a/scripts/lint.ts +++ b/scripts/lint.ts @@ -62,7 +62,7 @@ function fixLayerConfig(config: LayerConfigJson): void { } - /*delete config["color"] + delete config["color"] delete config["width"] delete config["dashArray"] @@ -72,7 +72,6 @@ function fixLayerConfig(config: LayerConfigJson): void { delete config["iconSize"] delete config["rotation"] delete config["wayHandling"] - */ } From ea61e17268d66b22756de9a9e7e4a4152335834e Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 28 Oct 2021 00:32:47 +0200 Subject: [PATCH 33/95] Translation regeneration --- .../charging_station/charging_station.json | 4584 ++++++++--------- .../public_bookcase/public_bookcase.json | 1012 ++-- langs/shared-questions/ca.json | 1 - langs/shared-questions/eo.json | 1 - langs/shared-questions/es.json | 1 - langs/shared-questions/fi.json | 1 - langs/shared-questions/hu.json | 1 - langs/shared-questions/ja.json | 1 - langs/shared-questions/nan.json | 1 - langs/shared-questions/zh_HANŨS.json | 1 - langs/shared-questions/zh_Hans.json | 1 - langs/themes/nan.json | 1 - langs/themes/pt.json | 1 - langs/themes/zh_HANŨS.json | 1 - langs/themes/zh_Hans.json | 1 - 15 files changed, 2798 insertions(+), 2811 deletions(-) delete mode 100644 langs/shared-questions/ca.json delete mode 100644 langs/shared-questions/eo.json delete mode 100644 langs/shared-questions/es.json delete mode 100644 langs/shared-questions/fi.json delete mode 100644 langs/shared-questions/hu.json delete mode 100644 langs/shared-questions/ja.json delete mode 100644 langs/shared-questions/nan.json delete mode 100644 langs/shared-questions/zh_HANŨS.json delete mode 100644 langs/shared-questions/zh_Hans.json delete mode 100644 langs/themes/nan.json delete mode 100644 langs/themes/pt.json delete mode 100644 langs/themes/zh_HANŨS.json delete mode 100644 langs/themes/zh_Hans.json diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index 7427e6b4e..b492e7cc2 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -1,2310 +1,2310 @@ { - "id": "charging_station", - "name": { - "en": "Charging stations", - "nl": "Oplaadpunten" - }, - "minzoom": 10, - "source": { - "osmTags": { - "or": [ - "amenity=charging_station", - "disused:amenity=charging_station", - "planned:amenity=charging_station", - "construction:amenity=charging_station" - ] - } - }, - "title": { - "render": { - "en": "Charging station", - "nl": "Oplaadpunten" - } - }, - "description": { - "en": "A charging station", - "nl": "Oplaadpunten" - }, - "tagRenderings": [ - "images", - { - "id": "Type", - "#": "Allowed vehicle types", - "question": { - "en": "Which vehicles are allowed to charge here?", - "nl": "Welke voertuigen kunnen hier opgeladen worden?", - "de": "Welche Fahrzeuge dürfen hier geladen werden?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "bicycle=yes", - "ifnot": "bicycle=no", - "then": { - "en": "bicycles can be charged here", - "nl": "Fietsen kunnen hier opgeladen worden", - "de": "Fahrräder können hier geladen werden" - } - }, - { - "if": "motorcar=yes", - "ifnot": "motorcar=no", - "then": { - "en": "Cars can be charged here", - "nl": "Elektrische auto's kunnen hier opgeladen worden", - "de": "Autos können hier geladen werden" - } - }, - { - "if": "scooter=yes", - "ifnot": "scooter=no", - "then": { - "en": "Scooters can be charged here", - "nl": "Electrische scooters (snorfiets of bromfiets) kunnen hier opgeladen worden", - "de": " Roller können hier geladen werden" - } - }, - { - "if": "hgv=yes", - "ifnot": "hgv=no", - "then": { - "en": "Heavy good vehicles (such as trucks) can be charged here", - "nl": "Vrachtwagens kunnen hier opgeladen worden", - "de": "Lastkraftwagen (LKW) können hier geladen werden" - } - }, - { - "if": "bus=yes", - "ifnot": "bus=no", - "then": { - "en": "Buses can be charged here", - "nl": "Bussen kunnen hier opgeladen worden", - "de": "Busse können hier geladen werden" - } - } - ] + "id": "charging_station", + "name": { + "en": "Charging stations", + "nl": "Oplaadpunten" }, - { - "id": "access", - "question": { - "en": "Who is allowed to use this charging station?", - "nl": "Wie mag er dit oplaadpunt gebruiken?", - "de": "Wer darf diese Ladestation benutzen?" - }, - "render": { - "en": "Access is {access}", - "nl": "Toegang voor {access}", - "de": "Zugang ist {access}" - }, - "freeform": { - "key": "access", - "addExtraTags": [ - "fixme=Freeform field used for access - doublecheck the value" - ] - }, - "mappings": [ - { - "if": "access=yes", - "then": { - "en": "Anyone can use this charging station (payment might be needed)", - "nl": "Toegankelijk voor iedereen (mogelijks met aanmelden en/of te betalen)" - } - }, - { - "if": { + "minzoom": 10, + "source": { + "osmTags": { "or": [ - "access=permissive", - "access=public" + "amenity=charging_station", + "disused:amenity=charging_station", + "planned:amenity=charging_station", + "construction:amenity=charging_station" ] - }, - "then": { - "en": "Anyone can use this charging station (payment might be needed)", - "nl": "Toegankelijk voor iedereen (mogelijks met aanmelden en/of te betalen)" - }, - "hideInAnswer": true - }, - { - "if": "access=customers", - "then": { - "en": "Only customers of the place this station belongs to can use this charging station
E.g. a charging station operated by hotel which is only usable by their guests", - "nl": "Enkel klanten van de bijhorende plaats mogen dit oplaadpunt gebruiken
Bijvoorbeeld een oplaadpunt op de parking van een restaurant dat enkel door klanten van het restaurant gebruikt mag worden" - } - }, - { - "if": "access=private", - "then": { - "en": "Not accessible to the general public (e.g. only accessible to the owners, employees, ...)", - "nl": "Niet toegankelijk voor het publiek Enkel toegankelijk voor de eigenaar, medewerkers ,... " - } } - ] }, - { - "id": "capacity", - "render": { - "en": "{capacity} vehicles can be charged here at the same time", - "nl": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden", - "de": "{capacity} Fahrzeuge können hier gleichzeitig geladen werden" - }, - "question": { - "en": "How much vehicles can be charged here at the same time?", - "nl": "Hoeveel voertuigen kunnen hier opgeladen worden?", - "de": "Wie viele Fahrzeuge können hier gleichzeitig geladen werden?" - }, - "freeform": { - "key": "capacity", - "type": "pnat" - } - }, - { - "id": "Available_charging_stations (generated)", - "question": { - "en": "Which charging stations are available here?", - "nl": "Welke aansluitingen zijn hier beschikbaar?", - "de": "Welche Ladestationen gibt es hier?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "socket:schuko=1", - "ifnot": "socket:schuko=", - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "hideInAnswer": { - "or": [ - "_country!=be", - "_country!=fr", - "_country!=ma", - "_country!=tn", - "_country!=pl", - "_country!=cs", - "_country!=sk", - "_country!=mo" - ] - } - }, - { - "if": { - "and": [ - "socket:schuko~*", - "socket:schuko!=1" - ] - }, - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:typee=1", - "ifnot": "socket:typee=", - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" - } - }, - { - "if": { - "and": [ - "socket:typee~*", - "socket:typee!=1" - ] - }, - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:chademo=1", - "ifnot": "socket:chademo=", - "then": { - "en": "
Chademo
", - "nl": "
Chademo
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:chademo~*", - "socket:chademo!=1" - ] - }, - "then": { - "en": "
Chademo
", - "nl": "
Chademo
", - "de": "
Chademo
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1_cable=1", - "ifnot": "socket:type1_cable=", - "then": { - "en": "
Type 1 with cable (J1772)
", - "nl": "
Type 1 met kabel (J1772)
", - "de": "
Typ 1 mit Kabel (J1772)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=1" - ] - }, - "then": { - "en": "
Type 1 with cable (J1772)
", - "nl": "
Type 1 met kabel (J1772)
", - "de": "
Typ 1 mit Kabel (J1772)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1=1", - "ifnot": "socket:type1=", - "then": { - "en": "
Type 1 without cable (J1772)
", - "nl": "
Type 1 zonder kabel (J1772)
", - "de": "
Typ 1 ohne Kabel (J1772)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type1~*", - "socket:type1!=1" - ] - }, - "then": { - "en": "
Type 1 without cable (J1772)
", - "nl": "
Type 1 zonder kabel (J1772)
", - "de": "
Typ 1 ohne Kabel (J1772)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1_combo=1", - "ifnot": "socket:type1_combo=", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
", - "de": "
Typ 1 CCS (auch bekannt als Typ 1 Combo)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=1" - ] - }, - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
", - "de": "
Typ 1 CCS (auch bekannt als Typ 1 Combo)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_supercharger=1", - "ifnot": "socket:tesla_supercharger=", - "then": { - "en": "
Tesla Supercharger
", - "nl": "
Tesla Supercharger
", - "de": "
Tesla Supercharger
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=1" - ] - }, - "then": { - "en": "
Tesla Supercharger
", - "nl": "
Tesla Supercharger
", - "de": "
Tesla Supercharger
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type2=1", - "ifnot": "socket:type2=", - "then": { - "en": "
Type 2 (mennekes)
", - "nl": "
Type 2 (mennekes)
", - "de": "
Typ 2 (Mennekes)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type2~*", - "socket:type2!=1" - ] - }, - "then": { - "en": "
Type 2 (mennekes)
", - "nl": "
Type 2 (mennekes)
", - "de": "
Typ 2 (Mennekes)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type2_combo=1", - "ifnot": "socket:type2_combo=", - "then": { - "en": "
Type 2 CCS (mennekes)
", - "nl": "
Type 2 CCS (mennekes)
", - "de": "
Typ 2 CCS (Mennekes)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=1" - ] - }, - "then": { - "en": "
Type 2 CCS (mennekes)
", - "nl": "
Type 2 CCS (mennekes)
", - "de": "
Typ 2 CCS (Mennekes)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type2_cable=1", - "ifnot": "socket:type2_cable=", - "then": { - "en": "
Type 2 with cable (mennekes)
", - "nl": "
Type 2 met kabel (J1772)
", - "de": "
Typ 2 mit Kabel (Mennekes)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=1" - ] - }, - "then": { - "en": "
Type 2 with cable (mennekes)
", - "nl": "
Type 2 met kabel (J1772)
", - "de": "
Typ 2 mit Kabel (Mennekes)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_supercharger_ccs=1", - "ifnot": "socket:tesla_supercharger_ccs=", - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
", - "de": "
Tesla Supercharger CCS (Typ 2 CSS)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=1" - ] - }, - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
", - "de": "
Tesla Supercharger CCS (Typ 2 CSS)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_destination=1", - "ifnot": "socket:tesla_destination=", - "then": { - "en": "
Tesla Supercharger (destination)
", - "nl": "
Tesla Supercharger (destination)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - }, - { - "or": [ - "_country!=us" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=1" - ] - }, - "then": { - "en": "
Tesla Supercharger (destination)
", - "nl": "
Tesla Supercharger (destination)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_destination=1", - "ifnot": "socket:tesla_destination=", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - }, - { - "or": [ - "_country=us" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=1" - ] - }, - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:USB-A=1", - "ifnot": "socket:USB-A=", - "then": { - "en": "
USB to charge phones and small electronics
", - "nl": "
USB om GSMs en kleine electronica op te laden
", - "de": "
USB zum Laden von Smartphones oder Elektrokleingeräten
" - } - }, - { - "if": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=1" - ] - }, - "then": { - "en": "
USB to charge phones and small electronics
", - "nl": "
USB om GSMs en kleine electronica op te laden
", - "de": "
USB zum Laden von Smartphones und Elektrokleingeräten
" - }, - "hideInAnswer": true - }, - { - "if": "socket:bosch_3pin=1", - "ifnot": "socket:bosch_3pin=", - "then": { - "en": "
Bosch Active Connect with 3 pins and cable
", - "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "bicycle=no" - ] - }, - { - "and": [ - { - "or": [ - "car=yes", - "motorcar=yes", - "hgv=yes", - "bus=yes" - ] - }, - "bicycle!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:bosch_3pin~*", - "socket:bosch_3pin!=1" - ] - }, - "then": { - "en": "
Bosch Active Connect with 3 pins and cable
", - "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "hideInAnswer": true - }, - { - "if": "socket:bosch_5pin=1", - "ifnot": "socket:bosch_5pin=", - "then": { - "en": "
Bosch Active Connect with 5 pins and cable
", - "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
", - "de": "
Bosch Active Connect mit 5 Pins und Kabel
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "bicycle=no" - ] - }, - { - "and": [ - { - "or": [ - "car=yes", - "motorcar=yes", - "hgv=yes", - "bus=yes" - ] - }, - "bicycle!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:bosch_5pin~*", - "socket:bosch_5pin!=1" - ] - }, - "then": { - "en": "
Bosch Active Connect with 5 pins and cable
", - "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
", - "de": "
Bosch Active Connect mit 5 Pins und Kabel
" - }, - "hideInAnswer": true + "title": { + "render": { + "en": "Charging station", + "nl": "Oplaadpunten" } - ] }, - { - "id": "plugs-0", - "question": { - "en": "How much plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
are available here?", - "nl": "Hoeveel stekkers van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:schuko} plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
available here", - "nl": "Hier zijn {socket:schuko} stekkers van het type
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "freeform": { - "key": "socket:schuko", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } + "description": { + "en": "A charging station", + "nl": "Oplaadpunten" }, - { - "id": "plugs-1", - "question": { - "en": "How much plugs of type
European wall plug with ground pin (CEE7/4 type E)
are available here?", - "nl": "Hoeveel stekkers van type
Europese stekker met aardingspin (CEE7/4 type E)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:typee} plugs of type
European wall plug with ground pin (CEE7/4 type E)
available here", - "nl": "Hier zijn {socket:typee} stekkers van het type
Europese stekker met aardingspin (CEE7/4 type E)
" - }, - "freeform": { - "key": "socket:typee", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "plugs-2", - "question": { - "en": "How much plugs of type
Chademo
are available here?", - "nl": "Hoeveel stekkers van type
Chademo
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:chademo} plugs of type
Chademo
available here", - "nl": "Hier zijn {socket:chademo} stekkers van het type
Chademo
" - }, - "freeform": { - "key": "socket:chademo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "plugs-3", - "question": { - "en": "How much plugs of type
Type 1 with cable (J1772)
are available here?", - "nl": "Hoeveel stekkers van type
Type 1 met kabel (J1772)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:type1_cable} plugs of type
Type 1 with cable (J1772)
available here", - "nl": "Hier zijn {socket:type1_cable} stekkers van het type
Type 1 met kabel (J1772)
" - }, - "freeform": { - "key": "socket:type1_cable", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "plugs-4", - "question": { - "en": "How much plugs of type
Type 1 without cable (J1772)
are available here?", - "nl": "Hoeveel stekkers van type
Type 1 zonder kabel (J1772)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:type1} plugs of type
Type 1 without cable (J1772)
available here", - "nl": "Hier zijn {socket:type1} stekkers van het type
Type 1 zonder kabel (J1772)
" - }, - "freeform": { - "key": "socket:type1", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "plugs-5", - "question": { - "en": "How much plugs of type
Type 1 CCS (aka Type 1 Combo)
are available here?", - "nl": "Hoeveel stekkers van type
Type 1 CCS (ook gekend als Type 1 Combo)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:type1_combo} plugs of type
Type 1 CCS (aka Type 1 Combo)
available here", - "nl": "Hier zijn {socket:type1_combo} stekkers van het type
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "freeform": { - "key": "socket:type1_combo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "plugs-6", - "question": { - "en": "How much plugs of type
Tesla Supercharger
are available here?", - "nl": "Hoeveel stekkers van type
Tesla Supercharger
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:tesla_supercharger} plugs of type
Tesla Supercharger
available here", - "nl": "Hier zijn {socket:tesla_supercharger} stekkers van het type
Tesla Supercharger
" - }, - "freeform": { - "key": "socket:tesla_supercharger", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "plugs-7", - "question": { - "en": "How much plugs of type
Type 2 (mennekes)
are available here?", - "nl": "Hoeveel stekkers van type
Type 2 (mennekes)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:type2} plugs of type
Type 2 (mennekes)
available here", - "nl": "Hier zijn {socket:type2} stekkers van het type
Type 2 (mennekes)
" - }, - "freeform": { - "key": "socket:type2", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "plugs-8", - "question": { - "en": "How much plugs of type
Type 2 CCS (mennekes)
are available here?", - "nl": "Hoeveel stekkers van type
Type 2 CCS (mennekes)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:type2_combo} plugs of type
Type 2 CCS (mennekes)
available here", - "nl": "Hier zijn {socket:type2_combo} stekkers van het type
Type 2 CCS (mennekes)
" - }, - "freeform": { - "key": "socket:type2_combo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "plugs-9", - "question": { - "en": "How much plugs of type
Type 2 with cable (mennekes)
are available here?", - "nl": "Hoeveel stekkers van type
Type 2 met kabel (J1772)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:type2_cable} plugs of type
Type 2 with cable (mennekes)
available here", - "nl": "Hier zijn {socket:type2_cable} stekkers van het type
Type 2 met kabel (J1772)
" - }, - "freeform": { - "key": "socket:type2_cable", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "plugs-10", - "question": { - "en": "How much plugs of type
Tesla Supercharger CCS (a branded type2_css)
are available here?", - "nl": "Hoeveel stekkers van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:tesla_supercharger_ccs} plugs of type
Tesla Supercharger CCS (a branded type2_css)
available here", - "nl": "Hier zijn {socket:tesla_supercharger_ccs} stekkers van het type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "plugs-11", - "question": { - "en": "How much plugs of type
Tesla Supercharger (destination)
are available here?", - "nl": "Hoeveel stekkers van type
Tesla Supercharger (destination)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:tesla_destination} plugs of type
Tesla Supercharger (destination)
available here", - "nl": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla Supercharger (destination)
" - }, - "freeform": { - "key": "socket:tesla_destination", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "plugs-12", - "question": { - "en": "How much plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
are available here?", - "nl": "Hoeveel stekkers van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:tesla_destination} plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
available here", - "nl": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "freeform": { - "key": "socket:tesla_destination", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "plugs-13", - "question": { - "en": "How much plugs of type
USB to charge phones and small electronics
are available here?", - "nl": "Hoeveel stekkers van type
USB om GSMs en kleine electronica op te laden
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:USB-A} plugs of type
USB to charge phones and small electronics
available here", - "nl": "Hier zijn {socket:USB-A} stekkers van het type
USB om GSMs en kleine electronica op te laden
" - }, - "freeform": { - "key": "socket:USB-A", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "plugs-14", - "question": { - "en": "How much plugs of type
Bosch Active Connect with 3 pins and cable
are available here?", - "nl": "Hoeveel stekkers van type
Bosch Active Connect met 3 pinnen aan een kabel
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:bosch_3pin} plugs of type
Bosch Active Connect with 3 pins and cable
available here", - "nl": "Hier zijn {socket:bosch_3pin} stekkers van het type
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "freeform": { - "key": "socket:bosch_3pin", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:bosch_3pin~*", - "socket:bosch_3pin!=0" - ] - } - }, - { - "id": "plugs-15", - "question": { - "en": "How much plugs of type
Bosch Active Connect with 5 pins and cable
are available here?", - "nl": "Hoeveel stekkers van type
Bosch Active Connect met 5 pinnen aan een kabel
heeft dit oplaadpunt?" - }, - "render": { - "en": "There are {socket:bosch_5pin} plugs of type
Bosch Active Connect with 5 pins and cable
available here", - "nl": "Hier zijn {socket:bosch_5pin} stekkers van het type
Bosch Active Connect met 5 pinnen aan een kabel
" - }, - "freeform": { - "key": "socket:bosch_5pin", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:bosch_5pin~*", - "socket:bosch_5pin!=0" - ] - } - }, - { - "id": "OH", - "render": "{opening_hours_table(opening_hours)}", - "freeform": { - "key": "opening_hours", - "type": "opening_hours" - }, - "question": { - "en": "When is this charging station opened?", - "nl": "Wanneer is dit oplaadpunt beschikbaar??", - "de": "Wann ist diese Ladestation geöffnet?", - "it": "Quali sono gli orari di apertura di questa stazione di ricarica?", - "ja": "この充電ステーションはいつオープンしますか?", - "nb_NO": "Når åpnet denne ladestasjonen?", - "ru": "В какое время работает эта зарядная станция?", - "zh_Hant": "何時是充電站開放使用的時間?" - }, - "mappings": [ + "tagRenderings": [ + "images", { - "if": "opening_hours=24/7", - "then": { - "en": "24/7 opened (including holidays)", - "nl": "24/7 open - ook tijdens vakanties", - "de": "durchgehend geöffnet (auch an Feiertagen)" - } - } - ] - }, - { - "id": "fee", - "question": { - "en": "Does one have to pay to use this charging station?", - "nl": "Moet men betalen om dit oplaadpunt te gebruiken?" - }, - "mappings": [ - { - "if": { - "and": [ - "fee=no" - ] - }, - "then": { - "nl": "Gratis te gebruiken", - "en": "Free to use" - }, - "hideInAnswer": true - }, - { - "if": { - "and": [ - "fee=no", - "fee:conditional=", - "charge=", - "authentication:none=yes" - ] - }, - "then": { - "nl": "Gratis te gebruiken (zonder aan te melden)", - "en": "Free to use (without authenticating)" - } - }, - { - "if": { - "and": [ - "fee=no", - "fee:conditional=", - "charge=", - "authentication:none=no" - ] - }, - "then": { - "nl": "Gratis te gebruiken, maar aanmelden met een applicatie is verplicht", - "en": "Free to use, but one has to authenticate" - } - }, - { - "if": { - "and": [ - "fee=yes", - "fee:conditional=no @ customers" - ] - }, - "then": { - "nl": "Betalend te gebruiken, maar gratis voor klanten van het bijhorende hotel/café/ziekenhuis/...", - "en": "Paid use, but free for customers of the hotel/pub/hospital/... who operates the charging station" - } - }, - { - "if": { - "and": [ - "fee=yes", - "fee:conditional=" - ] - }, - "then": { - "nl": "Betalend", - "en": "Paid use" - } - } - ] - }, - { - "id": "charge", - "question": { - "en": "How much does one have to pay to use this charging station?", - "nl": "Hoeveel moet men betalen om dit oplaadpunt te gebruiken?" - }, - "render": { - "en": "Using this charging station costs {charge}", - "nl": "Dit oplaadpunt gebruiken kost {charge}" - }, - "freeform": { - "key": "charge" - }, - "condition": "fee=yes" - }, - { - "id": "payment-options", - "builtin": "payment-options", - "override": { - "condition": { - "or": [ - "fee=yes", - "charge~*" - ] - }, - "mappings+": [ - { - "if": "payment:app=yes", - "ifnot": "payment:app=no", - "then": { - "en": "Payment is done using a dedicated app", - "nl": "Betalen via een app van het netwerk", - "de": "Bezahlung mit einer speziellen App" - } - }, - { - "if": "payment:membership_card=yes", - "ifnot": "payment:membership_card=no", - "then": { - "en": "Payment is done using a membership card", - "nl": "Betalen via een lidkaart van het netwerk", - "de": "Bezahlung mit einer Mitgliedskarte" - } - } - ] - } - }, - { - "id": "Authentication", - "#": "In some cases, charging is free but one has to be authenticated. We only ask for authentication if fee is no (or unset). By default one sees the questions for either the payment options or the authentication options, but normally not both", - "question": { - "en": "What kind of authentication is available at the charging station?", - "nl": "Hoe kan men zich aanmelden aan dit oplaadstation?", - "de": "Welche Authentifizierung ist an der Ladestation möglich?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "authentication:membership_card=yes", - "ifnot": "authentication:membership_card=no", - "then": { - "en": "Authentication by a membership card", - "nl": "Aanmelden met een lidkaart is mogelijk", - "de": "Authentifizierung durch eine Mitgliedskarte" - } - }, - { - "if": "authentication:app=yes", - "ifnot": "authentication:app=no", - "then": { - "en": "Authentication by an app", - "nl": "Aanmelden via een applicatie is mogelijk", - "de": "Authentifizierung durch eine App" - } - }, - { - "if": "authentication:phone_call=yes", - "ifnot": "authentication:phone_call=no", - "then": { - "en": "Authentication via phone call is available", - "nl": "Aanmelden door te bellen naar een telefoonnummer is mogelijk", - "de": "Authentifizierung per Anruf ist möglich" - } - }, - { - "if": "authentication:short_message=yes", - "ifnot": "authentication:short_message=no", - "then": { - "en": "Authentication via phone call is available", - "nl": "Aanmelden via SMS is mogelijk", - "de": "Authentifizierung per Anruf ist möglich" - } - }, - { - "if": "authentication:nfc=yes", - "ifnot": "authentication:nfc=no", - "then": { - "en": "Authentication via NFC is available", - "nl": "Aanmelden via NFC is mogelijk", - "de": "Authentifizierung über NFC ist möglich" - } - }, - { - "if": "authentication:money_card=yes", - "ifnot": "authentication:money_card=no", - "then": { - "en": "Authentication via Money Card is available", - "nl": "Aanmelden met Money Card is mogelijk", - "de": "Authentifizierung über Geldkarte ist möglich" - } - }, - { - "if": "authentication:debit_card=yes", - "ifnot": "authentication:debit_card=no", - "then": { - "en": "Authentication via debit card is available", - "nl": "Aanmelden met een betaalkaart is mogelijk", - "de": "Authentifizierung per Debitkarte ist möglich" - } - }, - { - "if": "authentication:none=yes", - "ifnot": "authentication:none=no", - "then": { - "en": "Charging here is (also) possible without authentication", - "nl": "Hier opladen is (ook) mogelijk zonder aan te melden", - "de": "Das Aufladen ist hier (auch) ohne Authentifizierung möglich" - } - } - ], - "condition": { - "or": [ - "fee=no", - "fee=" - ] - } - }, - { - "id": "Auth phone", - "render": { - "en": "Authenticate by calling or SMS'ing to {authentication:phone_call:number}", - "nl": "Aanmelden door te bellen of te SMS'en naar {authentication:phone_call:number}", - "de": "Authentifizierung durch Anruf oder SMS an {authentication:phone_call:number}" - }, - "question": { - "en": "What's the phone number for authentication call or SMS?", - "nl": "Wat is het telefoonnummer dat men moet bellen of SMS'en om zich aan te melden?", - "de": "Wie lautet die Telefonnummer für den Authentifizierungsanruf oder die SMS?" - }, - "freeform": { - "key": "authentication:phone_call:number", - "type": "phone" - }, - "condition": { - "or": [ - "authentication:phone_call=yes", - "authentication:short_message=yes" - ] - } - }, - { - "id": "maxstay", - "question": { - "en": "What is the maximum amount of time one is allowed to stay here?", - "nl": "Hoelang mag een voertuig hier blijven staan?", - "de": "Was ist die Höchstdauer des Aufenthalts hier?" - }, - "freeform": { - "key": "maxstay" - }, - "render": { - "en": "One can stay at most {canonical(maxstay)}", - "nl": "De maximale parkeertijd hier is {canonical(maxstay)}", - "de": "Die maximale Parkzeit beträgt {canonical(maxstay)}" - }, - "mappings": [ - { - "if": "maxstay=unlimited", - "then": { - "en": "No timelimit on leaving your vehicle here", - "nl": "Geen maximum parkeertijd", - "de": "Keine Höchstparkdauer" - } - } - ], - "condition": { - "or": [ - "maxstay~*", - "motorcar=yes", - "hgv=yes", - "bus=yes" - ] - } - }, - { - "id": "Network", - "render": { - "en": "Part of the network {network}", - "nl": "Maakt deel uit van het {network}-netwerk", - "de": "Teil des Netzwerks {network}", - "it": "{network}", - "ja": "{network}", - "nb_NO": "{network}", - "ru": "{network}", - "zh_Hant": "{network}" - }, - "question": { - "en": "Is this charging station part of a network?", - "nl": "Is dit oplaadpunt deel van een groter netwerk?", - "de": "Ist diese Ladestation Teil eines Netzwerks?", - "it": "A quale rete appartiene questa stazione di ricarica?", - "ja": "この充電ステーションの運営チェーンはどこですか?", - "ru": "К какой сети относится эта станция?", - "zh_Hant": "充電站所屬的網路是?" - }, - "freeform": { - "key": "network" - }, - "mappings": [ - { - "if": "no:network=yes", - "then": { - "en": "Not part of a bigger network", - "nl": "Maakt geen deel uit van een groter netwerk", - "de": "Nicht Teil eines größeren Netzwerks" - } - }, - { - "if": "network=none", - "then": { - "en": "Not part of a bigger network", - "nl": "Maakt geen deel uit van een groter netwerk", - "de": "Nicht Teil eines größeren Netzwerks" - }, - "hideInAnswer": true - }, - { - "if": "network=AeroVironment", - "then": "AeroVironment" - }, - { - "if": "network=Blink", - "then": "Blink" - }, - { - "if": "network=eVgo", - "then": "eVgo" - } - ] - }, - { - "id": "Operator", - "question": { - "en": "Who is the operator of this charging station?", - "nl": "Wie beheert dit oplaadpunt?", - "de": "Wer ist der Betreiber dieser Ladestation?" - }, - "render": { - "en": "This charging station is operated by {operator}", - "nl": "Wordt beheerd door {operator}", - "de": "Diese Ladestation wird betrieben von {operator}" - }, - "freeform": { - "key": "operator" - }, - "mappings": [ - { - "if": { - "and": [ - "network:={operator}" - ] - }, - "then": { - "en": "Actually, {operator} is the network", - "nl": "Eigenlijk is {operator} het netwerk waarvan het deel uitmaakt", - "de": "Eigentlich ist {operator} das Netzwerk" - }, - "addExtraTags": [ - "operator=" - ], - "hideInAnswer": "operator=" - } - ] - }, - { - "id": "phone", - "question": { - "en": "What number can one call if there is a problem with this charging station?", - "nl": "Wat is het telefoonnummer van de beheerder van dit oplaadpunt?", - "de": "Welche Nummer kann man anrufen, wenn es ein Problem mit dieser Ladestation gibt?" - }, - "render": { - "en": "In case of problems, call {phone}", - "nl": "Bij problemen, bel naar {phone}", - "de": "Bei Problemen, anrufen unter {phone}" - }, - "freeform": { - "key": "phone", - "type": "phone" - } - }, - { - "id": "email", - "question": { - "en": "What is the email address of the operator?", - "nl": "Wat is het email-adres van de operator?", - "de": "Wie ist die Email-Adresse des Betreibers?" - }, - "render": { - "en": "In case of problems, send an email to {email}", - "nl": "Bij problemen, email naar {email}", - "de": "Bei Problemen senden Sie eine E-Mail an {email}" - }, - "freeform": { - "key": "email", - "type": "email" - } - }, - { - "id": "website", - "question": { - "en": "What is the website of the operator?", - "nl": "Wat is de website waar men meer info kan vinden over dit oplaadpunt?", - "de": "Wie ist die Webseite des Betreibers?" - }, - "render": { - "en": "More info on {website}", - "nl": "Meer informatie op {website}", - "de": "Weitere Informationen auf {website}" - }, - "freeform": { - "key": "website", - "type": "url" - } - }, - "level", - { - "id": "ref", - "question": { - "en": "What is the reference number of this charging station?", - "nl": "Wat is het referentienummer van dit oplaadstation?", - "de": "Wie lautet die Kennung dieser Ladestation?" - }, - "render": { - "en": "Reference number is {ref}", - "nl": "Het referentienummer van dit oplaadpunt is {ref}", - "de": "Die Kennziffer ist {ref}" - }, - "freeform": { - "key": "ref" - }, - "#": "Only asked if part of a bigger network. Small operators typically don't have a reference number", - "condition": "network!=" - }, - { - "id": "Operational status", - "question": { - "en": "Is this charging point in use?", - "nl": "Is dit oplaadpunt operationeel?", - "de": "Ist dieser Ladepunkt in Betrieb?" - }, - "mappings": [ - { - "if": { - "and": [ - "planned:amenity=", - "construction:amenity=", - "disused:amenity=", - "operational_status=", - "amenity=charging_station" - ] - }, - "then": { - "en": "This charging station works", - "nl": "Dit oplaadpunt werkt", - "de": "Diese Ladestation funktioniert" - } - }, - { - "if": { - "and": [ - "planned:amenity=", - "construction:amenity=", - "disused:amenity=", - "operational_status=broken", - "amenity=charging_station" - ] - }, - "then": { - "en": "This charging station is broken", - "nl": "Dit oplaadpunt is kapot", - "de": "Diese Ladestation ist kaputt" - } - }, - { - "if": { - "and": [ - "planned:amenity=charging_station", - "construction:amenity=", - "disused:amenity=", - "operational_status=", - "amenity=" - ] - }, - "then": { - "en": "A charging station is planned here", - "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden", - "de": "Hier ist eine Ladestation geplant" - } - }, - { - "if": { - "and": [ - "planned:amenity=", - "construction:amenity=charging_station", - "disused:amenity=", - "operational_status=", - "amenity=" - ] - }, - "then": { - "en": "A charging station is constructed here", - "nl": "Hier wordt op dit moment een oplaadpunt gebouwd", - "de": "Hier wird eine Ladestation gebaut" - } - }, - { - "if": { - "and": [ - "planned:amenity=", - "construction:amenity=", - "disused:amenity=charging_station", - "operational_status=", - "amenity=" - ] - }, - "then": { - "en": "This charging station has beed permanently disabled and is not in use anymore but is still visible", - "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig", - "de": "Diese Ladestation wurde dauerhaft deaktiviert und wird nicht mehr benutzt, ist aber noch sichtbar" - } - } - ] - }, - { - "id": "Parking:fee", - "question": { - "en": "Does one have to pay a parking fee while charging?", - "nl": "Moet men parkeergeld betalen tijdens het opladen?", - "de": "Muss man beim Laden eine Parkgebühr bezahlen?" - }, - "mappings": [ - { - "if": "parking:fee=no", - "then": { - "en": "No additional parking cost while charging", - "nl": "Geen extra parkeerkost tijdens het opladen", - "de": "Keine zusätzlichen Parkgebühren beim Laden" - } - }, - { - "if": "parking:fee=yes", - "then": { - "en": "An additional parking fee should be paid while charging", - "nl": "Tijdens het opladen moet er parkeergeld betaald worden", - "de": "Beim Laden ist eine zusätzliche Parkgebühr zu entrichten" - } - } - ], - "condition": { - "or": [ - "motor_vehicle=yes", - "hgv=yes", - "bus=yes", - "bicycle=no", - "bicycle=" - ] - } - } - ], - "presets": [ - { - "tags": [ - "amenity=charging_station", - "motorcar=no", - "bicycle=yes", - "socket:typee=1" - ], - "title": { - "en": "Charging station", - "nl": "gewone stekker (bedoeld om electrische fietsen op te laden)", - "de": "Ladestation", - "ru": "Зарядная станция" - }, - "preciseInput": { - "preferredBackground": "map" - } - }, - { - "tags": [ - "amenity=charging_station", - "motorcar=no", - "bicycle=yes" - ], - "title": { - "en": "charging station for e-bikes", - "nl": "oplaadpunt voor elektrische fietsen" - }, - "preciseInput": { - "preferredBackground": "map" - } - }, - { - "tags": [ - "amenity=charging_station", - "motorcar=yes", - "bicycle=no" - ], - "title": { - "en": "charging station for cars", - "nl": "oplaadstation voor elektrische auto's" - }, - "preciseInput": { - "preferredBackground": "map" - } - }, - { - "tags": [ - "amenity=charging_station" - ], - "title": { - "en": "charging station", - "nl": "oplaadstation" - }, - "preciseInput": { - "preferredBackground": "map" - } - } - ], - "wayHandling": 1, - "filter": [ - { - "id": "vehicle-type", - "options": [ - { - "question": { - "en": "All vehicle types", - "nl": "Alle voertuigen", - "de": "Alle Fahrzeugtypen" - } - }, - { - "question": { - "en": "Charging station for bicycles", - "nl": "Oplaadpunten voor fietsen", - "de": "Ladestation für Fahrräder" - }, - "osmTags": "bicycle=yes" - }, - { - "question": { - "en": "Charging station for cars", - "nl": "Oplaadpunten voor auto's", - "de": "Ladestation für Autos" - }, - "osmTags": { - "or": [ - "car=yes", - "motorcar=yes" - ] - } - } - ] - }, - { - "id": "working", - "options": [ - { - "question": { - "en": "Only working charging stations", - "nl": "Enkel werkende oplaadpunten", - "de": "Nur funktionierende Ladestationen" - }, - "osmTags": { - "and": [ - "operational_status!=broken", - "amenity=charging_station" - ] - } - } - ] - }, - { - "id": "connection_type", - "options": [ - { - "question": { - "en": "All connectors", - "nl": "Alle types", - "de": "Alle Anschlüsse" - } - }, - { - "question": { - "en": "Has a
Schuko wall plug without ground pin (CEE7/4 type F)
connector", - "nl": "Heeft een
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "osmTags": "socket:schuko~*" - }, - { - "question": { - "en": "Has a
European wall plug with ground pin (CEE7/4 type E)
connector", - "nl": "Heeft een
Europese stekker met aardingspin (CEE7/4 type E)
" - }, - "osmTags": "socket:typee~*" - }, - { - "question": { - "en": "Has a
Chademo
connector", - "nl": "Heeft een
Chademo
", - "de": "Hat einen
Chademo
Stecker" - }, - "osmTags": "socket:chademo~*" - }, - { - "question": { - "en": "Has a
Type 1 with cable (J1772)
connector", - "nl": "Heeft een
Type 1 met kabel (J1772)
" - }, - "osmTags": "socket:type1_cable~*" - }, - { - "question": { - "en": "Has a
Type 1 without cable (J1772)
connector", - "nl": "Heeft een
Type 1 zonder kabel (J1772)
" - }, - "osmTags": "socket:type1~*" - }, - { - "question": { - "en": "Has a
Type 1 CCS (aka Type 1 Combo)
connector", - "nl": "Heeft een
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "osmTags": "socket:type1_combo~*" - }, - { - "question": { - "en": "Has a
Tesla Supercharger
connector", - "nl": "Heeft een
Tesla Supercharger
", - "de": "Hat einen
Tesla Supercharger
Stecker" - }, - "osmTags": "socket:tesla_supercharger~*" - }, - { - "question": { - "en": "Has a
Type 2 (mennekes)
connector", - "nl": "Heeft een
Type 2 (mennekes)
" - }, - "osmTags": "socket:type2~*" - }, - { - "question": { - "en": "Has a
Type 2 CCS (mennekes)
connector", - "nl": "Heeft een
Type 2 CCS (mennekes)
" - }, - "osmTags": "socket:type2_combo~*" - }, - { - "question": { - "en": "Has a
Type 2 with cable (mennekes)
connector", - "nl": "Heeft een
Type 2 met kabel (J1772)
" - }, - "osmTags": "socket:type2_cable~*" - }, - { - "question": { - "en": "Has a
Tesla Supercharger CCS (a branded type2_css)
connector", - "nl": "Heeft een
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "osmTags": "socket:tesla_supercharger_ccs~*" - }, - { - "question": { - "en": "Has a
Tesla Supercharger (destination)
connector", - "nl": "Heeft een
Tesla Supercharger (destination)
" - }, - "osmTags": "socket:tesla_destination~*" - }, - { - "question": { - "en": "Has a
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
connector", - "nl": "Heeft een
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "osmTags": "socket:tesla_destination~*" - }, - { - "question": { - "en": "Has a
USB to charge phones and small electronics
connector", - "nl": "Heeft een
USB om GSMs en kleine electronica op te laden
" - }, - "osmTags": "socket:USB-A~*" - }, - { - "question": { - "en": "Has a
Bosch Active Connect with 3 pins and cable
connector", - "nl": "Heeft een
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "osmTags": "socket:bosch_3pin~*" - }, - { - "question": { - "en": "Has a
Bosch Active Connect with 5 pins and cable
connector", - "nl": "Heeft een
Bosch Active Connect met 5 pinnen aan een kabel
" - }, - "osmTags": "socket:bosch_5pin~*" - } - ] - } - ], - "units": [ - { - "appliesToKey": [ - "maxstay" - ], - "applicableUnits": [ - { - "canonicalDenomination": "minutes", - "canonicalDenominationSingular": "minute", - "alternativeDenomination": [ - "m", - "min", - "mins", - "minuten", - "mns" - ], - "human": { - "en": " minutes", - "nl": " minuten", - "de": " Minuten", - "ru": " минут" - }, - "humanSingular": { - "en": " minute", - "nl": " minuut", - "de": " Minute", - "ru": " минута" - } - }, - { - "canonicalDenomination": "hours", - "canonicalDenominationSingular": "hour", - "alternativeDenomination": [ - "h", - "hrs", - "hours", - "u", - "uur", - "uren" - ], - "human": { - "en": " hours", - "nl": " uren", - "de": " Stunden", - "ru": " часов" - }, - "humanSingular": { - "en": " hour", - "nl": " uur", - "de": " Stunde", - "ru": " час" - } - }, - { - "canonicalDenomination": "days", - "canonicalDenominationSingular": "day", - "alternativeDenomination": [ - "dys", - "dagen", - "dag" - ], - "human": { - "en": " days", - "nl": " day", - "de": " Tage", - "ru": " дней" - }, - "humanSingular": { - "en": " day", - "nl": " dag", - "de": " Tag", - "ru": " день" - } - } - ] - }, - { - "appliesToKey": [ - "socket:schuko:voltage", - "socket:typee:voltage", - "socket:chademo:voltage", - "socket:type1_cable:voltage", - "socket:type1:voltage", - "socket:type1_combo:voltage", - "socket:tesla_supercharger:voltage", - "socket:type2:voltage", - "socket:type2_combo:voltage", - "socket:type2_cable:voltage", - "socket:tesla_supercharger_ccs:voltage", - "socket:tesla_destination:voltage", - "socket:tesla_destination:voltage", - "socket:USB-A:voltage", - "socket:bosch_3pin:voltage", - "socket:bosch_5pin:voltage" - ], - "applicableUnits": [ - { - "canonicalDenomination": "V", - "alternativeDenomination": [ - "v", - "volt", - "voltage", - "V", - "Volt" - ], - "human": { - "en": "Volts", - "nl": "volt", - "de": "Volt", - "ru": "Вольт" - } - } - ], - "eraseInvalidValues": true - }, - { - "appliesToKey": [ - "socket:schuko:current", - "socket:typee:current", - "socket:chademo:current", - "socket:type1_cable:current", - "socket:type1:current", - "socket:type1_combo:current", - "socket:tesla_supercharger:current", - "socket:type2:current", - "socket:type2_combo:current", - "socket:type2_cable:current", - "socket:tesla_supercharger_ccs:current", - "socket:tesla_destination:current", - "socket:tesla_destination:current", - "socket:USB-A:current", - "socket:bosch_3pin:current", - "socket:bosch_5pin:current" - ], - "applicableUnits": [ - { - "canonicalDenomination": "A", - "alternativeDenomination": [ - "a", - "amp", - "amperage", - "A" - ], - "human": { - "en": "A", - "nl": "A" - } - } - ], - "eraseInvalidValues": true - }, - { - "appliesToKey": [ - "socket:schuko:output", - "socket:typee:output", - "socket:chademo:output", - "socket:type1_cable:output", - "socket:type1:output", - "socket:type1_combo:output", - "socket:tesla_supercharger:output", - "socket:type2:output", - "socket:type2_combo:output", - "socket:type2_cable:output", - "socket:tesla_supercharger_ccs:output", - "socket:tesla_destination:output", - "socket:tesla_destination:output", - "socket:USB-A:output", - "socket:bosch_3pin:output", - "socket:bosch_5pin:output" - ], - "applicableUnits": [ - { - "canonicalDenomination": "kW", - "alternativeDenomination": [ - "kilowatt" - ], - "human": { - "en": "kilowatt", - "nl": "kilowatt", - "de": "Kilowatt", - "ru": "киловатт" - } - }, - { - "canonicalDenomination": "mW", - "alternativeDenomination": [ - "megawatt" - ], - "human": { - "en": "megawatt", - "nl": "megawatt", - "de": "Megawatt", - "ru": "мегаватт" - } - } - ], - "eraseInvalidValues": true - } - ], - "allowMove": { - "enableRelocation": false, - "enableImproveAccuracy": true - }, - "deletion": { - "softDeletionTags": { - "and": [ - "amenity=", - "disused:amenity=charging_station" - ] - }, - "neededChangesets": 10 - }, - "mapRendering": [ - { - "location": [ - "point", - "centroid" - ], - "icon": { - "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", - "mappings": [ - { - "if": "bicycle=yes", - "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" - }, - { - "if": { - "or": [ - "car=yes", - "motorcar=yes" - ] + "id": "Type", + "#": "Allowed vehicle types", + "question": { + "en": "Which vehicles are allowed to charge here?", + "nl": "Welke voertuigen kunnen hier opgeladen worden?", + "de": "Welche Fahrzeuge dürfen hier geladen werden?" }, - "then": "pin:#fff;./assets/themes/charging_stations/car.svg" - } - ] - }, - "iconBadges": [ - { - "if": { - "or": [ - "disused:amenity=charging_station", - "operational_status=broken" + "multiAnswer": true, + "mappings": [ + { + "if": "bicycle=yes", + "ifnot": "bicycle=no", + "then": { + "en": "bicycles can be charged here", + "nl": "Fietsen kunnen hier opgeladen worden", + "de": "Fahrräder können hier geladen werden" + } + }, + { + "if": "motorcar=yes", + "ifnot": "motorcar=no", + "then": { + "en": "Cars can be charged here", + "nl": "Elektrische auto's kunnen hier opgeladen worden", + "de": "Autos können hier geladen werden" + } + }, + { + "if": "scooter=yes", + "ifnot": "scooter=no", + "then": { + "en": "Scooters can be charged here", + "nl": "Electrische scooters (snorfiets of bromfiets) kunnen hier opgeladen worden", + "de": " Roller können hier geladen werden" + } + }, + { + "if": "hgv=yes", + "ifnot": "hgv=no", + "then": { + "en": "Heavy good vehicles (such as trucks) can be charged here", + "nl": "Vrachtwagens kunnen hier opgeladen worden", + "de": "Lastkraftwagen (LKW) können hier geladen werden" + } + }, + { + "if": "bus=yes", + "ifnot": "bus=no", + "then": { + "en": "Buses can be charged here", + "nl": "Bussen kunnen hier opgeladen worden", + "de": "Busse können hier geladen werden" + } + } ] - }, - "then": "cross:#c22;" }, { - "if": { - "or": [ - "proposed:amenity=charging_station", - "planned:amenity=charging_station" - ] - }, - "then": "./assets/layers/charging_station/under_construction.svg" - }, - { - "if": { - "and": [ - "bicycle=yes", - { - "or": [ - "motorcar=yes", - "car=yes" + "id": "access", + "question": { + "en": "Who is allowed to use this charging station?", + "nl": "Wie mag er dit oplaadpunt gebruiken?", + "de": "Wer darf diese Ladestation benutzen?" + }, + "render": { + "en": "Access is {access}", + "nl": "Toegang voor {access}", + "de": "Zugang ist {access}" + }, + "freeform": { + "key": "access", + "addExtraTags": [ + "fixme=Freeform field used for access - doublecheck the value" ] - } + }, + "mappings": [ + { + "if": "access=yes", + "then": { + "en": "Anyone can use this charging station (payment might be needed)", + "nl": "Toegankelijk voor iedereen (mogelijks met aanmelden en/of te betalen)" + } + }, + { + "if": { + "or": [ + "access=permissive", + "access=public" + ] + }, + "then": { + "en": "Anyone can use this charging station (payment might be needed)", + "nl": "Toegankelijk voor iedereen (mogelijks met aanmelden en/of te betalen)" + }, + "hideInAnswer": true + }, + { + "if": "access=customers", + "then": { + "en": "Only customers of the place this station belongs to can use this charging station
E.g. a charging station operated by hotel which is only usable by their guests", + "nl": "Enkel klanten van de bijhorende plaats mogen dit oplaadpunt gebruiken
Bijvoorbeeld een oplaadpunt op de parking van een restaurant dat enkel door klanten van het restaurant gebruikt mag worden" + } + }, + { + "if": "access=private", + "then": { + "en": "Not accessible to the general public (e.g. only accessible to the owners, employees, ...)", + "nl": "Niet toegankelijk voor het publiek Enkel toegankelijk voor de eigenaar, medewerkers ,... " + } + } ] - }, - "then": "circle:#fff;./assets/themes/charging_stations/car.svg" + }, + { + "id": "capacity", + "render": { + "en": "{capacity} vehicles can be charged here at the same time", + "nl": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden", + "de": "{capacity} Fahrzeuge können hier gleichzeitig geladen werden" + }, + "question": { + "en": "How much vehicles can be charged here at the same time?", + "nl": "Hoeveel voertuigen kunnen hier opgeladen worden?", + "de": "Wie viele Fahrzeuge können hier gleichzeitig geladen werden?" + }, + "freeform": { + "key": "capacity", + "type": "pnat" + } + }, + { + "id": "Available_charging_stations (generated)", + "question": { + "en": "Which charging stations are available here?", + "nl": "Welke aansluitingen zijn hier beschikbaar?", + "de": "Welche Ladestationen gibt es hier?" + }, + "multiAnswer": true, + "mappings": [ + { + "if": "socket:schuko=1", + "ifnot": "socket:schuko=", + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "hideInAnswer": { + "or": [ + "_country!=be", + "_country!=fr", + "_country!=ma", + "_country!=tn", + "_country!=pl", + "_country!=cs", + "_country!=sk", + "_country!=mo" + ] + } + }, + { + "if": { + "and": [ + "socket:schuko~*", + "socket:schuko!=1" + ] + }, + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:typee=1", + "ifnot": "socket:typee=", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" + } + }, + { + "if": { + "and": [ + "socket:typee~*", + "socket:typee!=1" + ] + }, + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:chademo=1", + "ifnot": "socket:chademo=", + "then": { + "en": "
Chademo
", + "nl": "
Chademo
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:chademo~*", + "socket:chademo!=1" + ] + }, + "then": { + "en": "
Chademo
", + "nl": "
Chademo
", + "de": "
Chademo
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1_cable=1", + "ifnot": "socket:type1_cable=", + "then": { + "en": "
Type 1 with cable (J1772)
", + "nl": "
Type 1 met kabel (J1772)
", + "de": "
Typ 1 mit Kabel (J1772)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=1" + ] + }, + "then": { + "en": "
Type 1 with cable (J1772)
", + "nl": "
Type 1 met kabel (J1772)
", + "de": "
Typ 1 mit Kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1=1", + "ifnot": "socket:type1=", + "then": { + "en": "
Type 1 without cable (J1772)
", + "nl": "
Type 1 zonder kabel (J1772)
", + "de": "
Typ 1 ohne Kabel (J1772)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type1~*", + "socket:type1!=1" + ] + }, + "then": { + "en": "
Type 1 without cable (J1772)
", + "nl": "
Type 1 zonder kabel (J1772)
", + "de": "
Typ 1 ohne Kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1_combo=1", + "ifnot": "socket:type1_combo=", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
", + "de": "
Typ 1 CCS (auch bekannt als Typ 1 Combo)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=1" + ] + }, + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
", + "de": "
Typ 1 CCS (auch bekannt als Typ 1 Combo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_supercharger=1", + "ifnot": "socket:tesla_supercharger=", + "then": { + "en": "
Tesla Supercharger
", + "nl": "
Tesla Supercharger
", + "de": "
Tesla Supercharger
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger
", + "nl": "
Tesla Supercharger
", + "de": "
Tesla Supercharger
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2=1", + "ifnot": "socket:type2=", + "then": { + "en": "
Type 2 (mennekes)
", + "nl": "
Type 2 (mennekes)
", + "de": "
Typ 2 (Mennekes)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type2~*", + "socket:type2!=1" + ] + }, + "then": { + "en": "
Type 2 (mennekes)
", + "nl": "
Type 2 (mennekes)
", + "de": "
Typ 2 (Mennekes)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2_combo=1", + "ifnot": "socket:type2_combo=", + "then": { + "en": "
Type 2 CCS (mennekes)
", + "nl": "
Type 2 CCS (mennekes)
", + "de": "
Typ 2 CCS (Mennekes)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=1" + ] + }, + "then": { + "en": "
Type 2 CCS (mennekes)
", + "nl": "
Type 2 CCS (mennekes)
", + "de": "
Typ 2 CCS (Mennekes)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2_cable=1", + "ifnot": "socket:type2_cable=", + "then": { + "en": "
Type 2 with cable (mennekes)
", + "nl": "
Type 2 met kabel (J1772)
", + "de": "
Typ 2 mit Kabel (Mennekes)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=1" + ] + }, + "then": { + "en": "
Type 2 with cable (mennekes)
", + "nl": "
Type 2 met kabel (J1772)
", + "de": "
Typ 2 mit Kabel (Mennekes)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_supercharger_ccs=1", + "ifnot": "socket:tesla_supercharger_ccs=", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
", + "de": "
Tesla Supercharger CCS (Typ 2 CSS)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
", + "de": "
Tesla Supercharger CCS (Typ 2 CSS)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_destination=1", + "ifnot": "socket:tesla_destination=", + "then": { + "en": "
Tesla Supercharger (destination)
", + "nl": "
Tesla Supercharger (destination)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + }, + { + "or": [ + "_country!=us" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger (destination)
", + "nl": "
Tesla Supercharger (destination)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_destination=1", + "ifnot": "socket:tesla_destination=", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + }, + { + "or": [ + "_country=us" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=1" + ] + }, + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:USB-A=1", + "ifnot": "socket:USB-A=", + "then": { + "en": "
USB to charge phones and small electronics
", + "nl": "
USB om GSMs en kleine electronica op te laden
", + "de": "
USB zum Laden von Smartphones oder Elektrokleingeräten
" + } + }, + { + "if": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=1" + ] + }, + "then": { + "en": "
USB to charge phones and small electronics
", + "nl": "
USB om GSMs en kleine electronica op te laden
", + "de": "
USB zum Laden von Smartphones und Elektrokleingeräten
" + }, + "hideInAnswer": true + }, + { + "if": "socket:bosch_3pin=1", + "ifnot": "socket:bosch_3pin=", + "then": { + "en": "
Bosch Active Connect with 3 pins and cable
", + "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "bicycle=no" + ] + }, + { + "and": [ + { + "or": [ + "car=yes", + "motorcar=yes", + "hgv=yes", + "bus=yes" + ] + }, + "bicycle!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:bosch_3pin~*", + "socket:bosch_3pin!=1" + ] + }, + "then": { + "en": "
Bosch Active Connect with 3 pins and cable
", + "nl": "
Bosch Active Connect met 3 pinnen aan een kabel
" + }, + "hideInAnswer": true + }, + { + "if": "socket:bosch_5pin=1", + "ifnot": "socket:bosch_5pin=", + "then": { + "en": "
Bosch Active Connect with 5 pins and cable
", + "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
", + "de": "
Bosch Active Connect mit 5 Pins und Kabel
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "bicycle=no" + ] + }, + { + "and": [ + { + "or": [ + "car=yes", + "motorcar=yes", + "hgv=yes", + "bus=yes" + ] + }, + "bicycle!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:bosch_5pin~*", + "socket:bosch_5pin!=1" + ] + }, + "then": { + "en": "
Bosch Active Connect with 5 pins and cable
", + "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
", + "de": "
Bosch Active Connect mit 5 Pins und Kabel
" + }, + "hideInAnswer": true + } + ] + }, + { + "id": "plugs-0", + "question": { + "en": "How much plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
are available here?", + "nl": "Hoeveel stekkers van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:schuko} plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
available here", + "nl": "Hier zijn {socket:schuko} stekkers van het type
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "freeform": { + "key": "socket:schuko", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "plugs-1", + "question": { + "en": "How much plugs of type
European wall plug with ground pin (CEE7/4 type E)
are available here?", + "nl": "Hoeveel stekkers van type
Europese stekker met aardingspin (CEE7/4 type E)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:typee} plugs of type
European wall plug with ground pin (CEE7/4 type E)
available here", + "nl": "Hier zijn {socket:typee} stekkers van het type
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "freeform": { + "key": "socket:typee", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "plugs-2", + "question": { + "en": "How much plugs of type
Chademo
are available here?", + "nl": "Hoeveel stekkers van type
Chademo
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:chademo} plugs of type
Chademo
available here", + "nl": "Hier zijn {socket:chademo} stekkers van het type
Chademo
" + }, + "freeform": { + "key": "socket:chademo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "plugs-3", + "question": { + "en": "How much plugs of type
Type 1 with cable (J1772)
are available here?", + "nl": "Hoeveel stekkers van type
Type 1 met kabel (J1772)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type1_cable} plugs of type
Type 1 with cable (J1772)
available here", + "nl": "Hier zijn {socket:type1_cable} stekkers van het type
Type 1 met kabel (J1772)
" + }, + "freeform": { + "key": "socket:type1_cable", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "plugs-4", + "question": { + "en": "How much plugs of type
Type 1 without cable (J1772)
are available here?", + "nl": "Hoeveel stekkers van type
Type 1 zonder kabel (J1772)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type1} plugs of type
Type 1 without cable (J1772)
available here", + "nl": "Hier zijn {socket:type1} stekkers van het type
Type 1 zonder kabel (J1772)
" + }, + "freeform": { + "key": "socket:type1", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "plugs-5", + "question": { + "en": "How much plugs of type
Type 1 CCS (aka Type 1 Combo)
are available here?", + "nl": "Hoeveel stekkers van type
Type 1 CCS (ook gekend als Type 1 Combo)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type1_combo} plugs of type
Type 1 CCS (aka Type 1 Combo)
available here", + "nl": "Hier zijn {socket:type1_combo} stekkers van het type
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "freeform": { + "key": "socket:type1_combo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "plugs-6", + "question": { + "en": "How much plugs of type
Tesla Supercharger
are available here?", + "nl": "Hoeveel stekkers van type
Tesla Supercharger
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:tesla_supercharger} plugs of type
Tesla Supercharger
available here", + "nl": "Hier zijn {socket:tesla_supercharger} stekkers van het type
Tesla Supercharger
" + }, + "freeform": { + "key": "socket:tesla_supercharger", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "plugs-7", + "question": { + "en": "How much plugs of type
Type 2 (mennekes)
are available here?", + "nl": "Hoeveel stekkers van type
Type 2 (mennekes)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type2} plugs of type
Type 2 (mennekes)
available here", + "nl": "Hier zijn {socket:type2} stekkers van het type
Type 2 (mennekes)
" + }, + "freeform": { + "key": "socket:type2", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "plugs-8", + "question": { + "en": "How much plugs of type
Type 2 CCS (mennekes)
are available here?", + "nl": "Hoeveel stekkers van type
Type 2 CCS (mennekes)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type2_combo} plugs of type
Type 2 CCS (mennekes)
available here", + "nl": "Hier zijn {socket:type2_combo} stekkers van het type
Type 2 CCS (mennekes)
" + }, + "freeform": { + "key": "socket:type2_combo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "plugs-9", + "question": { + "en": "How much plugs of type
Type 2 with cable (mennekes)
are available here?", + "nl": "Hoeveel stekkers van type
Type 2 met kabel (J1772)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type2_cable} plugs of type
Type 2 with cable (mennekes)
available here", + "nl": "Hier zijn {socket:type2_cable} stekkers van het type
Type 2 met kabel (J1772)
" + }, + "freeform": { + "key": "socket:type2_cable", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "plugs-10", + "question": { + "en": "How much plugs of type
Tesla Supercharger CCS (a branded type2_css)
are available here?", + "nl": "Hoeveel stekkers van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:tesla_supercharger_ccs} plugs of type
Tesla Supercharger CCS (a branded type2_css)
available here", + "nl": "Hier zijn {socket:tesla_supercharger_ccs} stekkers van het type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "plugs-11", + "question": { + "en": "How much plugs of type
Tesla Supercharger (destination)
are available here?", + "nl": "Hoeveel stekkers van type
Tesla Supercharger (destination)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:tesla_destination} plugs of type
Tesla Supercharger (destination)
available here", + "nl": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla Supercharger (destination)
" + }, + "freeform": { + "key": "socket:tesla_destination", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "plugs-12", + "question": { + "en": "How much plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
are available here?", + "nl": "Hoeveel stekkers van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:tesla_destination} plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
available here", + "nl": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "freeform": { + "key": "socket:tesla_destination", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "plugs-13", + "question": { + "en": "How much plugs of type
USB to charge phones and small electronics
are available here?", + "nl": "Hoeveel stekkers van type
USB om GSMs en kleine electronica op te laden
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:USB-A} plugs of type
USB to charge phones and small electronics
available here", + "nl": "Hier zijn {socket:USB-A} stekkers van het type
USB om GSMs en kleine electronica op te laden
" + }, + "freeform": { + "key": "socket:USB-A", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "plugs-14", + "question": { + "en": "How much plugs of type
Bosch Active Connect with 3 pins and cable
are available here?", + "nl": "Hoeveel stekkers van type
Bosch Active Connect met 3 pinnen aan een kabel
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:bosch_3pin} plugs of type
Bosch Active Connect with 3 pins and cable
available here", + "nl": "Hier zijn {socket:bosch_3pin} stekkers van het type
Bosch Active Connect met 3 pinnen aan een kabel
" + }, + "freeform": { + "key": "socket:bosch_3pin", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:bosch_3pin~*", + "socket:bosch_3pin!=0" + ] + } + }, + { + "id": "plugs-15", + "question": { + "en": "How much plugs of type
Bosch Active Connect with 5 pins and cable
are available here?", + "nl": "Hoeveel stekkers van type
Bosch Active Connect met 5 pinnen aan een kabel
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:bosch_5pin} plugs of type
Bosch Active Connect with 5 pins and cable
available here", + "nl": "Hier zijn {socket:bosch_5pin} stekkers van het type
Bosch Active Connect met 5 pinnen aan een kabel
" + }, + "freeform": { + "key": "socket:bosch_5pin", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:bosch_5pin~*", + "socket:bosch_5pin!=0" + ] + } + }, + { + "id": "OH", + "render": "{opening_hours_table(opening_hours)}", + "freeform": { + "key": "opening_hours", + "type": "opening_hours" + }, + "question": { + "en": "When is this charging station opened?", + "nl": "Wanneer is dit oplaadpunt beschikbaar??", + "de": "Wann ist diese Ladestation geöffnet?", + "it": "Quali sono gli orari di apertura di questa stazione di ricarica?", + "ja": "この充電ステーションはいつオープンしますか?", + "nb_NO": "Når åpnet denne ladestasjonen?", + "ru": "В какое время работает эта зарядная станция?", + "zh_Hant": "何時是充電站開放使用的時間?" + }, + "mappings": [ + { + "if": "opening_hours=24/7", + "then": { + "en": "24/7 opened (including holidays)", + "nl": "24/7 open - ook tijdens vakanties", + "de": "durchgehend geöffnet (auch an Feiertagen)" + } + } + ] + }, + { + "id": "fee", + "question": { + "en": "Does one have to pay to use this charging station?", + "nl": "Moet men betalen om dit oplaadpunt te gebruiken?" + }, + "mappings": [ + { + "if": { + "and": [ + "fee=no" + ] + }, + "then": { + "nl": "Gratis te gebruiken", + "en": "Free to use" + }, + "hideInAnswer": true + }, + { + "if": { + "and": [ + "fee=no", + "fee:conditional=", + "charge=", + "authentication:none=yes" + ] + }, + "then": { + "nl": "Gratis te gebruiken (zonder aan te melden)", + "en": "Free to use (without authenticating)" + } + }, + { + "if": { + "and": [ + "fee=no", + "fee:conditional=", + "charge=", + "authentication:none=no" + ] + }, + "then": { + "nl": "Gratis te gebruiken, maar aanmelden met een applicatie is verplicht", + "en": "Free to use, but one has to authenticate" + } + }, + { + "if": { + "and": [ + "fee=yes", + "fee:conditional=no @ customers" + ] + }, + "then": { + "nl": "Betalend te gebruiken, maar gratis voor klanten van het bijhorende hotel/café/ziekenhuis/...", + "en": "Paid use, but free for customers of the hotel/pub/hospital/... who operates the charging station" + } + }, + { + "if": { + "and": [ + "fee=yes", + "fee:conditional=" + ] + }, + "then": { + "nl": "Betalend", + "en": "Paid use" + } + } + ] + }, + { + "id": "charge", + "question": { + "en": "How much does one have to pay to use this charging station?", + "nl": "Hoeveel moet men betalen om dit oplaadpunt te gebruiken?" + }, + "render": { + "en": "Using this charging station costs {charge}", + "nl": "Dit oplaadpunt gebruiken kost {charge}" + }, + "freeform": { + "key": "charge" + }, + "condition": "fee=yes" + }, + { + "id": "payment-options", + "builtin": "payment-options", + "override": { + "condition": { + "or": [ + "fee=yes", + "charge~*" + ] + }, + "mappings+": [ + { + "if": "payment:app=yes", + "ifnot": "payment:app=no", + "then": { + "en": "Payment is done using a dedicated app", + "nl": "Betalen via een app van het netwerk", + "de": "Bezahlung mit einer speziellen App" + } + }, + { + "if": "payment:membership_card=yes", + "ifnot": "payment:membership_card=no", + "then": { + "en": "Payment is done using a membership card", + "nl": "Betalen via een lidkaart van het netwerk", + "de": "Bezahlung mit einer Mitgliedskarte" + } + } + ] + } + }, + { + "id": "Authentication", + "#": "In some cases, charging is free but one has to be authenticated. We only ask for authentication if fee is no (or unset). By default one sees the questions for either the payment options or the authentication options, but normally not both", + "question": { + "en": "What kind of authentication is available at the charging station?", + "nl": "Hoe kan men zich aanmelden aan dit oplaadstation?", + "de": "Welche Authentifizierung ist an der Ladestation möglich?" + }, + "multiAnswer": true, + "mappings": [ + { + "if": "authentication:membership_card=yes", + "ifnot": "authentication:membership_card=no", + "then": { + "en": "Authentication by a membership card", + "nl": "Aanmelden met een lidkaart is mogelijk", + "de": "Authentifizierung durch eine Mitgliedskarte" + } + }, + { + "if": "authentication:app=yes", + "ifnot": "authentication:app=no", + "then": { + "en": "Authentication by an app", + "nl": "Aanmelden via een applicatie is mogelijk", + "de": "Authentifizierung durch eine App" + } + }, + { + "if": "authentication:phone_call=yes", + "ifnot": "authentication:phone_call=no", + "then": { + "en": "Authentication via phone call is available", + "nl": "Aanmelden door te bellen naar een telefoonnummer is mogelijk", + "de": "Authentifizierung per Anruf ist möglich" + } + }, + { + "if": "authentication:short_message=yes", + "ifnot": "authentication:short_message=no", + "then": { + "en": "Authentication via phone call is available", + "nl": "Aanmelden via SMS is mogelijk", + "de": "Authentifizierung per Anruf ist möglich" + } + }, + { + "if": "authentication:nfc=yes", + "ifnot": "authentication:nfc=no", + "then": { + "en": "Authentication via NFC is available", + "nl": "Aanmelden via NFC is mogelijk", + "de": "Authentifizierung über NFC ist möglich" + } + }, + { + "if": "authentication:money_card=yes", + "ifnot": "authentication:money_card=no", + "then": { + "en": "Authentication via Money Card is available", + "nl": "Aanmelden met Money Card is mogelijk", + "de": "Authentifizierung über Geldkarte ist möglich" + } + }, + { + "if": "authentication:debit_card=yes", + "ifnot": "authentication:debit_card=no", + "then": { + "en": "Authentication via debit card is available", + "nl": "Aanmelden met een betaalkaart is mogelijk", + "de": "Authentifizierung per Debitkarte ist möglich" + } + }, + { + "if": "authentication:none=yes", + "ifnot": "authentication:none=no", + "then": { + "en": "Charging here is (also) possible without authentication", + "nl": "Hier opladen is (ook) mogelijk zonder aan te melden", + "de": "Das Aufladen ist hier (auch) ohne Authentifizierung möglich" + } + } + ], + "condition": { + "or": [ + "fee=no", + "fee=" + ] + } + }, + { + "id": "Auth phone", + "render": { + "en": "Authenticate by calling or SMS'ing to {authentication:phone_call:number}", + "nl": "Aanmelden door te bellen of te SMS'en naar {authentication:phone_call:number}", + "de": "Authentifizierung durch Anruf oder SMS an {authentication:phone_call:number}" + }, + "question": { + "en": "What's the phone number for authentication call or SMS?", + "nl": "Wat is het telefoonnummer dat men moet bellen of SMS'en om zich aan te melden?", + "de": "Wie lautet die Telefonnummer für den Authentifizierungsanruf oder die SMS?" + }, + "freeform": { + "key": "authentication:phone_call:number", + "type": "phone" + }, + "condition": { + "or": [ + "authentication:phone_call=yes", + "authentication:short_message=yes" + ] + } + }, + { + "id": "maxstay", + "question": { + "en": "What is the maximum amount of time one is allowed to stay here?", + "nl": "Hoelang mag een voertuig hier blijven staan?", + "de": "Was ist die Höchstdauer des Aufenthalts hier?" + }, + "freeform": { + "key": "maxstay" + }, + "render": { + "en": "One can stay at most {canonical(maxstay)}", + "nl": "De maximale parkeertijd hier is {canonical(maxstay)}", + "de": "Die maximale Parkzeit beträgt {canonical(maxstay)}" + }, + "mappings": [ + { + "if": "maxstay=unlimited", + "then": { + "en": "No timelimit on leaving your vehicle here", + "nl": "Geen maximum parkeertijd", + "de": "Keine Höchstparkdauer" + } + } + ], + "condition": { + "or": [ + "maxstay~*", + "motorcar=yes", + "hgv=yes", + "bus=yes" + ] + } + }, + { + "id": "Network", + "render": { + "en": "Part of the network {network}", + "nl": "Maakt deel uit van het {network}-netwerk", + "de": "Teil des Netzwerks {network}", + "it": "{network}", + "ja": "{network}", + "nb_NO": "{network}", + "ru": "{network}", + "zh_Hant": "{network}" + }, + "question": { + "en": "Is this charging station part of a network?", + "nl": "Is dit oplaadpunt deel van een groter netwerk?", + "de": "Ist diese Ladestation Teil eines Netzwerks?", + "it": "A quale rete appartiene questa stazione di ricarica?", + "ja": "この充電ステーションの運営チェーンはどこですか?", + "ru": "К какой сети относится эта станция?", + "zh_Hant": "充電站所屬的網路是?" + }, + "freeform": { + "key": "network" + }, + "mappings": [ + { + "if": "no:network=yes", + "then": { + "en": "Not part of a bigger network", + "nl": "Maakt geen deel uit van een groter netwerk", + "de": "Nicht Teil eines größeren Netzwerks" + } + }, + { + "if": "network=none", + "then": { + "en": "Not part of a bigger network", + "nl": "Maakt geen deel uit van een groter netwerk", + "de": "Nicht Teil eines größeren Netzwerks" + }, + "hideInAnswer": true + }, + { + "if": "network=AeroVironment", + "then": "AeroVironment" + }, + { + "if": "network=Blink", + "then": "Blink" + }, + { + "if": "network=eVgo", + "then": "eVgo" + } + ] + }, + { + "id": "Operator", + "question": { + "en": "Who is the operator of this charging station?", + "nl": "Wie beheert dit oplaadpunt?", + "de": "Wer ist der Betreiber dieser Ladestation?" + }, + "render": { + "en": "This charging station is operated by {operator}", + "nl": "Wordt beheerd door {operator}", + "de": "Diese Ladestation wird betrieben von {operator}" + }, + "freeform": { + "key": "operator" + }, + "mappings": [ + { + "if": { + "and": [ + "network:={operator}" + ] + }, + "then": { + "en": "Actually, {operator} is the network", + "nl": "Eigenlijk is {operator} het netwerk waarvan het deel uitmaakt", + "de": "Eigentlich ist {operator} das Netzwerk" + }, + "addExtraTags": [ + "operator=" + ], + "hideInAnswer": "operator=" + } + ] + }, + { + "id": "phone", + "question": { + "en": "What number can one call if there is a problem with this charging station?", + "nl": "Wat is het telefoonnummer van de beheerder van dit oplaadpunt?", + "de": "Welche Nummer kann man anrufen, wenn es ein Problem mit dieser Ladestation gibt?" + }, + "render": { + "en": "In case of problems, call {phone}", + "nl": "Bij problemen, bel naar {phone}", + "de": "Bei Problemen, anrufen unter {phone}" + }, + "freeform": { + "key": "phone", + "type": "phone" + } + }, + { + "id": "email", + "question": { + "en": "What is the email address of the operator?", + "nl": "Wat is het email-adres van de operator?", + "de": "Wie ist die Email-Adresse des Betreibers?" + }, + "render": { + "en": "In case of problems, send an email to {email}", + "nl": "Bij problemen, email naar {email}", + "de": "Bei Problemen senden Sie eine E-Mail an {email}" + }, + "freeform": { + "key": "email", + "type": "email" + } + }, + { + "id": "website", + "question": { + "en": "What is the website of the operator?", + "nl": "Wat is de website waar men meer info kan vinden over dit oplaadpunt?", + "de": "Wie ist die Webseite des Betreibers?" + }, + "render": { + "en": "More info on {website}", + "nl": "Meer informatie op {website}", + "de": "Weitere Informationen auf {website}" + }, + "freeform": { + "key": "website", + "type": "url" + } + }, + "level", + { + "id": "ref", + "question": { + "en": "What is the reference number of this charging station?", + "nl": "Wat is het referentienummer van dit oplaadstation?", + "de": "Wie lautet die Kennung dieser Ladestation?" + }, + "render": { + "en": "Reference number is {ref}", + "nl": "Het referentienummer van dit oplaadpunt is {ref}", + "de": "Die Kennziffer ist {ref}" + }, + "freeform": { + "key": "ref" + }, + "#": "Only asked if part of a bigger network. Small operators typically don't have a reference number", + "condition": "network!=" + }, + { + "id": "Operational status", + "question": { + "en": "Is this charging point in use?", + "nl": "Is dit oplaadpunt operationeel?", + "de": "Ist dieser Ladepunkt in Betrieb?" + }, + "mappings": [ + { + "if": { + "and": [ + "planned:amenity=", + "construction:amenity=", + "disused:amenity=", + "operational_status=", + "amenity=charging_station" + ] + }, + "then": { + "en": "This charging station works", + "nl": "Dit oplaadpunt werkt", + "de": "Diese Ladestation funktioniert" + } + }, + { + "if": { + "and": [ + "planned:amenity=", + "construction:amenity=", + "disused:amenity=", + "operational_status=broken", + "amenity=charging_station" + ] + }, + "then": { + "en": "This charging station is broken", + "nl": "Dit oplaadpunt is kapot", + "de": "Diese Ladestation ist kaputt" + } + }, + { + "if": { + "and": [ + "planned:amenity=charging_station", + "construction:amenity=", + "disused:amenity=", + "operational_status=", + "amenity=" + ] + }, + "then": { + "en": "A charging station is planned here", + "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden", + "de": "Hier ist eine Ladestation geplant" + } + }, + { + "if": { + "and": [ + "planned:amenity=", + "construction:amenity=charging_station", + "disused:amenity=", + "operational_status=", + "amenity=" + ] + }, + "then": { + "en": "A charging station is constructed here", + "nl": "Hier wordt op dit moment een oplaadpunt gebouwd", + "de": "Hier wird eine Ladestation gebaut" + } + }, + { + "if": { + "and": [ + "planned:amenity=", + "construction:amenity=", + "disused:amenity=charging_station", + "operational_status=", + "amenity=" + ] + }, + "then": { + "en": "This charging station has beed permanently disabled and is not in use anymore but is still visible", + "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig", + "de": "Diese Ladestation wurde dauerhaft deaktiviert und wird nicht mehr benutzt, ist aber noch sichtbar" + } + } + ] + }, + { + "id": "Parking:fee", + "question": { + "en": "Does one have to pay a parking fee while charging?", + "nl": "Moet men parkeergeld betalen tijdens het opladen?", + "de": "Muss man beim Laden eine Parkgebühr bezahlen?" + }, + "mappings": [ + { + "if": "parking:fee=no", + "then": { + "en": "No additional parking cost while charging", + "nl": "Geen extra parkeerkost tijdens het opladen", + "de": "Keine zusätzlichen Parkgebühren beim Laden" + } + }, + { + "if": "parking:fee=yes", + "then": { + "en": "An additional parking fee should be paid while charging", + "nl": "Tijdens het opladen moet er parkeergeld betaald worden", + "de": "Beim Laden ist eine zusätzliche Parkgebühr zu entrichten" + } + } + ], + "condition": { + "or": [ + "motor_vehicle=yes", + "hgv=yes", + "bus=yes", + "bicycle=no", + "bicycle=" + ] + } } - ], - "iconSize": { - "render": "50,50,bottom" - } - } - ] + ], + "presets": [ + { + "tags": [ + "amenity=charging_station", + "motorcar=no", + "bicycle=yes", + "socket:typee=1" + ], + "title": { + "en": "Charging station", + "nl": "gewone stekker (bedoeld om electrische fietsen op te laden)", + "de": "Ladestation", + "ru": "Зарядная станция" + }, + "preciseInput": { + "preferredBackground": "map" + } + }, + { + "tags": [ + "amenity=charging_station", + "motorcar=no", + "bicycle=yes" + ], + "title": { + "en": "charging station for e-bikes", + "nl": "oplaadpunt voor elektrische fietsen" + }, + "preciseInput": { + "preferredBackground": "map" + } + }, + { + "tags": [ + "amenity=charging_station", + "motorcar=yes", + "bicycle=no" + ], + "title": { + "en": "charging station for cars", + "nl": "oplaadstation voor elektrische auto's" + }, + "preciseInput": { + "preferredBackground": "map" + } + }, + { + "tags": [ + "amenity=charging_station" + ], + "title": { + "en": "charging station", + "nl": "oplaadstation" + }, + "preciseInput": { + "preferredBackground": "map" + } + } + ], + "wayHandling": 1, + "filter": [ + { + "id": "vehicle-type", + "options": [ + { + "question": { + "en": "All vehicle types", + "nl": "Alle voertuigen", + "de": "Alle Fahrzeugtypen" + } + }, + { + "question": { + "en": "Charging station for bicycles", + "nl": "Oplaadpunten voor fietsen", + "de": "Ladestation für Fahrräder" + }, + "osmTags": "bicycle=yes" + }, + { + "question": { + "en": "Charging station for cars", + "nl": "Oplaadpunten voor auto's", + "de": "Ladestation für Autos" + }, + "osmTags": { + "or": [ + "car=yes", + "motorcar=yes" + ] + } + } + ] + }, + { + "id": "working", + "options": [ + { + "question": { + "en": "Only working charging stations", + "nl": "Enkel werkende oplaadpunten", + "de": "Nur funktionierende Ladestationen" + }, + "osmTags": { + "and": [ + "operational_status!=broken", + "amenity=charging_station" + ] + } + } + ] + }, + { + "id": "connection_type", + "options": [ + { + "question": { + "en": "All connectors", + "nl": "Alle types", + "de": "Alle Anschlüsse" + } + }, + { + "question": { + "en": "Has a
Schuko wall plug without ground pin (CEE7/4 type F)
connector", + "nl": "Heeft een
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "osmTags": "socket:schuko~*" + }, + { + "question": { + "en": "Has a
European wall plug with ground pin (CEE7/4 type E)
connector", + "nl": "Heeft een
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "osmTags": "socket:typee~*" + }, + { + "question": { + "en": "Has a
Chademo
connector", + "nl": "Heeft een
Chademo
", + "de": "Hat einen
Chademo
Stecker" + }, + "osmTags": "socket:chademo~*" + }, + { + "question": { + "en": "Has a
Type 1 with cable (J1772)
connector", + "nl": "Heeft een
Type 1 met kabel (J1772)
" + }, + "osmTags": "socket:type1_cable~*" + }, + { + "question": { + "en": "Has a
Type 1 without cable (J1772)
connector", + "nl": "Heeft een
Type 1 zonder kabel (J1772)
" + }, + "osmTags": "socket:type1~*" + }, + { + "question": { + "en": "Has a
Type 1 CCS (aka Type 1 Combo)
connector", + "nl": "Heeft een
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "osmTags": "socket:type1_combo~*" + }, + { + "question": { + "en": "Has a
Tesla Supercharger
connector", + "nl": "Heeft een
Tesla Supercharger
", + "de": "Hat einen
Tesla Supercharger
Stecker" + }, + "osmTags": "socket:tesla_supercharger~*" + }, + { + "question": { + "en": "Has a
Type 2 (mennekes)
connector", + "nl": "Heeft een
Type 2 (mennekes)
" + }, + "osmTags": "socket:type2~*" + }, + { + "question": { + "en": "Has a
Type 2 CCS (mennekes)
connector", + "nl": "Heeft een
Type 2 CCS (mennekes)
" + }, + "osmTags": "socket:type2_combo~*" + }, + { + "question": { + "en": "Has a
Type 2 with cable (mennekes)
connector", + "nl": "Heeft een
Type 2 met kabel (J1772)
" + }, + "osmTags": "socket:type2_cable~*" + }, + { + "question": { + "en": "Has a
Tesla Supercharger CCS (a branded type2_css)
connector", + "nl": "Heeft een
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "osmTags": "socket:tesla_supercharger_ccs~*" + }, + { + "question": { + "en": "Has a
Tesla Supercharger (destination)
connector", + "nl": "Heeft een
Tesla Supercharger (destination)
" + }, + "osmTags": "socket:tesla_destination~*" + }, + { + "question": { + "en": "Has a
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
connector", + "nl": "Heeft een
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "osmTags": "socket:tesla_destination~*" + }, + { + "question": { + "en": "Has a
USB to charge phones and small electronics
connector", + "nl": "Heeft een
USB om GSMs en kleine electronica op te laden
" + }, + "osmTags": "socket:USB-A~*" + }, + { + "question": { + "en": "Has a
Bosch Active Connect with 3 pins and cable
connector", + "nl": "Heeft een
Bosch Active Connect met 3 pinnen aan een kabel
" + }, + "osmTags": "socket:bosch_3pin~*" + }, + { + "question": { + "en": "Has a
Bosch Active Connect with 5 pins and cable
connector", + "nl": "Heeft een
Bosch Active Connect met 5 pinnen aan een kabel
" + }, + "osmTags": "socket:bosch_5pin~*" + } + ] + } + ], + "units": [ + { + "appliesToKey": [ + "maxstay" + ], + "applicableUnits": [ + { + "canonicalDenomination": "minutes", + "canonicalDenominationSingular": "minute", + "alternativeDenomination": [ + "m", + "min", + "mins", + "minuten", + "mns" + ], + "human": { + "en": " minutes", + "nl": " minuten", + "de": " Minuten", + "ru": " минут" + }, + "humanSingular": { + "en": " minute", + "nl": " minuut", + "de": " Minute", + "ru": " минута" + } + }, + { + "canonicalDenomination": "hours", + "canonicalDenominationSingular": "hour", + "alternativeDenomination": [ + "h", + "hrs", + "hours", + "u", + "uur", + "uren" + ], + "human": { + "en": " hours", + "nl": " uren", + "de": " Stunden", + "ru": " часов" + }, + "humanSingular": { + "en": " hour", + "nl": " uur", + "de": " Stunde", + "ru": " час" + } + }, + { + "canonicalDenomination": "days", + "canonicalDenominationSingular": "day", + "alternativeDenomination": [ + "dys", + "dagen", + "dag" + ], + "human": { + "en": " days", + "nl": " day", + "de": " Tage", + "ru": " дней" + }, + "humanSingular": { + "en": " day", + "nl": " dag", + "de": " Tag", + "ru": " день" + } + } + ] + }, + { + "appliesToKey": [ + "socket:schuko:voltage", + "socket:typee:voltage", + "socket:chademo:voltage", + "socket:type1_cable:voltage", + "socket:type1:voltage", + "socket:type1_combo:voltage", + "socket:tesla_supercharger:voltage", + "socket:type2:voltage", + "socket:type2_combo:voltage", + "socket:type2_cable:voltage", + "socket:tesla_supercharger_ccs:voltage", + "socket:tesla_destination:voltage", + "socket:tesla_destination:voltage", + "socket:USB-A:voltage", + "socket:bosch_3pin:voltage", + "socket:bosch_5pin:voltage" + ], + "applicableUnits": [ + { + "canonicalDenomination": "V", + "alternativeDenomination": [ + "v", + "volt", + "voltage", + "V", + "Volt" + ], + "human": { + "en": "Volts", + "nl": "volt", + "de": "Volt", + "ru": "Вольт" + } + } + ], + "eraseInvalidValues": true + }, + { + "appliesToKey": [ + "socket:schuko:current", + "socket:typee:current", + "socket:chademo:current", + "socket:type1_cable:current", + "socket:type1:current", + "socket:type1_combo:current", + "socket:tesla_supercharger:current", + "socket:type2:current", + "socket:type2_combo:current", + "socket:type2_cable:current", + "socket:tesla_supercharger_ccs:current", + "socket:tesla_destination:current", + "socket:tesla_destination:current", + "socket:USB-A:current", + "socket:bosch_3pin:current", + "socket:bosch_5pin:current" + ], + "applicableUnits": [ + { + "canonicalDenomination": "A", + "alternativeDenomination": [ + "a", + "amp", + "amperage", + "A" + ], + "human": { + "en": "A", + "nl": "A" + } + } + ], + "eraseInvalidValues": true + }, + { + "appliesToKey": [ + "socket:schuko:output", + "socket:typee:output", + "socket:chademo:output", + "socket:type1_cable:output", + "socket:type1:output", + "socket:type1_combo:output", + "socket:tesla_supercharger:output", + "socket:type2:output", + "socket:type2_combo:output", + "socket:type2_cable:output", + "socket:tesla_supercharger_ccs:output", + "socket:tesla_destination:output", + "socket:tesla_destination:output", + "socket:USB-A:output", + "socket:bosch_3pin:output", + "socket:bosch_5pin:output" + ], + "applicableUnits": [ + { + "canonicalDenomination": "kW", + "alternativeDenomination": [ + "kilowatt" + ], + "human": { + "en": "kilowatt", + "nl": "kilowatt", + "de": "Kilowatt", + "ru": "киловатт" + } + }, + { + "canonicalDenomination": "mW", + "alternativeDenomination": [ + "megawatt" + ], + "human": { + "en": "megawatt", + "nl": "megawatt", + "de": "Megawatt", + "ru": "мегаватт" + } + } + ], + "eraseInvalidValues": true + } + ], + "allowMove": { + "enableRelocation": false, + "enableImproveAccuracy": true + }, + "deletion": { + "softDeletionTags": { + "and": [ + "amenity=", + "disused:amenity=charging_station" + ] + }, + "neededChangesets": 10 + }, + "mapRendering": [ + { + "location": [ + "point", + "centroid" + ], + "icon": { + "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", + "mappings": [ + { + "if": "bicycle=yes", + "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" + }, + { + "if": { + "or": [ + "car=yes", + "motorcar=yes" + ] + }, + "then": "pin:#fff;./assets/themes/charging_stations/car.svg" + } + ] + }, + "iconBadges": [ + { + "if": { + "or": [ + "disused:amenity=charging_station", + "operational_status=broken" + ] + }, + "then": "cross:#c22;" + }, + { + "if": { + "or": [ + "proposed:amenity=charging_station", + "planned:amenity=charging_station" + ] + }, + "then": "./assets/layers/charging_station/under_construction.svg" + }, + { + "if": { + "and": [ + "bicycle=yes", + { + "or": [ + "motorcar=yes", + "car=yes" + ] + } + ] + }, + "then": "circle:#fff;./assets/themes/charging_stations/car.svg" + } + ], + "iconSize": { + "render": "50,50,bottom" + } + } + ] } \ No newline at end of file diff --git a/assets/layers/public_bookcase/public_bookcase.json b/assets/layers/public_bookcase/public_bookcase.json index cabdc34f1..5188b070a 100644 --- a/assets/layers/public_bookcase/public_bookcase.json +++ b/assets/layers/public_bookcase/public_bookcase.json @@ -1,518 +1,518 @@ { - "id": "public_bookcase", - "name": { - "en": "Bookcases", - "nl": "Boekenruilkastjes", - "de": "Bücherschränke", - "fr": "Microbibliothèque", - "ru": "Книжные шкафы", - "it": "Microbiblioteche" - }, - "description": { - "en": "A streetside cabinet with books, accessible to anyone", - "nl": "Een straatkastje met boeken voor iedereen", - "de": "Ein Bücherschrank am Straßenrand mit Büchern, für jedermann zugänglich", - "fr": "Une armoire ou une boite contenant des livres en libre accès", - "it": "Una vetrinetta ai bordi della strada contenente libri, aperta al pubblico", - "ru": "Уличный шкаф с книгами, доступными для всех" - }, - "source": { - "osmTags": "amenity=public_bookcase" - }, - "minzoom": 10, - "wayHandling": 2, - "title": { - "render": { - "en": "Bookcase", - "nl": "Boekenruilkast", - "de": "Bücherschrank", - "fr": "Microbibliothèque", - "ru": "Книжный шкаф", - "it": "Microbiblioteca" - }, - "mappings": [ - { - "if": "name~*", - "then": { - "en": "Public bookcase {name}", - "nl": "Boekenruilkast {name}", - "de": "Öffentlicher Bücherschrank {name}", - "fr": "Microbibliothèque {name}", - "ru": "Общественный книжный шкаф {name}", - "it": "Microbiblioteca pubblica {name}" - } - } - ] - }, - "icon": { - "render": "./assets/themes/bookcases/bookcase.svg" - }, - "label": { - "mappings": [ - { - "if": "name~*", - "then": "
{name}
" - } - ] - }, - "color": { - "render": "#0000ff" - }, - "width": { - "render": "8" - }, - "presets": [ - { - "title": { - "en": "Bookcase", - "nl": "Boekenruilkast", - "de": "Bücherschrank", + "id": "public_bookcase", + "name": { + "en": "Bookcases", + "nl": "Boekenruilkastjes", + "de": "Bücherschränke", "fr": "Microbibliothèque", - "ru": "Книжный шкаф", - "it": "Microbiblioteca" - }, - "tags": [ - "amenity=public_bookcase" - ], - "preciseInput": { - "preferredBackground": "photo" - } - } - ], - "tagRenderings": [ - "images", - { - "id": "minimap", - "render": "{minimap():height: 9rem; border-radius: 2.5rem; overflow:hidden;border:1px solid gray}" + "ru": "Книжные шкафы", + "it": "Microbiblioteche" }, - { - "render": { - "en": "The name of this bookcase is {name}", - "nl": "De naam van dit boekenruilkastje is {name}", - "de": "Der Name dieses Bücherschrank lautet {name}", - "fr": "Le nom de cette microbibliothèque est {name}", - "ru": "Название книжного шкафа — {name}", - "it": "Questa microbiblioteca si chiama {name}" - }, - "question": { - "en": "What is the name of this public bookcase?", - "nl": "Wat is de naam van dit boekenuilkastje?", - "de": "Wie heißt dieser öffentliche Bücherschrank?", - "fr": "Quel est le nom de cette microbibliothèque ?", - "ru": "Как называется этот общественный книжный шкаф?", - "it": "Come si chiama questa microbiblioteca pubblica?" - }, - "freeform": { - "key": "name" - }, - "mappings": [ - { - "if": { - "and": [ - "noname=yes", - "name=" - ] - }, - "then": { - "en": "This bookcase doesn't have a name", - "nl": "Dit boekenruilkastje heeft geen naam", - "de": "Dieser Bücherschrank hat keinen Namen", - "fr": "Cette microbibliothèque n'a pas de nom", - "ru": "У этого книжного шкафа нет названия", - "it": "Questa microbiblioteca non ha un nome proprio" - } - } - ], - "id": "public_bookcase-name" + "description": { + "en": "A streetside cabinet with books, accessible to anyone", + "nl": "Een straatkastje met boeken voor iedereen", + "de": "Ein Bücherschrank am Straßenrand mit Büchern, für jedermann zugänglich", + "fr": "Une armoire ou une boite contenant des livres en libre accès", + "it": "Una vetrinetta ai bordi della strada contenente libri, aperta al pubblico", + "ru": "Уличный шкаф с книгами, доступными для всех" }, - { - "render": { - "en": "{capacity} books fit in this bookcase", - "nl": "Er passen {capacity} boeken", - "de": "{capacity} Bücher passen in diesen Bücherschrank", - "fr": "{capacity} livres peuvent entrer dans cette microbibliothèque", - "it": "Questa microbiblioteca può contenere fino a {capacity} libri", - "ru": "{capacity} книг помещается в этот книжный шкаф" - }, - "question": { - "en": "How many books fit into this public bookcase?", - "nl": "Hoeveel boeken passen er in dit boekenruilkastje?", - "de": "Wie viele Bücher passen in diesen öffentlichen Bücherschrank?", - "fr": "Combien de livres peuvent entrer dans cette microbibliothèque ?", - "ru": "Сколько книг помещается в этом общественном книжном шкафу?", - "it": "Quanti libri può contenere questa microbiblioteca?" - }, - "freeform": { - "key": "capacity", - "type": "nat", - "inline": true - }, - "id": "public_bookcase-capacity" + "source": { + "osmTags": "amenity=public_bookcase" }, - { - "id": "bookcase-booktypes", - "question": { - "en": "What kind of books can be found in this public bookcase?", - "nl": "Voor welke doelgroep zijn de meeste boeken in dit boekenruilkastje?", - "de": "Welche Art von Büchern sind in diesem öffentlichen Bücherschrank zu finden?", - "fr": "Quel type de livres peut-on dans cette microbibliothèque ?", - "it": "Che tipo di libri si possono trovare in questa microbiblioteca?", - "ru": "Какие книги можно найти в этом общественном книжном шкафу?" - }, - "mappings": [ - { - "if": "books=children", - "then": { - "en": "Mostly children books", - "nl": "Voornamelijk kinderboeken", - "de": "Vorwiegend Kinderbücher", - "fr": "Livres pour enfants", - "ru": "В основном детские книги", - "it": "Principalmente libri per l'infanzia" - } + "minzoom": 10, + "wayHandling": 2, + "title": { + "render": { + "en": "Bookcase", + "nl": "Boekenruilkast", + "de": "Bücherschrank", + "fr": "Microbibliothèque", + "ru": "Книжный шкаф", + "it": "Microbiblioteca" }, - { - "if": "books=adults", - "then": { - "en": "Mostly books for adults", - "nl": "Voornamelijk boeken voor volwassenen", - "de": "Vorwiegend Bücher für Erwachsene", - "fr": "Livres pour les adultes", - "ru": "В основном книги для взрослых", - "it": "Principalmente libri per persone in età adulta" - } - }, - { - "if": "books=children;adults", - "then": { - "en": "Both books for kids and adults", - "nl": "Boeken voor zowel kinderen als volwassenen", - "de": "Sowohl Bücher für Kinder als auch für Erwachsene", - "fr": "Livres pour enfants et adultes également", - "it": "Sia libri per l'infanzia, sia per l'età adulta", - "ru": "Книги и для детей, и для взрослых" - } - } - ] - }, - { - "id": "bookcase-is-indoors", - "question": { - "en": "Is this bookcase located outdoors?", - "nl": "Staat dit boekenruilkastje binnen of buiten?", - "de": "Befindet sich dieser Bücherschrank im Freien?", - "fr": "Cette microbiliothèque est-elle en extérieur ?", - "it": "Questa microbiblioteca si trova all'aperto?" - }, - "mappings": [ - { - "then": { - "en": "This bookcase is located indoors", - "nl": "Dit boekenruilkastje staat binnen", - "de": "Dieser Bücherschrank befindet sich im Innenbereich", - "fr": "Cette microbibliothèque est en intérieur", - "it": "Questa microbiblioteca si trova al chiuso" - }, - "if": "indoor=yes" - }, - { - "then": { - "en": "This bookcase is located outdoors", - "nl": "Dit boekenruilkastje staat buiten", - "de": "Dieser Bücherschrank befindet sich im Freien", - "fr": "Cette microbibliothèque est en extérieur", - "it": "Questa microbiblioteca si trova all'aperto" - }, - "if": "indoor=no" - }, - { - "then": { - "en": "This bookcase is located outdoors", - "nl": "Dit boekenruilkastje staat buiten", - "de": "Dieser Bücherschrank befindet sich im Freien", - "fr": "Cette microbibliothèque est en extérieur", - "it": "Questa microbiblioteca si trova all'aperto" - }, - "if": "indoor=", - "hideInAnswer": true - } - ] - }, - { - "id": "bookcase-is-accessible", - "question": { - "en": "Is this public bookcase freely accessible?", - "nl": "Is dit boekenruilkastje publiek toegankelijk?", - "de": "Ist dieser öffentliche Bücherschrank frei zugänglich?", - "fr": "Cette microbibliothèque est-elle librement accèssible ?", - "it": "Questa microbiblioteca è ad accesso libero?", - "ru": "Имеется ли свободный доступ к этому общественному книжному шкафу?" - }, - "condition": "indoor=yes", - "mappings": [ - { - "then": { - "en": "Publicly accessible", - "nl": "Publiek toegankelijk", - "de": "Öffentlich zugänglich", - "fr": "Accèssible au public", - "it": "È ad accesso libero", - "ru": "Свободный доступ" - }, - "if": "access=yes" - }, - { - "then": { - "en": "Only accessible to customers", - "nl": "Enkel toegankelijk voor klanten", - "de": "Nur für Kunden zugänglich", - "fr": "Accèssible aux clients", - "it": "L'accesso è riservato ai clienti" - }, - "if": "access=customers" - } - ] - }, - { - "question": { - "en": "Who maintains this public bookcase?", - "nl": "Wie is verantwoordelijk voor dit boekenruilkastje?", - "de": "Wer unterhält diesen öffentlichen Bücherschrank?", - "fr": "Qui entretien cette microbibliothèque ?", - "it": "Chi mantiene questa microbiblioteca?" - }, - "render": { - "en": "Operated by {operator}", - "nl": "Onderhouden door {operator}", - "de": "Betrieben von {operator}", - "fr": "Entretenue par {operator}", - "it": "È gestita da {operator}" - }, - "freeform": { - "type": "string", - "key": "operator" - }, - "id": "public_bookcase-operator" - }, - { - "question": { - "en": "Is this public bookcase part of a bigger network?", - "nl": "Is dit boekenruilkastje deel van een netwerk?", - "de": "Ist dieser öffentliche Bücherschrank Teil eines größeren Netzwerks?", - "fr": "Cette microbibliothèque fait-elle partie d'un réseau/groupe ?", - "it": "Questa microbiblioteca fa parte di una rete?" - }, - "render": { - "en": "This public bookcase is part of {brand}", - "nl": "Dit boekenruilkastje is deel van het netwerk {brand}", - "de": "Dieser Bücherschrank ist Teil von {brand}", - "fr": "Cette microbibliothèque fait partie du groupe {brand}", - "it": "Questa microbiblioteca fa parte di {brand}" - }, - "condition": "ref=", - "freeform": { - "key": "brand" - }, - "mappings": [ - { - "then": { - "en": "Part of the network 'Little Free Library'", - "nl": "Deel van het netwerk 'Little Free Library'", - "de": "Teil des Netzwerks 'Little Free Library'", - "fr": "Fait partie du réseau Little Free Library", - "it": "Fa parte della rete 'Little Free Library'" - }, - "if": { - "and": [ - "brand=Little Free Library", - "nobrand=" - ] - } - }, - { - "if": { - "and": [ - "nobrand=yes", - "brand=" - ] - }, - "then": { - "en": "This public bookcase is not part of a bigger network", - "nl": "Dit boekenruilkastje maakt geen deel uit van een netwerk", - "de": "Dieser öffentliche Bücherschrank ist nicht Teil eines größeren Netzwerks", - "fr": "Cette microbibliothèque ne fait pas partie d'un réseau/groupe", - "it": "Questa microbiblioteca non fa parte di una rete" - } - } - ], - "id": "public_bookcase-brand" - }, - { - "render": { - "en": "The reference number of this public bookcase within {brand} is {ref}", - "nl": "Het referentienummer binnen {brand} is {ref}", - "de": "Die Referenznummer dieses öffentlichen Bücherschranks innerhalb {brand} lautet {ref}", - "fr": "Cette microbibliothèque du réseau {brand} possède le numéro {ref}", - "it": "Il numero identificativo di questa microbiblioteca nella rete {brand} è {ref}" - }, - "question": { - "en": "What is the reference number of this public bookcase?", - "nl": "Wat is het referentienummer van dit boekenruilkastje?", - "de": "Wie lautet die Referenznummer dieses öffentlichen Bücherschranks?", - "fr": "Quelle est le numéro de référence de cette microbibliothèque ?", - "it": "Qual è il numero identificativo di questa microbiblioteca?" - }, - "condition": "brand~*", - "freeform": { - "key": "ref" - }, - "mappings": [ - { - "then": { - "en": "This bookcase is not part of a bigger network", - "nl": "Dit boekenruilkastje maakt geen deel uit van een netwerk", - "de": "Dieser Bücherschrank ist nicht Teil eines größeren Netzwerks", - "fr": "Cette microbibliothèque ne fait pas partie d'un réseau/groupe", - "it": "Questa microbiblioteca non fa parte di una rete" - }, - "if": { - "and": [ - "nobrand=yes", - "brand=", - "ref=" - ] - } - } - ], - "id": "public_bookcase-ref" - }, - { - "question": { - "en": "When was this public bookcase installed?", - "nl": "Op welke dag werd dit boekenruilkastje geinstalleerd?", - "de": "Wann wurde dieser öffentliche Bücherschrank installiert?", - "fr": "Quand a été installée cette microbibliothèque ?", - "it": "Quando è stata inaugurata questa microbiblioteca?", - "ru": "Когда был установлен этот общественный книжный шкаф?" - }, - "render": { - "en": "Installed on {start_date}", - "nl": "Geplaatst op {start_date}", - "de": "Installiert am {start_date}", - "fr": "Installée le {start_date}", - "it": "È stata inaugurata il {start_date}", - "ru": "Установлен {start_date}" - }, - "freeform": { - "key": "start_date", - "type": "date" - }, - "id": "public_bookcase-start_date" - }, - { - "render": { - "en": "More info on the website", - "nl": "Meer info op de website", - "de": "Weitere Informationen auf der Webseite", - "fr": "Plus d'infos sur le site web", - "ru": "Более подробная информация на сайте", - "it": "Maggiori informazioni sul sito web" - }, - "question": { - "en": "Is there a website with more information about this public bookcase?", - "nl": "Is er een website over dit boekenruilkastje?", - "de": "Gibt es eine Website mit weiteren Informationen über diesen öffentlichen Bücherschrank?", - "fr": "Y a-t-il un site web avec plus d'informations sur cette microbibliothèque ?", - "it": "C'è un sito web con maggiori informazioni su questa microbiblioteca?", - "ru": "Есть ли веб-сайт с более подробной информацией об этом общественном книжном шкафе?" - }, - "freeform": { - "key": "website", - "type": "url" - }, - "id": "public_bookcase-website" - } - ], - "deletion": { - "softDeletionTags": { - "and": [ - "disused:amenity=public_bookcase", - "amenity=" - ] - }, - "neededChangesets": 5 - }, - "filter": [ - { - "id": "kid-books", - "options": [ - { - "question": "Kinderboeken aanwezig?", - "osmTags": "books~.*children.*" - } - ] - }, - { - "id": "adult-books", - "options": [ - { - "question": "Boeken voor volwassenen aanwezig?", - "osmTags": "books~.*adults.*" - } - ] - }, - { - "id": "inside", - "options": [ - { - "question": { - "nl": "Binnen of buiten", - "en": "Indoor or outdoor", - "de": "Innen oder Außen" - } - }, - { - "question": "Binnen?", - "osmTags": "indoor=yes" - }, - { - "question": "Buiten?", - "osmTags": { - "or": [ - "indoor=no", - "indoor=" - ] - } - } - ] - } - ], - "mapRendering": [ - { - "icon": { - "render": "./assets/themes/bookcases/bookcase.svg" - }, - "label": { "mappings": [ - { - "if": "name~*", - "then": "
{name}
" - } + { + "if": "name~*", + "then": { + "en": "Public bookcase {name}", + "nl": "Boekenruilkast {name}", + "de": "Öffentlicher Bücherschrank {name}", + "fr": "Microbibliothèque {name}", + "ru": "Общественный книжный шкаф {name}", + "it": "Microbiblioteca pubblica {name}" + } + } ] - }, - "location": [ - "point", - "centroid" - ] }, - { - "color": { + "icon": { + "render": "./assets/themes/bookcases/bookcase.svg" + }, + "label": { + "mappings": [ + { + "if": "name~*", + "then": "
{name}
" + } + ] + }, + "color": { "render": "#0000ff" - }, - "width": { + }, + "width": { "render": "8" - } - } - ], - "allowMove": true + }, + "presets": [ + { + "title": { + "en": "Bookcase", + "nl": "Boekenruilkast", + "de": "Bücherschrank", + "fr": "Microbibliothèque", + "ru": "Книжный шкаф", + "it": "Microbiblioteca" + }, + "tags": [ + "amenity=public_bookcase" + ], + "preciseInput": { + "preferredBackground": "photo" + } + } + ], + "tagRenderings": [ + "images", + { + "id": "minimap", + "render": "{minimap():height: 9rem; border-radius: 2.5rem; overflow:hidden;border:1px solid gray}" + }, + { + "render": { + "en": "The name of this bookcase is {name}", + "nl": "De naam van dit boekenruilkastje is {name}", + "de": "Der Name dieses Bücherschrank lautet {name}", + "fr": "Le nom de cette microbibliothèque est {name}", + "ru": "Название книжного шкафа — {name}", + "it": "Questa microbiblioteca si chiama {name}" + }, + "question": { + "en": "What is the name of this public bookcase?", + "nl": "Wat is de naam van dit boekenuilkastje?", + "de": "Wie heißt dieser öffentliche Bücherschrank?", + "fr": "Quel est le nom de cette microbibliothèque ?", + "ru": "Как называется этот общественный книжный шкаф?", + "it": "Come si chiama questa microbiblioteca pubblica?" + }, + "freeform": { + "key": "name" + }, + "mappings": [ + { + "if": { + "and": [ + "noname=yes", + "name=" + ] + }, + "then": { + "en": "This bookcase doesn't have a name", + "nl": "Dit boekenruilkastje heeft geen naam", + "de": "Dieser Bücherschrank hat keinen Namen", + "fr": "Cette microbibliothèque n'a pas de nom", + "ru": "У этого книжного шкафа нет названия", + "it": "Questa microbiblioteca non ha un nome proprio" + } + } + ], + "id": "public_bookcase-name" + }, + { + "render": { + "en": "{capacity} books fit in this bookcase", + "nl": "Er passen {capacity} boeken", + "de": "{capacity} Bücher passen in diesen Bücherschrank", + "fr": "{capacity} livres peuvent entrer dans cette microbibliothèque", + "it": "Questa microbiblioteca può contenere fino a {capacity} libri", + "ru": "{capacity} книг помещается в этот книжный шкаф" + }, + "question": { + "en": "How many books fit into this public bookcase?", + "nl": "Hoeveel boeken passen er in dit boekenruilkastje?", + "de": "Wie viele Bücher passen in diesen öffentlichen Bücherschrank?", + "fr": "Combien de livres peuvent entrer dans cette microbibliothèque ?", + "ru": "Сколько книг помещается в этом общественном книжном шкафу?", + "it": "Quanti libri può contenere questa microbiblioteca?" + }, + "freeform": { + "key": "capacity", + "type": "nat", + "inline": true + }, + "id": "public_bookcase-capacity" + }, + { + "id": "bookcase-booktypes", + "question": { + "en": "What kind of books can be found in this public bookcase?", + "nl": "Voor welke doelgroep zijn de meeste boeken in dit boekenruilkastje?", + "de": "Welche Art von Büchern sind in diesem öffentlichen Bücherschrank zu finden?", + "fr": "Quel type de livres peut-on dans cette microbibliothèque ?", + "it": "Che tipo di libri si possono trovare in questa microbiblioteca?", + "ru": "Какие книги можно найти в этом общественном книжном шкафу?" + }, + "mappings": [ + { + "if": "books=children", + "then": { + "en": "Mostly children books", + "nl": "Voornamelijk kinderboeken", + "de": "Vorwiegend Kinderbücher", + "fr": "Livres pour enfants", + "ru": "В основном детские книги", + "it": "Principalmente libri per l'infanzia" + } + }, + { + "if": "books=adults", + "then": { + "en": "Mostly books for adults", + "nl": "Voornamelijk boeken voor volwassenen", + "de": "Vorwiegend Bücher für Erwachsene", + "fr": "Livres pour les adultes", + "ru": "В основном книги для взрослых", + "it": "Principalmente libri per persone in età adulta" + } + }, + { + "if": "books=children;adults", + "then": { + "en": "Both books for kids and adults", + "nl": "Boeken voor zowel kinderen als volwassenen", + "de": "Sowohl Bücher für Kinder als auch für Erwachsene", + "fr": "Livres pour enfants et adultes également", + "it": "Sia libri per l'infanzia, sia per l'età adulta", + "ru": "Книги и для детей, и для взрослых" + } + } + ] + }, + { + "id": "bookcase-is-indoors", + "question": { + "en": "Is this bookcase located outdoors?", + "nl": "Staat dit boekenruilkastje binnen of buiten?", + "de": "Befindet sich dieser Bücherschrank im Freien?", + "fr": "Cette microbiliothèque est-elle en extérieur ?", + "it": "Questa microbiblioteca si trova all'aperto?" + }, + "mappings": [ + { + "then": { + "en": "This bookcase is located indoors", + "nl": "Dit boekenruilkastje staat binnen", + "de": "Dieser Bücherschrank befindet sich im Innenbereich", + "fr": "Cette microbibliothèque est en intérieur", + "it": "Questa microbiblioteca si trova al chiuso" + }, + "if": "indoor=yes" + }, + { + "then": { + "en": "This bookcase is located outdoors", + "nl": "Dit boekenruilkastje staat buiten", + "de": "Dieser Bücherschrank befindet sich im Freien", + "fr": "Cette microbibliothèque est en extérieur", + "it": "Questa microbiblioteca si trova all'aperto" + }, + "if": "indoor=no" + }, + { + "then": { + "en": "This bookcase is located outdoors", + "nl": "Dit boekenruilkastje staat buiten", + "de": "Dieser Bücherschrank befindet sich im Freien", + "fr": "Cette microbibliothèque est en extérieur", + "it": "Questa microbiblioteca si trova all'aperto" + }, + "if": "indoor=", + "hideInAnswer": true + } + ] + }, + { + "id": "bookcase-is-accessible", + "question": { + "en": "Is this public bookcase freely accessible?", + "nl": "Is dit boekenruilkastje publiek toegankelijk?", + "de": "Ist dieser öffentliche Bücherschrank frei zugänglich?", + "fr": "Cette microbibliothèque est-elle librement accèssible ?", + "it": "Questa microbiblioteca è ad accesso libero?", + "ru": "Имеется ли свободный доступ к этому общественному книжному шкафу?" + }, + "condition": "indoor=yes", + "mappings": [ + { + "then": { + "en": "Publicly accessible", + "nl": "Publiek toegankelijk", + "de": "Öffentlich zugänglich", + "fr": "Accèssible au public", + "it": "È ad accesso libero", + "ru": "Свободный доступ" + }, + "if": "access=yes" + }, + { + "then": { + "en": "Only accessible to customers", + "nl": "Enkel toegankelijk voor klanten", + "de": "Nur für Kunden zugänglich", + "fr": "Accèssible aux clients", + "it": "L'accesso è riservato ai clienti" + }, + "if": "access=customers" + } + ] + }, + { + "question": { + "en": "Who maintains this public bookcase?", + "nl": "Wie is verantwoordelijk voor dit boekenruilkastje?", + "de": "Wer unterhält diesen öffentlichen Bücherschrank?", + "fr": "Qui entretien cette microbibliothèque ?", + "it": "Chi mantiene questa microbiblioteca?" + }, + "render": { + "en": "Operated by {operator}", + "nl": "Onderhouden door {operator}", + "de": "Betrieben von {operator}", + "fr": "Entretenue par {operator}", + "it": "È gestita da {operator}" + }, + "freeform": { + "type": "string", + "key": "operator" + }, + "id": "public_bookcase-operator" + }, + { + "question": { + "en": "Is this public bookcase part of a bigger network?", + "nl": "Is dit boekenruilkastje deel van een netwerk?", + "de": "Ist dieser öffentliche Bücherschrank Teil eines größeren Netzwerks?", + "fr": "Cette microbibliothèque fait-elle partie d'un réseau/groupe ?", + "it": "Questa microbiblioteca fa parte di una rete?" + }, + "render": { + "en": "This public bookcase is part of {brand}", + "nl": "Dit boekenruilkastje is deel van het netwerk {brand}", + "de": "Dieser Bücherschrank ist Teil von {brand}", + "fr": "Cette microbibliothèque fait partie du groupe {brand}", + "it": "Questa microbiblioteca fa parte di {brand}" + }, + "condition": "ref=", + "freeform": { + "key": "brand" + }, + "mappings": [ + { + "then": { + "en": "Part of the network 'Little Free Library'", + "nl": "Deel van het netwerk 'Little Free Library'", + "de": "Teil des Netzwerks 'Little Free Library'", + "fr": "Fait partie du réseau Little Free Library", + "it": "Fa parte della rete 'Little Free Library'" + }, + "if": { + "and": [ + "brand=Little Free Library", + "nobrand=" + ] + } + }, + { + "if": { + "and": [ + "nobrand=yes", + "brand=" + ] + }, + "then": { + "en": "This public bookcase is not part of a bigger network", + "nl": "Dit boekenruilkastje maakt geen deel uit van een netwerk", + "de": "Dieser öffentliche Bücherschrank ist nicht Teil eines größeren Netzwerks", + "fr": "Cette microbibliothèque ne fait pas partie d'un réseau/groupe", + "it": "Questa microbiblioteca non fa parte di una rete" + } + } + ], + "id": "public_bookcase-brand" + }, + { + "render": { + "en": "The reference number of this public bookcase within {brand} is {ref}", + "nl": "Het referentienummer binnen {brand} is {ref}", + "de": "Die Referenznummer dieses öffentlichen Bücherschranks innerhalb {brand} lautet {ref}", + "fr": "Cette microbibliothèque du réseau {brand} possède le numéro {ref}", + "it": "Il numero identificativo di questa microbiblioteca nella rete {brand} è {ref}" + }, + "question": { + "en": "What is the reference number of this public bookcase?", + "nl": "Wat is het referentienummer van dit boekenruilkastje?", + "de": "Wie lautet die Referenznummer dieses öffentlichen Bücherschranks?", + "fr": "Quelle est le numéro de référence de cette microbibliothèque ?", + "it": "Qual è il numero identificativo di questa microbiblioteca?" + }, + "condition": "brand~*", + "freeform": { + "key": "ref" + }, + "mappings": [ + { + "then": { + "en": "This bookcase is not part of a bigger network", + "nl": "Dit boekenruilkastje maakt geen deel uit van een netwerk", + "de": "Dieser Bücherschrank ist nicht Teil eines größeren Netzwerks", + "fr": "Cette microbibliothèque ne fait pas partie d'un réseau/groupe", + "it": "Questa microbiblioteca non fa parte di una rete" + }, + "if": { + "and": [ + "nobrand=yes", + "brand=", + "ref=" + ] + } + } + ], + "id": "public_bookcase-ref" + }, + { + "question": { + "en": "When was this public bookcase installed?", + "nl": "Op welke dag werd dit boekenruilkastje geinstalleerd?", + "de": "Wann wurde dieser öffentliche Bücherschrank installiert?", + "fr": "Quand a été installée cette microbibliothèque ?", + "it": "Quando è stata inaugurata questa microbiblioteca?", + "ru": "Когда был установлен этот общественный книжный шкаф?" + }, + "render": { + "en": "Installed on {start_date}", + "nl": "Geplaatst op {start_date}", + "de": "Installiert am {start_date}", + "fr": "Installée le {start_date}", + "it": "È stata inaugurata il {start_date}", + "ru": "Установлен {start_date}" + }, + "freeform": { + "key": "start_date", + "type": "date" + }, + "id": "public_bookcase-start_date" + }, + { + "render": { + "en": "More info on the website", + "nl": "Meer info op de website", + "de": "Weitere Informationen auf der Webseite", + "fr": "Plus d'infos sur le site web", + "ru": "Более подробная информация на сайте", + "it": "Maggiori informazioni sul sito web" + }, + "question": { + "en": "Is there a website with more information about this public bookcase?", + "nl": "Is er een website over dit boekenruilkastje?", + "de": "Gibt es eine Website mit weiteren Informationen über diesen öffentlichen Bücherschrank?", + "fr": "Y a-t-il un site web avec plus d'informations sur cette microbibliothèque ?", + "it": "C'è un sito web con maggiori informazioni su questa microbiblioteca?", + "ru": "Есть ли веб-сайт с более подробной информацией об этом общественном книжном шкафе?" + }, + "freeform": { + "key": "website", + "type": "url" + }, + "id": "public_bookcase-website" + } + ], + "deletion": { + "softDeletionTags": { + "and": [ + "disused:amenity=public_bookcase", + "amenity=" + ] + }, + "neededChangesets": 5 + }, + "filter": [ + { + "id": "kid-books", + "options": [ + { + "question": "Kinderboeken aanwezig?", + "osmTags": "books~.*children.*" + } + ] + }, + { + "id": "adult-books", + "options": [ + { + "question": "Boeken voor volwassenen aanwezig?", + "osmTags": "books~.*adults.*" + } + ] + }, + { + "id": "inside", + "options": [ + { + "question": { + "nl": "Binnen of buiten", + "en": "Indoor or outdoor", + "de": "Innen oder Außen" + } + }, + { + "question": "Binnen?", + "osmTags": "indoor=yes" + }, + { + "question": "Buiten?", + "osmTags": { + "or": [ + "indoor=no", + "indoor=" + ] + } + } + ] + } + ], + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/bookcases/bookcase.svg" + }, + "label": { + "mappings": [ + { + "if": "name~*", + "then": "
{name}
" + } + ] + }, + "location": [ + "point", + "centroid" + ] + }, + { + "color": { + "render": "#0000ff" + }, + "width": { + "render": "8" + } + } + ], + "allowMove": true } \ No newline at end of file diff --git a/langs/shared-questions/ca.json b/langs/shared-questions/ca.json deleted file mode 100644 index 0967ef424..000000000 --- a/langs/shared-questions/ca.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/langs/shared-questions/eo.json b/langs/shared-questions/eo.json deleted file mode 100644 index 0967ef424..000000000 --- a/langs/shared-questions/eo.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/langs/shared-questions/es.json b/langs/shared-questions/es.json deleted file mode 100644 index 0967ef424..000000000 --- a/langs/shared-questions/es.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/langs/shared-questions/fi.json b/langs/shared-questions/fi.json deleted file mode 100644 index 0967ef424..000000000 --- a/langs/shared-questions/fi.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/langs/shared-questions/hu.json b/langs/shared-questions/hu.json deleted file mode 100644 index 0967ef424..000000000 --- a/langs/shared-questions/hu.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/langs/shared-questions/ja.json b/langs/shared-questions/ja.json deleted file mode 100644 index 0967ef424..000000000 --- a/langs/shared-questions/ja.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/langs/shared-questions/nan.json b/langs/shared-questions/nan.json deleted file mode 100644 index 0967ef424..000000000 --- a/langs/shared-questions/nan.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/langs/shared-questions/zh_HANŨS.json b/langs/shared-questions/zh_HANŨS.json deleted file mode 100644 index 0967ef424..000000000 --- a/langs/shared-questions/zh_HANŨS.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/langs/shared-questions/zh_Hans.json b/langs/shared-questions/zh_Hans.json deleted file mode 100644 index 0967ef424..000000000 --- a/langs/shared-questions/zh_Hans.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/langs/themes/nan.json b/langs/themes/nan.json deleted file mode 100644 index 0967ef424..000000000 --- a/langs/themes/nan.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/langs/themes/pt.json b/langs/themes/pt.json deleted file mode 100644 index 0967ef424..000000000 --- a/langs/themes/pt.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/langs/themes/zh_HANŨS.json b/langs/themes/zh_HANŨS.json deleted file mode 100644 index 0967ef424..000000000 --- a/langs/themes/zh_HANŨS.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/langs/themes/zh_Hans.json b/langs/themes/zh_Hans.json deleted file mode 100644 index 0967ef424..000000000 --- a/langs/themes/zh_Hans.json +++ /dev/null @@ -1 +0,0 @@ -{} From 40a0e7931da30d5401535bdfd66cad809f45a510 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 28 Oct 2021 00:53:09 +0200 Subject: [PATCH 34/95] Remove empty elements --- UI/BaseUIElement.ts | 1 + UI/Popup/EditableTagRendering.ts | 12 ++++++++++-- UI/Popup/FeatureInfoBox.ts | 25 ++++++++++++------------- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/UI/BaseUIElement.ts b/UI/BaseUIElement.ts index a18b25972..80cb7b26e 100644 --- a/UI/BaseUIElement.ts +++ b/UI/BaseUIElement.ts @@ -45,6 +45,7 @@ export default abstract class BaseUIElement { * Adds all the relevant classes, space separated */ public SetClass(clss: string) { + if(clss == undefined){return } const all = clss.split(" ").map(clsName => clsName.trim()); let recordedChange = false; for (let c of all) { diff --git a/UI/Popup/EditableTagRendering.ts b/UI/Popup/EditableTagRendering.ts index 55cd7504d..be9634b2f 100644 --- a/UI/Popup/EditableTagRendering.ts +++ b/UI/Popup/EditableTagRendering.ts @@ -16,7 +16,10 @@ export default class EditableTagRendering extends Toggle { constructor(tags: UIEventSource, configuration: TagRenderingConfig, units: Unit [], - editMode = new UIEventSource(false) + options:{ + editMode? : UIEventSource , + innerElementClasses?: string + } ) { // The tagrendering is hidden if: @@ -27,7 +30,12 @@ export default class EditableTagRendering extends Toggle { (configuration?.condition?.matchesProperties(tags) ?? true)) super( - new Lazy(() => EditableTagRendering.CreateRendering(tags, configuration, units, editMode)), + new Lazy(() => { + const editMode = options.editMode ?? new UIEventSource(false) + const rendering = EditableTagRendering.CreateRendering(tags, configuration, units, editMode); + rendering.SetClass(options.innerElementClasses) + return rendering + }), undefined, renderingIsShown ) diff --git a/UI/Popup/FeatureInfoBox.ts b/UI/Popup/FeatureInfoBox.ts index cfffd0ffe..58a07cc3a 100644 --- a/UI/Popup/FeatureInfoBox.ts +++ b/UI/Popup/FeatureInfoBox.ts @@ -68,7 +68,8 @@ export default class FeatureInfoBox extends ScrollableFullScreen { const groupName = allGroupNames[i]; const trs = layerConfig.tagRenderings.filter(tr => tr.group === groupName) - const renderingsForGroup: BaseUIElement[] = [] + const renderingsForGroup: (EditableTagRendering | BaseUIElement)[] = [] + const innerClasses = "block w-full break-word text-default m-1 p-1 border-b border-gray-200 mb-2 pb-2"; for (const tr of trs) { if (tr.question === null || tr.id === "questions") { // This is a question box! @@ -76,21 +77,19 @@ export default class FeatureInfoBox extends ScrollableFullScreen { questionBoxes.delete(tr.group) renderingsForGroup.push(questionBox) } else { - const etr = new EditableTagRendering(tags, tr, layerConfig.units).SetClass("editable-tag-rendering") - + let classes = innerClasses + if(renderingsForGroup.length === 0 && i > 0){ + // This is the first element of a group! + // It should act as header and be sticky + classes= "sticky top-0" + } + + const etr = new EditableTagRendering(tags, tr, layerConfig.units,{ + innerElementClasses: innerClasses + }) renderingsForGroup.push(etr) } } - let j = 0 - if (i !== 0) { - renderingsForGroup[0]?.SetStyle("position: sticky; top: -5px") - j = 1 - } - for (/* j = 0 or 1 */; j < renderingsForGroup.length; j++) { - renderingsForGroup[j].SetClass("block w-full break-word text-default m-1 p-1 border-b border-gray-200 mb-2 pb-2") - } - - allRenderings.push(...renderingsForGroup) } From 85174461b4282836f8adf3cd98468a497b74c01e Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 28 Oct 2021 00:53:29 +0200 Subject: [PATCH 35/95] Port themes to the new format --- assets/themes/sidewalks/sidewalks.json | 26 +++- assets/themes/uk_addresses/uk_addresses.json | 134 ++++++++++--------- 2 files changed, 99 insertions(+), 61 deletions(-) diff --git a/assets/themes/sidewalks/sidewalks.json b/assets/themes/sidewalks/sidewalks.json index 59c29a952..31c97b49e 100644 --- a/assets/themes/sidewalks/sidewalks.json +++ b/assets/themes/sidewalks/sidewalks.json @@ -28,7 +28,13 @@ }, "minzoom": 12, "source": { - "osmTags": "highway=residential" + "osmTags": { + "or": [ + "highway=residential", + "highway=tertiary", + "highway=secondary" + ] + } }, "title": { "render": { @@ -112,6 +118,15 @@ "color": { "render": "#888" }, + "dasharray": { + "render": "", + "mappings": [ + { + "if": "sidewalk:right=", + "then": "6,6" + } + ] + }, "width": { "render": 6, "mappings": [ @@ -131,6 +146,15 @@ }, { "color": "#888", + "dasharray": { + "render": "", + "mappings": [ + { + "if": "sidewalk:right=", + "then": "6,6" + } + ] + }, "width": { "render": 6, "mappings": [ diff --git a/assets/themes/uk_addresses/uk_addresses.json b/assets/themes/uk_addresses/uk_addresses.json index 03c1de997..71e5c173e 100644 --- a/assets/themes/uk_addresses/uk_addresses.json +++ b/assets/themes/uk_addresses/uk_addresses.json @@ -57,23 +57,27 @@ }, "name": "Addresses to check", "minzoom": 14, - "wayHandling": 1, - "icon": { - "render": "./assets/themes/uk_addresses/housenumber_unknown.svg", - "mappings": [ - { - "if": "_embedding_object:id~*", - "then": "./assets/themes/uk_addresses/housenumber_unknown_small.svg" + "mapRendering": [ + { + "location": "point", + "icon": { + "render": "./assets/themes/uk_addresses/housenumber_unknown.svg", + "mappings": [ + { + "if": "_embedding_object:id~*", + "then": "./assets/themes/uk_addresses/housenumber_unknown_small.svg" + }, + { + "if": "_imported=yes", + "then": "./assets/themes/uk_addresses/housenumber_unknown_small.svg" + } + ] }, - { - "if": "_imported=yes", - "then": "./assets/themes/uk_addresses/housenumber_unknown_small.svg" + "iconSize": { + "render": "40,40,center" } - ] - }, - "iconSize": { - "render": "40,40,center" - }, + } + ], "title": { "render": "Address to be determined" }, @@ -225,50 +229,55 @@ } } ], - "icon": { - "render": "./assets/themes/uk_addresses/housenumber_ok.svg", - "mappings": [ - { - "if": { - "or": [ - { - "and": [ - "addr:housenumber=", - "nohousenumber!=yes" + "mapRendering": [ + { + "location": "point", + "icon": { + "render": "./assets/themes/uk_addresses/housenumber_ok.svg", + "mappings": [ + { + "if": { + "or": [ + { + "and": [ + "addr:housenumber=", + "nohousenumber!=yes" + ] + }, + "addr:street=" ] }, - "addr:street=" - ] - }, - "then": "./assets/themes/uk_addresses/housenumber_unknown.svg" - } - ] - }, - "width": { - "render": "8" - }, - "iconSize": { - "render": "40,40,center" - }, - "color": { - "render": "#00f", - "mappings": [ - { - "if": { - "or": [ - { - "and": [ - "addr:housenumber=", - "nohousenumber!=yes" + "then": "./assets/themes/uk_addresses/housenumber_unknown.svg" + } + ] + }, + "width": { + "render": "8" + }, + "iconSize": { + "render": "40,40,center" + }, + "color": { + "render": "#00f", + "mappings": [ + { + "if": { + "or": [ + { + "and": [ + "addr:housenumber=", + "nohousenumber!=yes" + ] + }, + "addr:street=" ] }, - "addr:street=" - ] - }, - "then": "#ff0" + "then": "#ff0" + } + ] } - ] - } + } + ] }, { "id": "named_streets", @@ -281,12 +290,17 @@ ] } }, - "color": { - "render": "#ccc" - }, - "width": { - "render": "0" - } + "mapRendering": [ + { + "location": "point", + "color": { + "render": "#ccc" + }, + "width": { + "render": "0" + } + } + ] } ], "enableShareScreen": false, From 9ad1d4d08917ec4201898834f2838afec5ad0e51 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 28 Oct 2021 00:53:44 +0200 Subject: [PATCH 36/95] Add missing dutch translations --- langs/layers/nl.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 545fc977b..005033ab6 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -3846,6 +3846,14 @@ } }, "question": "Wat voor soort vuilnisbak is dit?" + }, + "dispensing_dog_bags": { + "question": "Heeft deze vuilnisbak een verdeler voor hondenpoepzakjes?", + "mappings": { + "0": "Deze vuilnisbak heeft een verdeler voor hondenpoepzakjes", + "1": "Deze vuilnisbak heeft geenverdeler voor hondenpoepzakjes", + "2": "Deze vuilnisbaak heeft waarschijnlijk geen verdeler voor hondenpoepzakjes" + } } }, "title": { From 070262a959709bd79d3c83abba46f29bfb18f949 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 28 Oct 2021 00:56:34 +0200 Subject: [PATCH 37/95] Add missing translations --- assets/layers/waste_basket/waste_basket.json | 13 ++++++++---- langs/layers/nl.json | 22 +++++++++++++------- langs/nan.json | 1 - 3 files changed, 23 insertions(+), 13 deletions(-) delete mode 100644 langs/nan.json diff --git a/assets/layers/waste_basket/waste_basket.json b/assets/layers/waste_basket/waste_basket.json index 8f4c53e3a..71d686f30 100644 --- a/assets/layers/waste_basket/waste_basket.json +++ b/assets/layers/waste_basket/waste_basket.json @@ -89,7 +89,9 @@ { "id": "dispensing_dog_bags", "question": { - "en": "Does this waste basket have a dispenser for dog excrement bags?" + "en": "Does this waste basket have a dispenser for dog excrement bags?", + "nl": "Heeft deze vuilnisbak een verdeler voor hondenpoepzakjes?", + "then": "Heeft deze vuilnisbak een verdeler voor hondenpoepzakjes?" }, "condition": { "or": [ @@ -107,7 +109,8 @@ ] }, "then": { - "en": "This waste basket has a dispenser for (dog) excrement bags" + "en": "This waste basket has a dispenser for (dog) excrement bags", + "nl": "Deze vuilnisbak heeft een verdeler voor hondenpoepzakjes" } }, { @@ -118,13 +121,15 @@ ] }, "then": { - "en": "This waste basket does not have a dispenser for (dog) excrement bags" + "en": "This waste basket does not have a dispenser for (dog) excrement bags", + "nl": "Deze vuilnisbak heeft geenverdeler voor hondenpoepzakjes" } }, { "if": "vending=", "then": { - "en": "This waste basket does not have a dispenser for (dog) excrement bags" + "en": "This waste basket does not have a dispenser for (dog) excrement bags", + "nl": "Deze vuilnisbaak heeft waarschijnlijk geen verdeler voor hondenpoepzakjes" }, "hideInAnwer": true } diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 005033ab6..fc52ebc8e 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -3824,6 +3824,20 @@ } }, "tagRenderings": { + "dispensing_dog_bags": { + "mappings": { + "0": { + "then": "Deze vuilnisbak heeft een verdeler voor hondenpoepzakjes" + }, + "1": { + "then": "Deze vuilnisbak heeft geenverdeler voor hondenpoepzakjes" + }, + "2": { + "then": "Deze vuilnisbaak heeft waarschijnlijk geen verdeler voor hondenpoepzakjes" + } + }, + "question": "Heeft deze vuilnisbak een verdeler voor hondenpoepzakjes?" + }, "waste-basket-waste-types": { "mappings": { "0": { @@ -3846,14 +3860,6 @@ } }, "question": "Wat voor soort vuilnisbak is dit?" - }, - "dispensing_dog_bags": { - "question": "Heeft deze vuilnisbak een verdeler voor hondenpoepzakjes?", - "mappings": { - "0": "Deze vuilnisbak heeft een verdeler voor hondenpoepzakjes", - "1": "Deze vuilnisbak heeft geenverdeler voor hondenpoepzakjes", - "2": "Deze vuilnisbaak heeft waarschijnlijk geen verdeler voor hondenpoepzakjes" - } } }, "title": { diff --git a/langs/nan.json b/langs/nan.json deleted file mode 100644 index 0967ef424..000000000 --- a/langs/nan.json +++ /dev/null @@ -1 +0,0 @@ -{} From 10d9f18110562c1fa16e24bd7b0fc341d6004a8f Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 28 Oct 2021 01:26:35 +0200 Subject: [PATCH 38/95] Fixes to sidewalks theme --- Models/ThemeConfig/LineRenderingConfig.ts | 4 +- UI/Popup/FeatureInfoBox.ts | 8 +- .../left_right_style/left_right_style.json | 21 ++- assets/themes/grb_import/grb.json | 173 ++++++++++-------- assets/themes/sidewalks/sidewalks.json | 19 +- scripts/lint.ts | 2 +- 6 files changed, 136 insertions(+), 91 deletions(-) diff --git a/Models/ThemeConfig/LineRenderingConfig.ts b/Models/ThemeConfig/LineRenderingConfig.ts index 05da6604e..96f7f0a1a 100644 --- a/Models/ThemeConfig/LineRenderingConfig.ts +++ b/Models/ThemeConfig/LineRenderingConfig.ts @@ -29,7 +29,7 @@ public GenerateLeafletStyle( tags: {} ): { color: string, weight: number, - dashArray: number[], + dashArray: string, offset: number } { function rendernum(tr: TagRenderingConfig, deflt: number) { @@ -49,7 +49,7 @@ public GenerateLeafletStyle( tags: {} ): return Utils.SubstituteKeys(str, tags)?.replace(/{.*}/g, ""); } - const dashArray = render(this.dashArray)?.split(" ")?.map(Number); + const dashArray = render(this.dashArray); let color = render(this.color, "#00f"); if (color.startsWith("--")) { color = getComputedStyle(document.body).getPropertyValue( diff --git a/UI/Popup/FeatureInfoBox.ts b/UI/Popup/FeatureInfoBox.ts index 58a07cc3a..9b95512a9 100644 --- a/UI/Popup/FeatureInfoBox.ts +++ b/UI/Popup/FeatureInfoBox.ts @@ -78,15 +78,19 @@ export default class FeatureInfoBox extends ScrollableFullScreen { renderingsForGroup.push(questionBox) } else { let classes = innerClasses - if(renderingsForGroup.length === 0 && i > 0){ + let isHeader = renderingsForGroup.length === 0 && i > 0 + if(isHeader){ // This is the first element of a group! // It should act as header and be sticky - classes= "sticky top-0" + classes= "" } const etr = new EditableTagRendering(tags, tr, layerConfig.units,{ innerElementClasses: innerClasses }) + if(isHeader){ + etr.SetClass("sticky top-0") + } renderingsForGroup.push(etr) } } diff --git a/assets/layers/left_right_style/left_right_style.json b/assets/layers/left_right_style/left_right_style.json index f198f626a..57ec143ef 100644 --- a/assets/layers/left_right_style/left_right_style.json +++ b/assets/layers/left_right_style/left_right_style.json @@ -11,10 +11,21 @@ }, "mapRendering": [ { - "location": [ - "point" - ] - }, - {} + "width": 15, + "color": { + "render": "#ff000088", + "mappings": [{ + "if": "id=left", + "then": "#0000ff88" + }] + }, + "offset": { + "render": "-15", + "mappings": [{ + "if": "id=right", + "then": "15" + }] + } + } ] } \ No newline at end of file diff --git a/assets/themes/grb_import/grb.json b/assets/themes/grb_import/grb.json index 3a97f1c9a..ab379c070 100644 --- a/assets/themes/grb_import/grb.json +++ b/assets/themes/grb_import/grb.json @@ -29,35 +29,39 @@ "maxCacheAge": 0 }, "minzoom": 18, - "width": { - "render": "2" - }, - "color": { - "render": "#00c", - "mappings": [ - { - "if": "building=house", - "then": "#a00" + "mapRendering": [ + { + "width": { + "render": "2" }, - { - "if": "building=shed", - "then": "#563e02" - }, - { - "if": { - "or": [ - "building=garage", - "building=garages" - ] - }, - "then": "#f9bfbb" - }, - { - "if": "building=yes", - "then": "#0774f2" + "color": { + "render": "#00c", + "mappings": [ + { + "if": "building=house", + "then": "#a00" + }, + { + "if": "building=shed", + "then": "#563e02" + }, + { + "if": { + "or": [ + "building=garage", + "building=garages" + ] + }, + "then": "#f9bfbb" + }, + { + "if": "building=yes", + "then": "#0774f2" + } + ] } - ] - }, + } + ], "title": "OSM-gebouw", "tagRenderings": [ "all_tags" @@ -93,12 +97,16 @@ "maxCacheAge": 0 }, "minzoom": 18, - "color": { - "render": "#00c" - }, - "width": { - "render": "1" - }, + "mapRendering": [ + { + "color": { + "render": "#00c" + }, + "width": { + "render": "1" + } + } + ], "title": { "render": { "*": "OSM-Object" @@ -265,26 +273,34 @@ } } ], - "label": { - "mappings": [ - { - "if": "addr:housenumber~*", - "then": "
{addr:housenumber}
" + "mapRendering": [ + { + "location": [ + "point", + "center" + ], + "label": { + "mappings": [ + { + "if": "addr:housenumber~*", + "then": "
{addr:housenumber}
" + } + ] + }, + "iconSize": { + "render": "40,40,center" } - ] - }, - "width": { - "render": "2" - }, - "iconSize": { - "render": "40,40,center" - }, - "dashes": "2 2", - "color": { - "render": "#00f" - }, - "wayHandling": 2, - "presets": [] + }, + { + "dashes": "2 2", + "color": { + "render": "#00f" + }, + "width": { + "render": "2" + } + } + ] }, { "id": "crab-addresses 2021-10-26", @@ -298,8 +314,13 @@ "minzoom": 19, "name": "CRAB-addressen", "title": "CRAB-adres", - "icon": "circle:#bb3322", - "iconSize": "15,15,center", + "mapRendering": [ + { + "location": "point", + "icon": "circle:#bb3322", + "iconSize": "15,15,center" + } + ], "tagRenderings": [ "all_tags", { @@ -465,25 +486,33 @@ } } ], - "label": { - "mappings": [ - { - "if": "addr:housenumber~*", - "then": "
{addr:housenumber}
" + "mapRendering": [ + { + "location": [ + "point", + "center" + ], + "iconSize": { + "render": "40,40,center" + }, + "label": { + "mappings": [ + { + "if": "addr:housenumber~*", + "then": "
{addr:housenumber}
" + } + ] } - ] - }, - "width": { - "render": "2" - }, - "iconSize": { - "render": "40,40,center" - }, - "dashes": "2 2", - "color": { - "render": "#00f" - }, - "wayHandling": 2, + }, + { + "width": { + "render": "2" + }, + "color": { + "render": "#00f" + } + } + ], "presets": [] } ], diff --git a/assets/themes/sidewalks/sidewalks.json b/assets/themes/sidewalks/sidewalks.json index 31c97b49e..540cdb57a 100644 --- a/assets/themes/sidewalks/sidewalks.json +++ b/assets/themes/sidewalks/sidewalks.json @@ -20,6 +20,7 @@ "startZoom": 1, "widenFactor": 0.05, "socialImage": "", + "hideFromOverview": true, "layers": [ { "id": "sidewalks", @@ -68,11 +69,11 @@ "renderings": [ { "id": "sidewalk_minimap", - "render": "{sided_minimap(left|right):height:3rem;border-radius:0.5rem;overflow:hidden}" + "render": "{sided_minimap(left|right):height:8rem;border-radius:0.5rem;overflow:hidden}" }, { "id": "has_sidewalk", - "question": "Is there a sidewalk on the left|right side of the road?", + "question": "Is there a sidewalk on this side of the road?", "mappings": [ { "if": "sidewalk:left|right=yes", @@ -111,19 +112,21 @@ "iconSize": "20,20,center" }, { + "#": "The center line", "color": "#ffffff55", "width": 8 }, { + "#": "left", "color": { "render": "#888" }, - "dasharray": { + "dashArray": { "render": "", "mappings": [ { - "if": "sidewalk:right=", - "then": "6,6" + "if": "sidewalk:left=", + "then": "1,12" } ] }, @@ -133,7 +136,6 @@ { "if": { "or": [ - "sidewalk:left=", "sidewalk:left=no", "sidewalk:left=separate" ] @@ -146,12 +148,12 @@ }, { "color": "#888", - "dasharray": { + "dashArray": { "render": "", "mappings": [ { "if": "sidewalk:right=", - "then": "6,6" + "then": "1,12" } ] }, @@ -161,7 +163,6 @@ { "if": { "or": [ - "sidewalk:right=", "sidewalk:right=no", "sidewalk:right=separate" ] diff --git a/scripts/lint.ts b/scripts/lint.ts index 92ec2facc..565903c23 100644 --- a/scripts/lint.ts +++ b/scripts/lint.ts @@ -32,7 +32,7 @@ function fixLayerConfig(config: LayerConfigJson): void { } } - if (config.mapRendering === undefined || config.id !== "sidewalks") { + if (config.mapRendering === undefined && config.id !== "sidewalks") { // This is a legacy format, lets create a pointRendering let location: ("point" | "centroid")[] = ["point"] let wayHandling: number = config["wayHandling"] ?? 0 From c74989e88d1eb55259e79c335e62e4f850b7590b Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 28 Oct 2021 03:15:36 +0200 Subject: [PATCH 39/95] Rendering bug fixes, add 'get' to the ExtraFunctions, filtering in the GRB theme --- Logic/ExtraFunction.ts | 39 +++- Logic/FeatureSource/FeaturePipeline.ts | 5 +- .../Sources/FilteringFeatureSource.ts | 45 ++++- Models/ThemeConfig/LineRenderingConfig.ts | 10 +- Models/ThemeConfig/PointRenderingConfig.ts | 13 ++ UI/ShowDataLayer/ShowDataLayer.ts | 8 +- assets/themes/grb_import/grb.json | 102 +++++++++- assets/themes/natuurpunt/natuurpunt.json | 188 +++++++++++------- assets/themes/uk_addresses/uk_addresses.json | 22 +- 9 files changed, 318 insertions(+), 114 deletions(-) diff --git a/Logic/ExtraFunction.ts b/Logic/ExtraFunction.ts index 4fc1840c1..d9fb6ede8 100644 --- a/Logic/ExtraFunction.ts +++ b/Logic/ExtraFunction.ts @@ -163,12 +163,41 @@ export class ExtraFunction { } ) + private static readonly GetParsed = new ExtraFunction( + { + name: "get", + doc: "Gets the property of the feature, parses it (as JSON) and returns it. Might return 'undefined' if not defined, null, ...", + args: ["key"] + }, + (params, feat) => { + return key => { + const value = feat.properties[key] + if (value === undefined) { + return undefined; + } + try { + const parsed = JSON.parse(value) + if(parsed === null){ + return undefined; + } + return parsed; + } catch (e) { + console.warn("Could not parse property " + key + " due to: " + e + ", the value is " + value) + return undefined; + } + + } + + } + ) + private static readonly allFuncs: ExtraFunction[] = [ ExtraFunction.DistanceToFunc, ExtraFunction.OverlapFunc, ExtraFunction.ClosestObjectFunc, ExtraFunction.ClosestNObjectFunc, - ExtraFunction.Memberships + ExtraFunction.Memberships, + ExtraFunction.GetParsed ]; private readonly _name: string; private readonly _args: string[]; @@ -248,11 +277,11 @@ export class ExtraFunction { console.error("Could not calculate the distance between", feature, "and", otherFeature) throw "Undefined distance!" } - - if(distance === 0){ - console.trace("Got a suspiciously zero distance between", otherFeature, "and self-feature",feature) + + if (distance === 0) { + console.trace("Got a suspiciously zero distance between", otherFeature, "and self-feature", feature) } - + if (distance > maxDistance) { continue } diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index a017f6818..0f2fa19ef 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -12,7 +12,6 @@ import OverpassFeatureSource from "../Actors/OverpassFeatureSource"; import {Changes} from "../Osm/Changes"; import GeoJsonSource from "./Sources/GeoJsonSource"; import Loc from "../../Models/Loc"; -import WayHandlingApplyingFeatureSource from "./Sources/RenderingMultiPlexerFeatureSource"; import RegisteringAllFromFeatureSourceActor from "./Actors/RegisteringAllFromFeatureSourceActor"; import TiledFromLocalStorageSource from "./TiledFeatureSource/TiledFromLocalStorageSource"; import SaveTileToLocalStorageActor from "./Actors/SaveTileToLocalStorageActor"; @@ -26,6 +25,7 @@ import OsmFeatureSource from "./TiledFeatureSource/OsmFeatureSource"; import {OsmConnection} from "../Osm/OsmConnection"; import {Tiles} from "../../Models/TileRange"; import TileFreshnessCalculator from "./TileFreshnessCalculator"; +import {ElementStorage} from "../ElementStorage"; /** @@ -85,7 +85,8 @@ export default class FeaturePipeline { readonly overpassMaxZoom: UIEventSource; readonly osmConnection: OsmConnection readonly currentBounds: UIEventSource, - readonly osmApiTileSize: UIEventSource + readonly osmApiTileSize: UIEventSource, + readonly allElements: ElementStorage }) { this.state = state; diff --git a/Logic/FeatureSource/Sources/FilteringFeatureSource.ts b/Logic/FeatureSource/Sources/FilteringFeatureSource.ts index ec097acca..cf0475d26 100644 --- a/Logic/FeatureSource/Sources/FilteringFeatureSource.ts +++ b/Logic/FeatureSource/Sources/FilteringFeatureSource.ts @@ -3,6 +3,8 @@ import FilteredLayer from "../../../Models/FilteredLayer"; import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; import Hash from "../../Web/Hash"; import {BBox} from "../../BBox"; +import {ElementStorage} from "../../ElementStorage"; +import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"; export default class FilteringFeatureSource implements FeatureSourceForLayer, Tiled { public features: UIEventSource<{ feature: any; freshness: Date }[]> = @@ -12,12 +14,16 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti public readonly tileIndex: number public readonly bbox: BBox private readonly upstream: FeatureSourceForLayer; - private readonly state: { locationControl: UIEventSource<{ zoom: number }>; selectedElement: UIEventSource }; + private readonly state: { + locationControl: UIEventSource<{ zoom: number }>; selectedElement: UIEventSource, + allElements: ElementStorage + }; constructor( state: { locationControl: UIEventSource<{ zoom: number }>, selectedElement: UIEventSource, + allElements: ElementStorage }, tileIndex, upstream: FeatureSourceForLayer @@ -30,23 +36,51 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti this.layer = upstream.layer; const layer = upstream.layer; - + const self = this; upstream.features.addCallback(() => { - this. update(); + self.update(); }); layer.appliedFilters.addCallback(_ => { - this.update() + self.update() + }) + + this._is_dirty.stabilized(250).addCallbackAndRunD(dirty => { + if (dirty) { + self.update() + } }) this.update(); } - public update() { + private readonly _alreadyRegistered = new Set>(); + private readonly _is_dirty = new UIEventSource(false) + + private registerCallback(feature: any, layer: LayerConfig) { + const src = this.state.allElements.addOrGetElement(feature) + if (this._alreadyRegistered.has(src)) { + return + } + this._alreadyRegistered.add(src) + if (layer.isShown !== undefined) { + + const self = this; + src.map(tags => layer.isShown?.GetRenderValue(tags, "yes").txt).addCallbackAndRunD(isShown => { + self._is_dirty.setData(true) + }) + } + } + + public update() { + const self = this; const layer = this.upstream.layer; const features: { feature: any; freshness: Date }[] = this.upstream.features.data; const newFeatures = features.filter((f) => { + + self.registerCallback(f.feature, layer.layerDef) + if ( this.state.selectedElement.data?.id === f.feature.id || f.feature.id === Hash.hash.data) { @@ -79,6 +113,7 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti }); this.features.setData(newFeatures); + this._is_dirty.setData(false) } } diff --git a/Models/ThemeConfig/LineRenderingConfig.ts b/Models/ThemeConfig/LineRenderingConfig.ts index 96f7f0a1a..1f0397888 100644 --- a/Models/ThemeConfig/LineRenderingConfig.ts +++ b/Models/ThemeConfig/LineRenderingConfig.ts @@ -13,19 +13,19 @@ export default class LineRenderingConfig extends WithContextLoader { public readonly dashArray: TagRenderingConfig; public readonly offset: TagRenderingConfig; public readonly leftRightSensitive: boolean - + constructor(json: LineRenderingConfigJson, context: string) { super(json, context) this.color = this.tr("color", "#0000ff"); this.width = this.tr("width", "7"); this.dashArray = this.tr("dashArray", ""); - - this.leftRightSensitive = json.offset !== undefined && json.offset !== 0 && json.offset !== "0" - + + this.leftRightSensitive = json.offset !== undefined && json.offset !== 0 && json.offset !== "0" + this.offset = this.tr("offset", "0"); } -public GenerateLeafletStyle( tags: {} ): + public GenerateLeafletStyle(tags: {}): { color: string, weight: number, diff --git a/Models/ThemeConfig/PointRenderingConfig.ts b/Models/ThemeConfig/PointRenderingConfig.ts index e34d6e34c..4eb6d2f46 100644 --- a/Models/ThemeConfig/PointRenderingConfig.ts +++ b/Models/ThemeConfig/PointRenderingConfig.ts @@ -15,6 +15,7 @@ import {VariableUiElement} from "../../UI/Base/VariableUIElement"; export default class PointRenderingConfig extends WithContextLoader { + private static readonly allowed_location_codes = new Set(["point", "centroid","start","end"]) public readonly location: Set<"point" | "centroid" | "start" | "end"> public readonly icon: TagRenderingConfig; @@ -25,7 +26,19 @@ export default class PointRenderingConfig extends WithContextLoader { constructor(json: PointRenderingConfigJson, context: string) { super(json, context) + + if(typeof json.location === "string"){ + json.location = [json.location] + } + this.location = new Set(json.location) + + this.location.forEach(l => { + const allowed = PointRenderingConfig.allowed_location_codes + if(!allowed.has(l)){ + throw `A point rendering has an invalid location: '${l}' is not one of ${Array.from(allowed).join(", ")} (at ${context}.location)` + } + }) if(this.location.size == 0){ throw "A pointRendering should have at least one 'location' to defined where it should be rendered. (At "+context+".location)" diff --git a/UI/ShowDataLayer/ShowDataLayer.ts b/UI/ShowDataLayer/ShowDataLayer.ts index e2b5f6440..309903348 100644 --- a/UI/ShowDataLayer/ShowDataLayer.ts +++ b/UI/ShowDataLayer/ShowDataLayer.ts @@ -212,12 +212,13 @@ export default class ShowDataLayer { const lineRenderingIndex = feature.lineRenderingIndex if (pointRenderingIndex !== undefined) { + const style = layer.mapRendering[pointRenderingIndex].GenerateLeafletStyle(tagsSource, this._enablePopups) return { - icon: layer.mapRendering[pointRenderingIndex].GenerateLeafletStyle(tagsSource, this._enablePopups) + icon: style } } if (lineRenderingIndex !== undefined) { - return layer.lineRendering[lineRenderingIndex].GenerateLeafletStyle(tagsSource) + return layer.lineRendering[lineRenderingIndex].GenerateLeafletStyle(tagsSource.data) } throw "Neither lineRendering nor mapRendering defined for " + feature @@ -232,9 +233,8 @@ export default class ShowDataLayer { if (layer === undefined) { return; } - let tagSource = this.allElements?.getEventSourceById(feature.properties.id) ?? new UIEventSource(feature.properties) - const clickable = !(layer.title === undefined && (layer.tagRenderings ?? []).length === 0) + const clickable = !(layer.title === undefined && (layer.tagRenderings ?? []).length === 0) && this._enablePopups let style: any = layer.mapRendering[feature.pointRenderingIndex].GenerateLeafletStyle(tagSource, clickable); const baseElement = style.html; if (!this._enablePopups) { diff --git a/assets/themes/grb_import/grb.json b/assets/themes/grb_import/grb.json index ab379c070..144e85ffd 100644 --- a/assets/themes/grb_import/grb.json +++ b/assets/themes/grb_import/grb.json @@ -20,6 +20,9 @@ "startZoom": 14, "widenFactor": 2, "socialImage": "", + "clustering": { + "maxZoom": 15 + }, "layers": [ { "id": "OSM-buildings", @@ -28,7 +31,7 @@ "osmTags": "building~*", "maxCacheAge": 0 }, - "minzoom": 18, + "minzoom": 16, "mapRendering": [ { "width": { @@ -277,7 +280,7 @@ { "location": [ "point", - "center" + "centroid" ], "label": { "mappings": [ @@ -311,17 +314,46 @@ "geoJsonZoomLevel": 18, "maxCacheAge": 0 }, - "minzoom": 19, + "minzoom": 16, "name": "CRAB-addressen", "title": "CRAB-adres", "mapRendering": [ { - "location": "point", + "location": ["point"], "icon": "circle:#bb3322", "iconSize": "15,15,center" } ], + "calculatedTags": [ + "_embedded_in=feat.overlapWith('OSM-buildings').filter(f => f.feat.properties['addr:housenumber'] !== undefined)[0]?.feat?.properties ", + "_embedding_nr=feat.get('_embedded_in')['addr:housenumber']", + "_embedding_street=feat.get('_embedded_in')['addr:street']" + ], + "isShown": { + "render": "yes", + "mappings": [ + { + "if": { + "and":["_embedding_nr:={HUISNR}","_embedding_street:={STRAATNM}"] + }, + "then": "no" + } + ] + }, "tagRenderings": [ + + { + "id": "render_crab", + "render": "Volgens het CRAB ligt hier {STRAATNM} {HUISNR} (label: {HNRLABEL})" + }, + { + "id": "render_embedded", + "render": "Het omliggende object met addres heeft {_embedding_street} {_embedding_nr}", + "condition": { + "and": ["_embedding_street~*","_embedding_nr~*"] + } + }, + "all_tags", { "id": "import-button", @@ -334,7 +366,7 @@ "name": { "nl": "Fixmes op gebouwen" }, - "minzoom": 12, + "minzoom": 16, "source": { "maxCacheAge": 0, "osmTags": { @@ -490,7 +522,7 @@ { "location": [ "point", - "center" + "centroid" ], "iconSize": { "render": "40,40,center" @@ -512,12 +544,66 @@ "render": "#00f" } } + ] + }, + { + "id": "GRB", + "source": { + "geoJson": "https://betadata.grbosm.site/grb?bbox={x_min},{y_min},{x_max},{y_max}", + "geoJsonZoomLevel": 18, + "mercatorCrs": true, + "maxCacheAge": 0 + }, + "name": "GRB geometries", + "title": "GRB outline", + "minzoom": 16, + "calculatedTags": [ + "_overlaps_with=feat.overlapWith('OSM-buildings').filter(f => f.overlap > 1 && feat.properties._surface - f.overlap < 5)[0] ?? null", + "_osm_obj:source:ref=JSON.parse(feat.properties._overlaps_with)?.feat?.properties['source:geometry:ref']", + "_osm_obj:source:date=JSON.parse(feat.properties._overlaps_with)?.feat?.properties['source:geometry:date'].replace(/\\//g, '-')", + "_imported_osm_object_found= feat.properties['_osm_obj:source:ref'] == feat.properties['source:geometry:entity'] + '/' + feat.properties['source:geometry:oidn']", + "_grb_date=feat.properties['source:geometry:date'].replace(/\\//g,'-')", + "_imported_osm_still_fresh= feat.properties['_osm_obj:source:date'] == feat.properties._grb_date" ], - "presets": [] + "tagRenderings": [ + "all_tags" + ], + "isShown": { + "render": "yes", + "mappings": [ + { + "if": { + "and": [ + "_imported_osm_object_found=true", + "_imported_osm_still_fresh=true" + ] + }, + "then": "no" + } + ] + }, + "mapRendering": [ + { + "color": { + "render": "#00a", + "mappings": [ + { + "if": { + "and": [ + "_imported_osm_object_found=true", + "_imported_osm_still_fresh=true" + ] + }, + "then": "#0f0" + } + ] + } + } + ] } ], "hideFromOverview": true, "defaultBackgroundId": "AGIVFlandersGRB", - "overpassMaxZoom": 18, + "overpassMaxZoom": 15, "osmApiTileSize": 17 } \ No newline at end of file diff --git a/assets/themes/natuurpunt/natuurpunt.json b/assets/themes/natuurpunt/natuurpunt.json index 4fc3421eb..6441dcf3a 100644 --- a/assets/themes/natuurpunt/natuurpunt.json +++ b/assets/themes/natuurpunt/natuurpunt.json @@ -57,9 +57,13 @@ }, "minzoom": 13, "minzoomVisible": 0, - "icon": { - "render": "circle:#FE6F32;./assets/themes/natuurpunt/nature_reserve.svg" - } + "mapRendering": [ + { + "icon": { + "render": "circle:#FE6F32;./assets/themes/natuurpunt/nature_reserve.svg" + } + } + ] } }, { @@ -77,9 +81,13 @@ "isOsmCache": "duplicate" }, "minzoom": 1, - "icon": { - "render": "circle:#FE6F32;./assets/themes/natuurpunt/nature_reserve.svg" - }, + "mapRendering": [ + { + "icon": { + "render": "circle:#FE6F32;./assets/themes/natuurpunt/nature_reserve.svg" + } + } + ], "presets": [] } }, @@ -96,9 +104,13 @@ "isOsmCache": true }, "minzoom": 1, - "icon": { - "render": "circle:#FE6F32;./assets/themes/natuurpunt/information.svg" - } + "mapRendering": [ + { + "icon": { + "render": "circle:#FE6F32;./assets/themes/natuurpunt/information.svg" + } + } + ] } }, { @@ -115,19 +127,23 @@ "isOsmCache": true }, "minzoom": 10, - "icon": { - "render": "circle:#FE6F32;./assets/themes/natuurpunt/trail.svg", - "mappings": [ - { - "if": "wheelchair=yes", - "then": "circle:#FE6F32;./assets/themes/natuurpunt/walk_wheelchair.svg" - }, - { - "if": "pushchair=yes", - "then": "circle:#FE6F32;./assets/themes/natuurpunt/pushchair.svg" + "mapRendering": [ + { + "icon": { + "render": "circle:#FE6F32;./assets/themes/natuurpunt/trail.svg", + "mappings": [ + { + "if": "wheelchair=yes", + "then": "circle:#FE6F32;./assets/themes/natuurpunt/walk_wheelchair.svg" + }, + { + "if": "pushchair=yes", + "then": "circle:#FE6F32;./assets/themes/natuurpunt/pushchair.svg" + } + ] } - ] - } + } + ] } }, { @@ -139,19 +155,23 @@ "geoJsonZoomLevel": 12, "isOsmCache": true }, - "icon": { - "render": "circle:#FE6F32;./assets/themes/natuurpunt/toilets.svg", - "mappings": [ - { - "if": "wheelchair=yes", - "then": "circle:#FE6F32;./assets/themes/natuurpunt/wheelchair.svg" - }, - { - "if": "toilets:position=urinals", - "then": "circle:#FE6F32;./assets/themes/natuurpunt/urinal.svg" + "mapRendering": [ + { + "icon": { + "render": "circle:#FE6F32;./assets/themes/natuurpunt/toilets.svg", + "mappings": [ + { + "if": "wheelchair=yes", + "then": "circle:#FE6F32;./assets/themes/natuurpunt/wheelchair.svg" + }, + { + "if": "toilets:position=urinals", + "then": "circle:#FE6F32;./assets/themes/natuurpunt/urinal.svg" + } + ] } - ] - } + } + ] } }, { @@ -163,10 +183,14 @@ "geoJsonZoomLevel": 12, "isOsmCache": true }, - "icon": { - "render": "circle:#FE6F32;./assets/themes/natuurpunt/birdhide.svg", - "mappings": null - } + "mapRendering": [ + { + "icon": { + "render": "circle:#FE6F32;./assets/themes/natuurpunt/birdhide.svg", + "mappings": null + } + } + ] } }, { @@ -178,9 +202,13 @@ "geoJsonZoomLevel": 12, "isOsmCache": true }, - "icon": { - "render": "circle:#FE6F32;./assets/themes/natuurpunt/picnic_table.svg" - } + "mapRendering": [ + { + "icon": { + "render": "circle:#FE6F32;./assets/themes/natuurpunt/picnic_table.svg" + } + } + ] } }, { @@ -192,34 +220,42 @@ "geoJsonZoomLevel": 12, "isOsmCache": true }, - "icon": { - "render": "circle:#FE6F32;./assets/themes/natuurpunt/drips.svg" - } + "mapRendering": [ + { + "icon": { + "render": "circle:#FE6F32;./assets/themes/natuurpunt/drips.svg" + } + } + ] } }, { "builtin": "parking", "override": { "minzoom": "16", - "icon": { - "render": "circle:#FE6F32;./assets/themes/natuurpunt/parking.svg", - "mappings": [ - { - "if": "amenity=bicycle_parking", - "then": "circle:#FE6F32;./assets/themes/natuurpunt/parkingbike.svg" - } - ] - }, - "iconOverlays": [ + "mapRendering": [ { - "if": "amenity=motorcycle_parking", - "then": "circle:#335D9F;./assets/themes/natuurpunt/parkingmotor.svg", - "badge": true - }, - { - "if": "capacity:disabled=yes", - "then": "circle:#335D9F;./assets/themes/natuurpunt/parkingwheels.svg", - "badge": true + "icon": { + "render": "circle:#FE6F32;./assets/themes/natuurpunt/parking.svg", + "mappings": [ + { + "if": "amenity=bicycle_parking", + "then": "circle:#FE6F32;./assets/themes/natuurpunt/parkingbike.svg" + } + ] + }, + "iconOverlays": [ + { + "if": "amenity=motorcycle_parking", + "then": "circle:#335D9F;./assets/themes/natuurpunt/parkingmotor.svg", + "badge": true + }, + { + "if": "capacity:disabled=yes", + "then": "circle:#335D9F;./assets/themes/natuurpunt/parkingwheels.svg", + "badge": true + } + ] } ] } @@ -233,9 +269,13 @@ "geoJsonZoomLevel": 12, "isOsmCache": true }, - "icon": { - "render": "circle:#FE6F32;./assets/themes/natuurpunt/information_board.svg" - } + "mapRendering": [ + { + "icon": { + "render": "circle:#FE6F32;./assets/themes/natuurpunt/information_board.svg" + } + } + ] } }, { @@ -247,9 +287,13 @@ "geoJsonZoomLevel": 12, "isOsmCache": true }, - "icon": { - "render": "circle:#FE6F32;./assets/themes/natuurpunt/bench.svg" - } + "mapRendering": [ + { + "icon": { + "render": "circle:#FE6F32;./assets/themes/natuurpunt/bench.svg" + } + } + ] } }, { @@ -261,9 +305,13 @@ "geoJsonZoomLevel": 12, "isOsmCache": true }, - "icon": { - "render": "circle:#FE6F32;./assets/themes/natuurpunt/watermill.svg" - } + "mapRendering": [ + { + "icon": { + "render": "circle:#FE6F32;./assets/themes/natuurpunt/watermill.svg" + } + } + ] } } ], diff --git a/assets/themes/uk_addresses/uk_addresses.json b/assets/themes/uk_addresses/uk_addresses.json index 71e5c173e..883ec7c53 100644 --- a/assets/themes/uk_addresses/uk_addresses.json +++ b/assets/themes/uk_addresses/uk_addresses.json @@ -251,12 +251,11 @@ } ] }, - "width": { - "render": "8" - }, "iconSize": { "render": "40,40,center" - }, + } + + }, { "color": { "render": "#00f", "mappings": [ @@ -275,6 +274,9 @@ "then": "#ff0" } ] + }, + "width": { + "render": "8" } } ] @@ -290,17 +292,7 @@ ] } }, - "mapRendering": [ - { - "location": "point", - "color": { - "render": "#ccc" - }, - "width": { - "render": "0" - } - } - ] + "mapRendering": [] } ], "enableShareScreen": false, From cb61f5332adb49f18171cc673d4a5779788da542 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 28 Oct 2021 03:21:17 +0200 Subject: [PATCH 40/95] Regenerate documentation --- Docs/CalculatedTags.md | 18 +- Docs/SpecialRenderings.md | 17 +- .../mapcomplete_charging_stations.json | 826 +++++------------- Docs/TagInfo/mapcomplete_climbing.json | 196 ++--- Docs/TagInfo/mapcomplete_cyclofix.json | 133 --- Docs/TagInfo/mapcomplete_drinking_water.json | 19 - Docs/TagInfo/mapcomplete_food.json | 10 + Docs/TagInfo/mapcomplete_fritures.json | 20 + Docs/TagInfo/mapcomplete_maps.json | 19 - Docs/TagInfo/mapcomplete_nature.json | 95 -- Docs/TagInfo/mapcomplete_waste_basket.json | 25 + Docs/URL_Parameters.md | 66 +- Logic/State/FeatureSwitchState.ts | 2 +- scripts/generateDocs.ts | 19 +- 14 files changed, 415 insertions(+), 1050 deletions(-) diff --git a/Docs/CalculatedTags.md b/Docs/CalculatedTags.md index b9bfe2104..5ca15250d 100644 --- a/Docs/CalculatedTags.md +++ b/Docs/CalculatedTags.md @@ -100,7 +100,7 @@ Adds the time that the data got loaded - pretty much the time of downloading fro -### _last_edit:contributor, _last_edit:contributor:uid, _last_edit:changeset, _last_edit:timestamp, _version_number +### _last_edit:contributor, _last_edit:contributor:uid, _last_edit:changeset, _last_edit:timestamp, _version_number, _backend @@ -109,6 +109,15 @@ Information about the last edit of this object. +### sidewalk:left, sidewalk:right, generic_key:left:property, generic_key:right:property + + + +Rewrites tags from 'generic_key:both:property' as 'generic_key:left:property' and 'generic_key:right:property' (and similar for sidewalk tagging). Note that this rewritten tags _will be reuploaded on a change_. To prevent to much unrelated retagging, this is only enabled if the layer has at least some lineRenderings with offset defined + + + + Calculating tags with Javascript ---------------------------------- @@ -162,6 +171,7 @@ Some advanced functions are available on **feat** as well: - closest - closestn - memberships + - get ### distanceTo @@ -202,4 +212,10 @@ If a 'unique tag key' is given, the tag with this key will only appear once (e.g For example: `_part_of_walking_routes=feat.memberships().map(r => r.relation.tags.name).join(';')` + +### get + + Gets the property of the feature, parses it (as JSON) and returns it. Might return 'undefined' if not defined, null, ... + + 0. key Generated from SimpleMetaTagger, ExtraFunction \ No newline at end of file diff --git a/Docs/SpecialRenderings.md b/Docs/SpecialRenderings.md index 7fff50797..452b907da 100644 --- a/Docs/SpecialRenderings.md +++ b/Docs/SpecialRenderings.md @@ -14,11 +14,11 @@ name | default | description ------ | --------- | ------------- -image key/prefix (multiple values allowed if comma-seperated) | image | The keys given to the images, e.g. if image is given, the first picture URL will be added as image, the second as image:0, the third as image:1, etc... +image key/prefix (multiple values allowed if comma-seperated) | image,mapillary,image,wikidata,wikimedia_commons,image,image | The keys given to the images, e.g. if image is given, the first picture URL will be added as image, the second as image:0, the third as image:1, etc... #### Example usage - `{image_carousel(image)}` + `{image_carousel(image,mapillary,image,wikidata,wikimedia_commons,image,image)}` ### image_upload Creates a button where a user can upload an image to IMGUR @@ -44,7 +44,7 @@ keyToShowWikipediaFor | wikidata | Use the wikidata entry from this key to show `{wikipedia()}` is a basic example, `{wikipedia(name:etymology:wikidata)}` to show the wikipedia page of whom the feature was named after. Also remember that these can be styled, e.g. `{wikipedia():max-height: 10rem}` to limit the height ### minimap - A small map showing the selected feature. Note that no styling is applied, wrap this in a div + A small map showing the selected feature. name | default | description ------ | --------- | ------------- @@ -54,6 +54,17 @@ idKey | id | (Matches all resting arguments) This argument should be the key of #### Example usage `{minimap()}`, `{minimap(17, id, _list_of_embedded_feature_ids_calculated_by_calculated_tag):height:10rem; border: 2px solid black}` +### sided_minimap + + A small map showing _only one side_ the selected feature. *This features requires to have linerenderings with offset* as only linerenderings with a postive or negative offset will be shown. Note: in most cases, this map will be automatically introduced + +name | default | description +------ | --------- | ------------- +side | undefined | The side to show, either `left` or `right` + +#### Example usage + + `{sided_minimap(left)}` ### reviews Adds an overview of the mangrove-reviews of this object. Mangrove.Reviews needs - in order to identify the reviewed object - a coordinate and a name. By default, the name of the object is given, but this can be overwritten diff --git a/Docs/TagInfo/mapcomplete_charging_stations.json b/Docs/TagInfo/mapcomplete_charging_stations.json index 6cd7b7772..c4fcc5c4d 100644 --- a/Docs/TagInfo/mapcomplete_charging_stations.json +++ b/Docs/TagInfo/mapcomplete_charging_stations.json @@ -92,7 +92,7 @@ }, { "key": "access", - "description": "Layer 'Charging stations' shows access=customers with a fixed text, namely 'Only customers of the place this station belongs to can use this charging station
E.g. a charging station operated by hotel which is only usable by their guests ' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows access=customers with a fixed text, namely 'Only customers of the place this station belongs to can use this charging station
E.g. a charging station operated by hotel which is only usable by their guests' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "customers" }, { @@ -252,632 +252,163 @@ "key": "socket:schuko", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:schuko' (in the MapComplete.osm.be theme 'Charging stations')" }, - { - "key": "socket:schuko:voltage", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:schuko:voltage' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:schuko:voltage", - "description": "Layer 'Charging stations' shows socket:socket:schuko:voltage=230 V with a fixed text, namely '
Schuko wall plug without ground pin (CEE7/4 type F)
outputs 230 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "230 V" - }, - { - "key": "socket:schuko:current", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:schuko:current' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:schuko:current", - "description": "Layer 'Charging stations' shows socket:socket:schuko:current=16 A with a fixed text, namely '
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 16 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "16 A" - }, - { - "key": "socket:schuko:output", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:schuko:output' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:schuko:output", - "description": "Layer 'Charging stations' shows socket:socket:schuko:output=3.6 kw with a fixed text, namely '
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 3.6 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "3.6 kw" - }, { "key": "socket:typee", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:typee' (in the MapComplete.osm.be theme 'Charging stations')" }, - { - "key": "socket:typee:voltage", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:typee:voltage' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:typee:voltage", - "description": "Layer 'Charging stations' shows socket:socket:typee:voltage=230 V with a fixed text, namely '
European wall plug with ground pin (CEE7/4 type E)
outputs 230 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "230 V" - }, - { - "key": "socket:typee:current", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:typee:current' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:typee:current", - "description": "Layer 'Charging stations' shows socket:socket:typee:current=16 A with a fixed text, namely '
European wall plug with ground pin (CEE7/4 type E)
outputs at most 16 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "16 A" - }, - { - "key": "socket:typee:output", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:typee:output' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:typee:output", - "description": "Layer 'Charging stations' shows socket:socket:typee:output=3 kw with a fixed text, namely '
European wall plug with ground pin (CEE7/4 type E)
outputs at most 3 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "3 kw" - }, - { - "key": "socket:socket:typee:output", - "description": "Layer 'Charging stations' shows socket:socket:typee:output=22 kw with a fixed text, namely '
European wall plug with ground pin (CEE7/4 type E)
outputs at most 22 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "22 kw" - }, { "key": "socket:chademo", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:chademo' (in the MapComplete.osm.be theme 'Charging stations')" }, - { - "key": "socket:chademo:voltage", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:chademo:voltage' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:chademo:voltage", - "description": "Layer 'Charging stations' shows socket:socket:chademo:voltage=500 V with a fixed text, namely '
Chademo
outputs 500 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "500 V" - }, - { - "key": "socket:chademo:current", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:chademo:current' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:chademo:current", - "description": "Layer 'Charging stations' shows socket:socket:chademo:current=120 A with a fixed text, namely '
Chademo
outputs at most 120 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "120 A" - }, - { - "key": "socket:chademo:output", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:chademo:output' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:chademo:output", - "description": "Layer 'Charging stations' shows socket:socket:chademo:output=50 kw with a fixed text, namely '
Chademo
outputs at most 50 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "50 kw" - }, { "key": "socket:type1_cable", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1_cable' (in the MapComplete.osm.be theme 'Charging stations')" }, - { - "key": "socket:type1_cable:voltage", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1_cable:voltage' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:type1_cable:voltage", - "description": "Layer 'Charging stations' shows socket:socket:type1_cable:voltage=200 V with a fixed text, namely '
Type 1 with cable (J1772)
outputs 200 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "200 V" - }, - { - "key": "socket:socket:type1_cable:voltage", - "description": "Layer 'Charging stations' shows socket:socket:type1_cable:voltage=240 V with a fixed text, namely '
Type 1 with cable (J1772)
outputs 240 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "240 V" - }, - { - "key": "socket:type1_cable:current", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1_cable:current' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:type1_cable:current", - "description": "Layer 'Charging stations' shows socket:socket:type1_cable:current=32 A with a fixed text, namely '
Type 1 with cable (J1772)
outputs at most 32 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "32 A" - }, - { - "key": "socket:type1_cable:output", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1_cable:output' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:type1_cable:output", - "description": "Layer 'Charging stations' shows socket:socket:type1_cable:output=3.7 kw with a fixed text, namely '
Type 1 with cable (J1772)
outputs at most 3.7 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "3.7 kw" - }, - { - "key": "socket:socket:type1_cable:output", - "description": "Layer 'Charging stations' shows socket:socket:type1_cable:output=7 kw with a fixed text, namely '
Type 1 with cable (J1772)
outputs at most 7 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "7 kw" - }, { "key": "socket:type1", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1' (in the MapComplete.osm.be theme 'Charging stations')" }, - { - "key": "socket:type1:voltage", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1:voltage' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:type1:voltage", - "description": "Layer 'Charging stations' shows socket:socket:type1:voltage=200 V with a fixed text, namely '
Type 1 without cable (J1772)
outputs 200 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "200 V" - }, - { - "key": "socket:socket:type1:voltage", - "description": "Layer 'Charging stations' shows socket:socket:type1:voltage=240 V with a fixed text, namely '
Type 1 without cable (J1772)
outputs 240 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "240 V" - }, - { - "key": "socket:type1:current", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1:current' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:type1:current", - "description": "Layer 'Charging stations' shows socket:socket:type1:current=32 A with a fixed text, namely '
Type 1 without cable (J1772)
outputs at most 32 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "32 A" - }, - { - "key": "socket:type1:output", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1:output' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:type1:output", - "description": "Layer 'Charging stations' shows socket:socket:type1:output=3.7 kw with a fixed text, namely '
Type 1 without cable (J1772)
outputs at most 3.7 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "3.7 kw" - }, - { - "key": "socket:socket:type1:output", - "description": "Layer 'Charging stations' shows socket:socket:type1:output=6.6 kw with a fixed text, namely '
Type 1 without cable (J1772)
outputs at most 6.6 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "6.6 kw" - }, - { - "key": "socket:socket:type1:output", - "description": "Layer 'Charging stations' shows socket:socket:type1:output=7 kw with a fixed text, namely '
Type 1 without cable (J1772)
outputs at most 7 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "7 kw" - }, - { - "key": "socket:socket:type1:output", - "description": "Layer 'Charging stations' shows socket:socket:type1:output=7.2 kw with a fixed text, namely '
Type 1 without cable (J1772)
outputs at most 7.2 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "7.2 kw" - }, { "key": "socket:type1_combo", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1_combo' (in the MapComplete.osm.be theme 'Charging stations')" }, - { - "key": "socket:type1_combo:voltage", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1_combo:voltage' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:type1_combo:voltage", - "description": "Layer 'Charging stations' shows socket:socket:type1_combo:voltage=400 V with a fixed text, namely '
Type 1 CCS (aka Type 1 Combo)
outputs 400 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "400 V" - }, - { - "key": "socket:socket:type1_combo:voltage", - "description": "Layer 'Charging stations' shows socket:socket:type1_combo:voltage=1000 V with a fixed text, namely '
Type 1 CCS (aka Type 1 Combo)
outputs 1000 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "1000 V" - }, - { - "key": "socket:type1_combo:current", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1_combo:current' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:type1_combo:current", - "description": "Layer 'Charging stations' shows socket:socket:type1_combo:current=50 A with a fixed text, namely '
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "50 A" - }, - { - "key": "socket:socket:type1_combo:current", - "description": "Layer 'Charging stations' shows socket:socket:type1_combo:current=125 A with a fixed text, namely '
Type 1 CCS (aka Type 1 Combo)
outputs at most 125 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "125 A" - }, - { - "key": "socket:type1_combo:output", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1_combo:output' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:type1_combo:output", - "description": "Layer 'Charging stations' shows socket:socket:type1_combo:output=50 kw with a fixed text, namely '
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "50 kw" - }, - { - "key": "socket:socket:type1_combo:output", - "description": "Layer 'Charging stations' shows socket:socket:type1_combo:output=62.5 kw with a fixed text, namely '
Type 1 CCS (aka Type 1 Combo)
outputs at most 62.5 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "62.5 kw" - }, - { - "key": "socket:socket:type1_combo:output", - "description": "Layer 'Charging stations' shows socket:socket:type1_combo:output=150 kw with a fixed text, namely '
Type 1 CCS (aka Type 1 Combo)
outputs at most 150 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "150 kw" - }, - { - "key": "socket:socket:type1_combo:output", - "description": "Layer 'Charging stations' shows socket:socket:type1_combo:output=350 kw with a fixed text, namely '
Type 1 CCS (aka Type 1 Combo)
outputs at most 350 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "350 kw" - }, { "key": "socket:tesla_supercharger", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:tesla_supercharger' (in the MapComplete.osm.be theme 'Charging stations')" }, - { - "key": "socket:tesla_supercharger:voltage", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:tesla_supercharger:voltage' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:tesla_supercharger:voltage", - "description": "Layer 'Charging stations' shows socket:socket:tesla_supercharger:voltage=480 V with a fixed text, namely '
Tesla Supercharger
outputs 480 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "480 V" - }, - { - "key": "socket:tesla_supercharger:current", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:tesla_supercharger:current' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:tesla_supercharger:current", - "description": "Layer 'Charging stations' shows socket:socket:tesla_supercharger:current=125 A with a fixed text, namely '
Tesla Supercharger
outputs at most 125 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "125 A" - }, - { - "key": "socket:socket:tesla_supercharger:current", - "description": "Layer 'Charging stations' shows socket:socket:tesla_supercharger:current=350 A with a fixed text, namely '
Tesla Supercharger
outputs at most 350 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "350 A" - }, - { - "key": "socket:tesla_supercharger:output", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:tesla_supercharger:output' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:tesla_supercharger:output", - "description": "Layer 'Charging stations' shows socket:socket:tesla_supercharger:output=120 kw with a fixed text, namely '
Tesla Supercharger
outputs at most 120 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "120 kw" - }, - { - "key": "socket:socket:tesla_supercharger:output", - "description": "Layer 'Charging stations' shows socket:socket:tesla_supercharger:output=150 kw with a fixed text, namely '
Tesla Supercharger
outputs at most 150 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "150 kw" - }, - { - "key": "socket:socket:tesla_supercharger:output", - "description": "Layer 'Charging stations' shows socket:socket:tesla_supercharger:output=250 kw with a fixed text, namely '
Tesla Supercharger
outputs at most 250 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "250 kw" - }, { "key": "socket:type2", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2' (in the MapComplete.osm.be theme 'Charging stations')" }, - { - "key": "socket:type2:voltage", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2:voltage' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:type2:voltage", - "description": "Layer 'Charging stations' shows socket:socket:type2:voltage=230 V with a fixed text, namely '
Type 2 (mennekes)
outputs 230 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "230 V" - }, - { - "key": "socket:socket:type2:voltage", - "description": "Layer 'Charging stations' shows socket:socket:type2:voltage=400 V with a fixed text, namely '
Type 2 (mennekes)
outputs 400 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "400 V" - }, - { - "key": "socket:type2:current", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2:current' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:type2:current", - "description": "Layer 'Charging stations' shows socket:socket:type2:current=16 A with a fixed text, namely '
Type 2 (mennekes)
outputs at most 16 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "16 A" - }, - { - "key": "socket:socket:type2:current", - "description": "Layer 'Charging stations' shows socket:socket:type2:current=32 A with a fixed text, namely '
Type 2 (mennekes)
outputs at most 32 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "32 A" - }, - { - "key": "socket:type2:output", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2:output' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:type2:output", - "description": "Layer 'Charging stations' shows socket:socket:type2:output=11 kw with a fixed text, namely '
Type 2 (mennekes)
outputs at most 11 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "11 kw" - }, - { - "key": "socket:socket:type2:output", - "description": "Layer 'Charging stations' shows socket:socket:type2:output=22 kw with a fixed text, namely '
Type 2 (mennekes)
outputs at most 22 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "22 kw" - }, { "key": "socket:type2_combo", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2_combo' (in the MapComplete.osm.be theme 'Charging stations')" }, - { - "key": "socket:type2_combo:voltage", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2_combo:voltage' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:type2_combo:voltage", - "description": "Layer 'Charging stations' shows socket:socket:type2_combo:voltage=500 V with a fixed text, namely '
Type 2 CCS (mennekes)
outputs 500 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "500 V" - }, - { - "key": "socket:socket:type2_combo:voltage", - "description": "Layer 'Charging stations' shows socket:socket:type2_combo:voltage=920 V with a fixed text, namely '
Type 2 CCS (mennekes)
outputs 920 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "920 V" - }, - { - "key": "socket:type2_combo:current", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2_combo:current' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:type2_combo:current", - "description": "Layer 'Charging stations' shows socket:socket:type2_combo:current=125 A with a fixed text, namely '
Type 2 CCS (mennekes)
outputs at most 125 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "125 A" - }, - { - "key": "socket:socket:type2_combo:current", - "description": "Layer 'Charging stations' shows socket:socket:type2_combo:current=350 A with a fixed text, namely '
Type 2 CCS (mennekes)
outputs at most 350 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "350 A" - }, - { - "key": "socket:type2_combo:output", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2_combo:output' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:type2_combo:output", - "description": "Layer 'Charging stations' shows socket:socket:type2_combo:output=50 kw with a fixed text, namely '
Type 2 CCS (mennekes)
outputs at most 50 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "50 kw" - }, { "key": "socket:type2_cable", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2_cable' (in the MapComplete.osm.be theme 'Charging stations')" }, - { - "key": "socket:type2_cable:voltage", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2_cable:voltage' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:type2_cable:voltage", - "description": "Layer 'Charging stations' shows socket:socket:type2_cable:voltage=230 V with a fixed text, namely '
Type 2 with cable (mennekes)
outputs 230 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "230 V" - }, - { - "key": "socket:socket:type2_cable:voltage", - "description": "Layer 'Charging stations' shows socket:socket:type2_cable:voltage=400 V with a fixed text, namely '
Type 2 with cable (mennekes)
outputs 400 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "400 V" - }, - { - "key": "socket:type2_cable:current", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2_cable:current' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:type2_cable:current", - "description": "Layer 'Charging stations' shows socket:socket:type2_cable:current=16 A with a fixed text, namely '
Type 2 with cable (mennekes)
outputs at most 16 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "16 A" - }, - { - "key": "socket:socket:type2_cable:current", - "description": "Layer 'Charging stations' shows socket:socket:type2_cable:current=32 A with a fixed text, namely '
Type 2 with cable (mennekes)
outputs at most 32 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "32 A" - }, - { - "key": "socket:type2_cable:output", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2_cable:output' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:type2_cable:output", - "description": "Layer 'Charging stations' shows socket:socket:type2_cable:output=11 kw with a fixed text, namely '
Type 2 with cable (mennekes)
outputs at most 11 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "11 kw" - }, - { - "key": "socket:socket:type2_cable:output", - "description": "Layer 'Charging stations' shows socket:socket:type2_cable:output=22 kw with a fixed text, namely '
Type 2 with cable (mennekes)
outputs at most 22 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "22 kw" - }, { "key": "socket:tesla_supercharger_ccs", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:tesla_supercharger_ccs' (in the MapComplete.osm.be theme 'Charging stations')" }, - { - "key": "socket:tesla_supercharger_ccs:voltage", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:tesla_supercharger_ccs:voltage' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:tesla_supercharger_ccs:voltage", - "description": "Layer 'Charging stations' shows socket:socket:tesla_supercharger_ccs:voltage=500 V with a fixed text, namely '
Tesla Supercharger CCS (a branded type2_css)
outputs 500 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "500 V" - }, - { - "key": "socket:socket:tesla_supercharger_ccs:voltage", - "description": "Layer 'Charging stations' shows socket:socket:tesla_supercharger_ccs:voltage=920 V with a fixed text, namely '
Tesla Supercharger CCS (a branded type2_css)
outputs 920 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "920 V" - }, - { - "key": "socket:tesla_supercharger_ccs:current", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:tesla_supercharger_ccs:current' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:tesla_supercharger_ccs:current", - "description": "Layer 'Charging stations' shows socket:socket:tesla_supercharger_ccs:current=125 A with a fixed text, namely '
Tesla Supercharger CCS (a branded type2_css)
outputs at most 125 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "125 A" - }, - { - "key": "socket:socket:tesla_supercharger_ccs:current", - "description": "Layer 'Charging stations' shows socket:socket:tesla_supercharger_ccs:current=350 A with a fixed text, namely '
Tesla Supercharger CCS (a branded type2_css)
outputs at most 350 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "350 A" - }, - { - "key": "socket:tesla_supercharger_ccs:output", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:tesla_supercharger_ccs:output' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:tesla_supercharger_ccs:output", - "description": "Layer 'Charging stations' shows socket:socket:tesla_supercharger_ccs:output=50 kw with a fixed text, namely '
Tesla Supercharger CCS (a branded type2_css)
outputs at most 50 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "50 kw" - }, { "key": "socket:tesla_destination", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:tesla_destination' (in the MapComplete.osm.be theme 'Charging stations')" }, - { - "key": "socket:tesla_destination:voltage", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:tesla_destination:voltage' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:tesla_destination:voltage", - "description": "Layer 'Charging stations' shows socket:socket:tesla_destination:voltage=480 V with a fixed text, namely '
Tesla Supercharger (destination)
outputs 480 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "480 V" - }, - { - "key": "socket:tesla_destination:current", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:tesla_destination:current' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:tesla_destination:current", - "description": "Layer 'Charging stations' shows socket:socket:tesla_destination:current=125 A with a fixed text, namely '
Tesla Supercharger (destination)
outputs at most 125 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "125 A" - }, - { - "key": "socket:socket:tesla_destination:current", - "description": "Layer 'Charging stations' shows socket:socket:tesla_destination:current=350 A with a fixed text, namely '
Tesla Supercharger (destination)
outputs at most 350 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "350 A" - }, - { - "key": "socket:tesla_destination:output", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:tesla_destination:output' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:tesla_destination:output", - "description": "Layer 'Charging stations' shows socket:socket:tesla_destination:output=120 kw with a fixed text, namely '
Tesla Supercharger (destination)
outputs at most 120 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "120 kw" - }, - { - "key": "socket:socket:tesla_destination:output", - "description": "Layer 'Charging stations' shows socket:socket:tesla_destination:output=150 kw with a fixed text, namely '
Tesla Supercharger (destination)
outputs at most 150 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "150 kw" - }, - { - "key": "socket:socket:tesla_destination:output", - "description": "Layer 'Charging stations' shows socket:socket:tesla_destination:output=250 kw with a fixed text, namely '
Tesla Supercharger (destination)
outputs at most 250 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "250 kw" - }, { "key": "socket:tesla_destination", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:tesla_destination' (in the MapComplete.osm.be theme 'Charging stations')" }, - { - "key": "socket:tesla_destination:voltage", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:tesla_destination:voltage' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:tesla_destination:voltage", - "description": "Layer 'Charging stations' shows socket:socket:tesla_destination:voltage=230 V with a fixed text, namely '
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 230 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "230 V" - }, - { - "key": "socket:socket:tesla_destination:voltage", - "description": "Layer 'Charging stations' shows socket:socket:tesla_destination:voltage=400 V with a fixed text, namely '
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 400 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "400 V" - }, - { - "key": "socket:tesla_destination:current", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:tesla_destination:current' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:tesla_destination:current", - "description": "Layer 'Charging stations' shows socket:socket:tesla_destination:current=16 A with a fixed text, namely '
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 16 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "16 A" - }, - { - "key": "socket:socket:tesla_destination:current", - "description": "Layer 'Charging stations' shows socket:socket:tesla_destination:current=32 A with a fixed text, namely '
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 32 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "32 A" - }, - { - "key": "socket:tesla_destination:output", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:tesla_destination:output' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:tesla_destination:output", - "description": "Layer 'Charging stations' shows socket:socket:tesla_destination:output=11 kw with a fixed text, namely '
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 11 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "11 kw" - }, - { - "key": "socket:socket:tesla_destination:output", - "description": "Layer 'Charging stations' shows socket:socket:tesla_destination:output=22 kw with a fixed text, namely '
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 22 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "22 kw" - }, { "key": "socket:USB-A", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:USB-A' (in the MapComplete.osm.be theme 'Charging stations')" }, - { - "key": "socket:USB-A:voltage", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:USB-A:voltage' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:USB-A:voltage", - "description": "Layer 'Charging stations' shows socket:socket:USB-A:voltage=5 V with a fixed text, namely '
USB to charge phones and small electronics
outputs 5 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "5 V" - }, - { - "key": "socket:USB-A:current", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:USB-A:current' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:USB-A:current", - "description": "Layer 'Charging stations' shows socket:socket:USB-A:current=1 A with a fixed text, namely '
USB to charge phones and small electronics
outputs at most 1 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "1 A" - }, - { - "key": "socket:socket:USB-A:current", - "description": "Layer 'Charging stations' shows socket:socket:USB-A:current=2 A with a fixed text, namely '
USB to charge phones and small electronics
outputs at most 2 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "2 A" - }, - { - "key": "socket:USB-A:output", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:USB-A:output' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:socket:USB-A:output", - "description": "Layer 'Charging stations' shows socket:socket:USB-A:output=5w with a fixed text, namely '
USB to charge phones and small electronics
outputs at most 5w' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "5w" - }, - { - "key": "socket:socket:USB-A:output", - "description": "Layer 'Charging stations' shows socket:socket:USB-A:output=10w with a fixed text, namely '
USB to charge phones and small electronics
outputs at most 10w' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "10w" - }, { "key": "socket:bosch_3pin", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:bosch_3pin' (in the MapComplete.osm.be theme 'Charging stations')" }, - { - "key": "socket:bosch_3pin:voltage", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:bosch_3pin:voltage' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:bosch_3pin:current", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:bosch_3pin:current' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "socket:bosch_3pin:output", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:bosch_3pin:output' (in the MapComplete.osm.be theme 'Charging stations')" - }, { "key": "socket:bosch_5pin", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:bosch_5pin' (in the MapComplete.osm.be theme 'Charging stations')" }, { - "key": "socket:bosch_5pin:voltage", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:bosch_5pin:voltage' (in the MapComplete.osm.be theme 'Charging stations')" + "key": "opening_hours", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'opening_hours' (in the MapComplete.osm.be theme 'Charging stations')" }, { - "key": "socket:bosch_5pin:current", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:bosch_5pin:current' (in the MapComplete.osm.be theme 'Charging stations')" + "key": "opening_hours", + "description": "Layer 'Charging stations' shows opening_hours=24/7 with a fixed text, namely '24/7 opened (including holidays)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "24/7" }, { - "key": "socket:bosch_5pin:output", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:bosch_5pin:output' (in the MapComplete.osm.be theme 'Charging stations')" + "key": "fee", + "description": "Layer 'Charging stations' shows fee=no with a fixed text, namely 'Free to use' (in the MapComplete.osm.be theme 'Charging stations')", + "value": "no" + }, + { + "key": "fee", + "description": "Layer 'Charging stations' shows fee=no&fee:conditional=&charge=&authentication:none=yes with a fixed text, namely 'Free to use (without authenticating)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "no" + }, + { + "key": "fee:conditional", + "description": "Layer 'Charging stations' shows fee=no&fee:conditional=&charge=&authentication:none=yes with a fixed text, namely 'Free to use (without authenticating)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key fee:conditional.", + "value": "" + }, + { + "key": "charge", + "description": "Layer 'Charging stations' shows fee=no&fee:conditional=&charge=&authentication:none=yes with a fixed text, namely 'Free to use (without authenticating)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key charge.", + "value": "" + }, + { + "key": "authentication:none", + "description": "Layer 'Charging stations' shows fee=no&fee:conditional=&charge=&authentication:none=yes with a fixed text, namely 'Free to use (without authenticating)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "yes" + }, + { + "key": "fee", + "description": "Layer 'Charging stations' shows fee=no&fee:conditional=&charge=&authentication:none=no with a fixed text, namely 'Free to use, but one has to authenticate' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "no" + }, + { + "key": "fee:conditional", + "description": "Layer 'Charging stations' shows fee=no&fee:conditional=&charge=&authentication:none=no with a fixed text, namely 'Free to use, but one has to authenticate' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key fee:conditional.", + "value": "" + }, + { + "key": "charge", + "description": "Layer 'Charging stations' shows fee=no&fee:conditional=&charge=&authentication:none=no with a fixed text, namely 'Free to use, but one has to authenticate' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key charge.", + "value": "" + }, + { + "key": "authentication:none", + "description": "Layer 'Charging stations' shows fee=no&fee:conditional=&charge=&authentication:none=no with a fixed text, namely 'Free to use, but one has to authenticate' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "no" + }, + { + "key": "fee", + "description": "Layer 'Charging stations' shows fee=yes&fee:conditional=no @ customers with a fixed text, namely 'Paid use, but free for customers of the hotel/pub/hospital/... who operates the charging station' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "yes" + }, + { + "key": "fee:conditional", + "description": "Layer 'Charging stations' shows fee=yes&fee:conditional=no @ customers with a fixed text, namely 'Paid use, but free for customers of the hotel/pub/hospital/... who operates the charging station' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "no @ customers" + }, + { + "key": "fee", + "description": "Layer 'Charging stations' shows fee=yes&fee:conditional= with a fixed text, namely 'Paid use' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "yes" + }, + { + "key": "fee:conditional", + "description": "Layer 'Charging stations' shows fee=yes&fee:conditional= with a fixed text, namely 'Paid use' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key fee:conditional.", + "value": "" + }, + { + "key": "charge", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'charge' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "payment:cash", + "description": "Layer 'Charging stations' shows payment:cash=yes with a fixed text, namely 'Cash is accepted here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "yes" + }, + { + "key": "payment:cards", + "description": "Layer 'Charging stations' shows payment:cards=yes with a fixed text, namely 'Payment cards are accepted here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "yes" + }, + { + "key": "payment:app", + "description": "Layer 'Charging stations' shows payment:app=yes with a fixed text, namely 'Payment is done using a dedicated app' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "yes" + }, + { + "key": "payment:membership_card", + "description": "Layer 'Charging stations' shows payment:membership_card=yes with a fixed text, namely 'Payment is done using a membership card' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "yes" }, { "key": "authentication:membership_card", @@ -916,56 +447,13 @@ }, { "key": "authentication:none", - "description": "Layer 'Charging stations' shows authentication:none=yes with a fixed text, namely 'No authentication is needed' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows authentication:none=yes with a fixed text, namely 'Charging here is (also) possible without authentication' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "yes" }, { "key": "authentication:phone_call:number", "description": "Layer 'Charging stations' shows and asks freeform values for key 'authentication:phone_call:number' (in the MapComplete.osm.be theme 'Charging stations')" }, - { - "key": "opening_hours", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'opening_hours' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "opening_hours", - "description": "Layer 'Charging stations' shows opening_hours=24/7 with a fixed text, namely '24/7 opened (including holidays)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "24/7" - }, - { - "key": "charge", - "description": "Layer 'Charging stations' shows and asks freeform values for key 'charge' (in the MapComplete.osm.be theme 'Charging stations')" - }, - { - "key": "fee", - "description": "Layer 'Charging stations' shows fee=no&charge= with a fixed text, namely 'Free to use' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "no" - }, - { - "key": "charge", - "description": "Layer 'Charging stations' shows fee=no&charge= with a fixed text, namely 'Free to use' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key charge.", - "value": "" - }, - { - "key": "payment:cash", - "description": "Layer 'Charging stations' shows payment:cash=yes with a fixed text, namely 'Cash is accepted here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "yes" - }, - { - "key": "payment:cards", - "description": "Layer 'Charging stations' shows payment:cards=yes with a fixed text, namely 'Payment cards are accepted here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "yes" - }, - { - "key": "payment:app", - "description": "Layer 'Charging stations' shows payment:app=yes with a fixed text, namely 'Payment is done using a dedicated app' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "yes" - }, - { - "key": "payment:membership_card", - "description": "Layer 'Charging stations' shows payment:membership_card=yes with a fixed text, namely 'Payment is done using a membership card' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "yes" - }, { "key": "maxstay", "description": "Layer 'Charging stations' shows and asks freeform values for key 'maxstay' (in the MapComplete.osm.be theme 'Charging stations')" @@ -1053,49 +541,129 @@ "key": "ref", "description": "Layer 'Charging stations' shows and asks freeform values for key 'ref' (in the MapComplete.osm.be theme 'Charging stations')" }, - { - "key": "operational_status", - "description": "Layer 'Charging stations' shows operational_status=broken with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "broken" - }, { "key": "planned:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=charging_station&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "charging_station" - }, - { - "key": "amenity", - "description": "Layer 'Charging stations' shows planned:amenity=charging_station&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.", "value": "" }, { "key": "construction:amenity", - "description": "Layer 'Charging stations' shows construction:amenity=charging_station&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "charging_station" - }, - { - "key": "amenity", - "description": "Layer 'Charging stations' shows construction:amenity=charging_station&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.", "value": "" }, { "key": "disused:amenity", - "description": "Layer 'Charging stations' shows disused:amenity=charging_station&amenity= with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "charging_station" + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.", + "value": "" }, { - "key": "amenity", - "description": "Layer 'Charging stations' shows disused:amenity=charging_station&amenity= with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key amenity.", + "key": "operational_status", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.", "value": "" }, { "key": "amenity", - "description": "Layer 'Charging stations' shows amenity=charging_station&operational_status= with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "charging_station" + }, + { + "key": "planned:amenity", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.", + "value": "" + }, + { + "key": "construction:amenity", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.", + "value": "" + }, + { + "key": "disused:amenity", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.", + "value": "" + }, + { + "key": "operational_status", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "broken" + }, + { + "key": "amenity", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "charging_station" + }, + { + "key": "planned:amenity", + "description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "charging_station" + }, + { + "key": "construction:amenity", + "description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.", + "value": "" + }, + { + "key": "disused:amenity", + "description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.", + "value": "" + }, + { + "key": "operational_status", + "description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.", + "value": "" + }, + { + "key": "amenity", + "description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key amenity.", + "value": "" + }, + { + "key": "planned:amenity", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.", + "value": "" + }, + { + "key": "construction:amenity", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "charging_station" + }, + { + "key": "disused:amenity", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.", + "value": "" + }, + { + "key": "operational_status", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.", + "value": "" + }, + { + "key": "amenity", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key amenity.", + "value": "" + }, + { + "key": "planned:amenity", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.", + "value": "" + }, + { + "key": "construction:amenity", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.", + "value": "" + }, + { + "key": "disused:amenity", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "charging_station" }, { "key": "operational_status", - "description": "Layer 'Charging stations' shows amenity=charging_station&operational_status= with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.", + "value": "" + }, + { + "key": "amenity", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key amenity.", "value": "" }, { diff --git a/Docs/TagInfo/mapcomplete_climbing.json b/Docs/TagInfo/mapcomplete_climbing.json index d0505237c..75142f1ce 100644 --- a/Docs/TagInfo/mapcomplete_climbing.json +++ b/Docs/TagInfo/mapcomplete_climbing.json @@ -194,6 +194,26 @@ "key": "wikipedia", "description": "The layer 'Climbing gyms allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary" }, + { + "key": "name", + "description": "Layer 'Climbing gyms' shows and asks freeform values for key 'name' (in the MapComplete.osm.be theme 'Open Climbing Map')" + }, + { + "key": "website", + "description": "Layer 'Climbing gyms' shows and asks freeform values for key 'website' (in the MapComplete.osm.be theme 'Open Climbing Map')" + }, + { + "key": "phone", + "description": "Layer 'Climbing gyms' shows and asks freeform values for key 'phone' (in the MapComplete.osm.be theme 'Open Climbing Map')" + }, + { + "key": "email", + "description": "Layer 'Climbing gyms' shows and asks freeform values for key 'email' (in the MapComplete.osm.be theme 'Open Climbing Map')" + }, + { + "key": "opening_hours", + "description": "Layer 'Climbing gyms' shows and asks freeform values for key 'opening_hours' (in the MapComplete.osm.be theme 'Open Climbing Map')" + }, { "key": "url", "description": "Layer 'Climbing gyms' shows and asks freeform values for key 'url' (in the MapComplete.osm.be theme 'Open Climbing Map')" @@ -314,26 +334,6 @@ "key": "climbing:speed", "description": "Layer 'Climbing gyms' shows climbing:speed~^..*$ with a fixed text, namely 'There are {climbing:speed} speed climbing walls' (in the MapComplete.osm.be theme 'Open Climbing Map')" }, - { - "key": "name", - "description": "Layer 'Climbing gyms' shows and asks freeform values for key 'name' (in the MapComplete.osm.be theme 'Open Climbing Map')" - }, - { - "key": "website", - "description": "Layer 'Climbing gyms' shows and asks freeform values for key 'website' (in the MapComplete.osm.be theme 'Open Climbing Map')" - }, - { - "key": "phone", - "description": "Layer 'Climbing gyms' shows and asks freeform values for key 'phone' (in the MapComplete.osm.be theme 'Open Climbing Map')" - }, - { - "key": "email", - "description": "Layer 'Climbing gyms' shows and asks freeform values for key 'email' (in the MapComplete.osm.be theme 'Open Climbing Map')" - }, - { - "key": "opening_hours", - "description": "Layer 'Climbing gyms' shows and asks freeform values for key 'opening_hours' (in the MapComplete.osm.be theme 'Open Climbing Map')" - }, { "key": "climbing", "description": "The MapComplete theme Open Climbing Map has a layer Climbing routes showing features with this tag", @@ -355,6 +355,46 @@ "key": "wikipedia", "description": "The layer 'Climbing routes allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary" }, + { + "key": "name", + "description": "Layer 'Climbing routes' shows and asks freeform values for key 'name' (in the MapComplete.osm.be theme 'Open Climbing Map')" + }, + { + "key": "noname", + "description": "Layer 'Climbing routes' shows noname=yes&name= with a fixed text, namely 'This climbing route doesn't have a name' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Climbing Map')", + "value": "yes" + }, + { + "key": "name", + "description": "Layer 'Climbing routes' shows noname=yes&name= with a fixed text, namely 'This climbing route doesn't have a name' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Climbing Map') Picking this answer will delete the key name.", + "value": "" + }, + { + "key": "climbing:length", + "description": "Layer 'Climbing routes' shows and asks freeform values for key 'climbing:length' (in the MapComplete.osm.be theme 'Open Climbing Map')" + }, + { + "key": "climbing:grade:french", + "description": "Layer 'Climbing routes' shows and asks freeform values for key 'climbing:grade:french' (in the MapComplete.osm.be theme 'Open Climbing Map')" + }, + { + "key": "climbing:bolts", + "description": "Layer 'Climbing routes' shows and asks freeform values for key 'climbing:bolts' (in the MapComplete.osm.be theme 'Open Climbing Map')" + }, + { + "key": "climbing:bolted", + "description": "Layer 'Climbing routes' shows climbing:bolted=no with a fixed text, namely 'This route is not bolted' (in the MapComplete.osm.be theme 'Open Climbing Map')", + "value": "no" + }, + { + "key": "climbing:bolted", + "description": "Layer 'Climbing routes' shows climbing:bolted=no&climbing:bolts= with a fixed text, namely 'This route is not bolted' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Climbing Map')", + "value": "no&climbing:bolts=" + }, + { + "key": "description", + "description": "Layer 'Climbing routes' shows and asks freeform values for key 'description' (in the MapComplete.osm.be theme 'Open Climbing Map')" + }, { "key": "url", "description": "Layer 'Climbing routes' shows and asks freeform values for key 'url' (in the MapComplete.osm.be theme 'Open Climbing Map')" @@ -475,46 +515,6 @@ "key": "climbing:speed", "description": "Layer 'Climbing routes' shows climbing:speed~^..*$ with a fixed text, namely 'There are {climbing:speed} speed climbing walls' (in the MapComplete.osm.be theme 'Open Climbing Map')" }, - { - "key": "name", - "description": "Layer 'Climbing routes' shows and asks freeform values for key 'name' (in the MapComplete.osm.be theme 'Open Climbing Map')" - }, - { - "key": "noname", - "description": "Layer 'Climbing routes' shows noname=yes&name= with a fixed text, namely 'This climbing route doesn't have a name' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Climbing Map')", - "value": "yes" - }, - { - "key": "name", - "description": "Layer 'Climbing routes' shows noname=yes&name= with a fixed text, namely 'This climbing route doesn't have a name' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Climbing Map') Picking this answer will delete the key name.", - "value": "" - }, - { - "key": "climbing:length", - "description": "Layer 'Climbing routes' shows and asks freeform values for key 'climbing:length' (in the MapComplete.osm.be theme 'Open Climbing Map')" - }, - { - "key": "climbing:grade:french", - "description": "Layer 'Climbing routes' shows and asks freeform values for key 'climbing:grade:french' (in the MapComplete.osm.be theme 'Open Climbing Map')" - }, - { - "key": "climbing:bolts", - "description": "Layer 'Climbing routes' shows and asks freeform values for key 'climbing:bolts' (in the MapComplete.osm.be theme 'Open Climbing Map')" - }, - { - "key": "climbing:bolted", - "description": "Layer 'Climbing routes' shows climbing:bolted=no with a fixed text, namely 'This route is not bolted' (in the MapComplete.osm.be theme 'Open Climbing Map')", - "value": "no" - }, - { - "key": "climbing:bolted", - "description": "Layer 'Climbing routes' shows climbing:bolted=no&climbing:bolts= with a fixed text, namely 'This route is not bolted' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Climbing Map')", - "value": "no&climbing:bolts=" - }, - { - "key": "description", - "description": "Layer 'Climbing routes' shows and asks freeform values for key 'description' (in the MapComplete.osm.be theme 'Open Climbing Map')" - }, { "key": "sport", "description": "The MapComplete theme Open Climbing Map has a layer Climbing opportunities showing features with this tag", @@ -536,6 +536,44 @@ "key": "wikipedia", "description": "The layer 'Climbing opportunities allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary" }, + { + "key": "name", + "description": "Layer 'Climbing opportunities' shows and asks freeform values for key 'name' (in the MapComplete.osm.be theme 'Open Climbing Map')" + }, + { + "key": "noname", + "description": "Layer 'Climbing opportunities' shows noname=yes&name= with a fixed text, namely 'This climbing opportunity doesn't have a name' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Climbing Map')", + "value": "yes" + }, + { + "key": "name", + "description": "Layer 'Climbing opportunities' shows noname=yes&name= with a fixed text, namely 'This climbing opportunity doesn't have a name' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Climbing Map') Picking this answer will delete the key name.", + "value": "" + }, + { + "key": "climbing", + "description": "Layer 'Climbing opportunities' shows climbing=boulder with a fixed text, namely 'A climbing boulder - a single rock or cliff with one or a few climbing routes which can be climbed safely without rope' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Climbing Map')", + "value": "boulder" + }, + { + "key": "climbing", + "description": "Layer 'Climbing opportunities' shows climbing=crag with a fixed text, namely 'A climbing crag - a single rock or cliff with at least a few climbing routes' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Climbing Map')", + "value": "crag" + }, + { + "key": "climbing", + "description": "Layer 'Climbing opportunities' shows climbing=area with a fixed text, namely 'A climbing area with one or more climbing crags and/or boulders' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Climbing Map')", + "value": "area" + }, + { + "key": "rock", + "description": "Layer 'Climbing opportunities' shows and asks freeform values for key 'rock' (in the MapComplete.osm.be theme 'Open Climbing Map')" + }, + { + "key": "rock", + "description": "Layer 'Climbing opportunities' shows rock=limestone with a fixed text, namely 'Limestone' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Climbing Map')", + "value": "limestone" + }, { "key": "url", "description": "Layer 'Climbing opportunities' shows and asks freeform values for key 'url' (in the MapComplete.osm.be theme 'Open Climbing Map')" @@ -656,44 +694,6 @@ "key": "climbing:speed", "description": "Layer 'Climbing opportunities' shows climbing:speed~^..*$ with a fixed text, namely 'There are {climbing:speed} speed climbing walls' (in the MapComplete.osm.be theme 'Open Climbing Map')" }, - { - "key": "name", - "description": "Layer 'Climbing opportunities' shows and asks freeform values for key 'name' (in the MapComplete.osm.be theme 'Open Climbing Map')" - }, - { - "key": "noname", - "description": "Layer 'Climbing opportunities' shows noname=yes&name= with a fixed text, namely 'This climbing opportunity doesn't have a name' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Climbing Map')", - "value": "yes" - }, - { - "key": "name", - "description": "Layer 'Climbing opportunities' shows noname=yes&name= with a fixed text, namely 'This climbing opportunity doesn't have a name' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Climbing Map') Picking this answer will delete the key name.", - "value": "" - }, - { - "key": "climbing", - "description": "Layer 'Climbing opportunities' shows climbing=boulder with a fixed text, namely 'A climbing boulder - a single rock or cliff with one or a few climbing routes which can be climbed safely without rope' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Climbing Map')", - "value": "boulder" - }, - { - "key": "climbing", - "description": "Layer 'Climbing opportunities' shows climbing=crag with a fixed text, namely 'A climbing crag - a single rock or cliff with at least a few climbing routes' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Climbing Map')", - "value": "crag" - }, - { - "key": "climbing", - "description": "Layer 'Climbing opportunities' shows climbing=area with a fixed text, namely 'A climbing area with one or more climbing crags and/or boulders' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Climbing Map')", - "value": "area" - }, - { - "key": "rock", - "description": "Layer 'Climbing opportunities' shows and asks freeform values for key 'rock' (in the MapComplete.osm.be theme 'Open Climbing Map')" - }, - { - "key": "rock", - "description": "Layer 'Climbing opportunities' shows rock=limestone with a fixed text, namely 'Limestone' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Climbing Map')", - "value": "limestone" - }, { "key": "leisure", "description": "The MapComplete theme Open Climbing Map has a layer Climbing opportunities? showing features with this tag", diff --git a/Docs/TagInfo/mapcomplete_cyclofix.json b/Docs/TagInfo/mapcomplete_cyclofix.json index 9138e75b4..f15af7e5c 100644 --- a/Docs/TagInfo/mapcomplete_cyclofix.json +++ b/Docs/TagInfo/mapcomplete_cyclofix.json @@ -116,25 +116,6 @@ "key": "opening_hours", "description": "Layer 'Bike cafe' shows and asks freeform values for key 'opening_hours' (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')" }, - { - "key": "service:bicycle:cleaning:charge", - "description": "Layer 'Bike cafe' shows and asks freeform values for key 'service:bicycle:cleaning:charge' (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Bike cafe' shows service:bicycle:cleaning:fee=no&service:bicycle:cleaning:charge= with a fixed text, namely 'The cleaning service is free to use' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')", - "value": "no&service:bicycle:cleaning:charge=" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Bike cafe' shows service:bicycle:cleaning:fee=no& with a fixed text, namely 'Free to use' (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')", - "value": "no&" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Bike cafe' shows service:bicycle:cleaning:fee=yes with a fixed text, namely 'The cleaning service has a fee' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')", - "value": "yes" - }, { "key": "shop", "description": "The MapComplete theme Cyclofix - an open map for cyclists has a layer Bike repair/shop showing features with this tag", @@ -417,25 +398,6 @@ "key": "description", "description": "Layer 'Bicycle library' shows and asks freeform values for key 'description' (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')" }, - { - "key": "service:bicycle:cleaning:charge", - "description": "Layer 'Bicycle library' shows and asks freeform values for key 'service:bicycle:cleaning:charge' (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Bicycle library' shows service:bicycle:cleaning:fee=no&service:bicycle:cleaning:charge= with a fixed text, namely 'The cleaning service is free to use' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')", - "value": "no&service:bicycle:cleaning:charge=" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Bicycle library' shows service:bicycle:cleaning:fee=no& with a fixed text, namely 'Free to use' (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')", - "value": "no&" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Bicycle library' shows service:bicycle:cleaning:fee=yes with a fixed text, namely 'The cleaning service has a fee' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')", - "value": "yes" - }, { "key": "amenity", "description": "The MapComplete theme Cyclofix - an open map for cyclists has a layer Bike stations (repair, pump or both) showing features with this tag", @@ -616,25 +578,6 @@ "description": "Layer 'Bike stations (repair, pump or both)' shows level=1 with a fixed text, namely 'Located on the first floor' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')", "value": "1" }, - { - "key": "service:bicycle:cleaning:charge", - "description": "Layer 'Bike stations (repair, pump or both)' shows and asks freeform values for key 'service:bicycle:cleaning:charge' (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Bike stations (repair, pump or both)' shows service:bicycle:cleaning:fee=no&service:bicycle:cleaning:charge= with a fixed text, namely 'The cleaning service is free to use' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')", - "value": "no&service:bicycle:cleaning:charge=" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Bike stations (repair, pump or both)' shows service:bicycle:cleaning:fee=no& with a fixed text, namely 'Free to use' (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')", - "value": "no&" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Bike stations (repair, pump or both)' shows service:bicycle:cleaning:fee=yes with a fixed text, namely 'The cleaning service has a fee' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')", - "value": "yes" - }, { "key": "amenity", "description": "The MapComplete theme Cyclofix - an open map for cyclists has a layer Bicycle tube vending machine showing features with this tag", @@ -751,25 +694,6 @@ "description": "Layer 'Bicycle tube vending machine' shows vending:bicycle_lock=yes with a fixed text, namely 'Bicycle locks are sold here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')", "value": "yes" }, - { - "key": "service:bicycle:cleaning:charge", - "description": "Layer 'Bicycle tube vending machine' shows and asks freeform values for key 'service:bicycle:cleaning:charge' (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Bicycle tube vending machine' shows service:bicycle:cleaning:fee=no&service:bicycle:cleaning:charge= with a fixed text, namely 'The cleaning service is free to use' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')", - "value": "no&service:bicycle:cleaning:charge=" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Bicycle tube vending machine' shows service:bicycle:cleaning:fee=no& with a fixed text, namely 'Free to use' (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')", - "value": "no&" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Bicycle tube vending machine' shows service:bicycle:cleaning:fee=yes with a fixed text, namely 'The cleaning service has a fee' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')", - "value": "yes" - }, { "key": "amenity", "description": "The MapComplete theme Cyclofix - an open map for cyclists has a layer Drinking water showing features with this tag", @@ -820,25 +744,6 @@ "description": "Layer 'Drinking water' shows bottle=no with a fixed text, namely 'Water bottles may not fit' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')", "value": "no" }, - { - "key": "service:bicycle:cleaning:charge", - "description": "Layer 'Drinking water' shows and asks freeform values for key 'service:bicycle:cleaning:charge' (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Drinking water' shows service:bicycle:cleaning:fee=no&service:bicycle:cleaning:charge= with a fixed text, namely 'The cleaning service is free to use' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')", - "value": "no&service:bicycle:cleaning:charge=" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Drinking water' shows service:bicycle:cleaning:fee=no& with a fixed text, namely 'Free to use' (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')", - "value": "no&" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Drinking water' shows service:bicycle:cleaning:fee=yes with a fixed text, namely 'The cleaning service has a fee' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')", - "value": "yes" - }, { "key": "theme", "description": "The MapComplete theme Cyclofix - an open map for cyclists has a layer Bike related object showing features with this tag", @@ -920,25 +825,6 @@ "key": "opening_hours", "description": "Layer 'Bike related object' shows and asks freeform values for key 'opening_hours' (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')" }, - { - "key": "service:bicycle:cleaning:charge", - "description": "Layer 'Bike related object' shows and asks freeform values for key 'service:bicycle:cleaning:charge' (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Bike related object' shows service:bicycle:cleaning:fee=no&service:bicycle:cleaning:charge= with a fixed text, namely 'The cleaning service is free to use' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')", - "value": "no&service:bicycle:cleaning:charge=" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Bike related object' shows service:bicycle:cleaning:fee=no& with a fixed text, namely 'Free to use' (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')", - "value": "no&" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Bike related object' shows service:bicycle:cleaning:fee=yes with a fixed text, namely 'The cleaning service has a fee' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')", - "value": "yes" - }, { "key": "service:bicycle:cleaning", "description": "The MapComplete theme Cyclofix - an open map for cyclists has a layer Bike cleaning service showing features with this tag", @@ -1149,25 +1035,6 @@ { "key": "capacity:cargo_bike", "description": "Layer 'Bike parking' shows and asks freeform values for key 'capacity:cargo_bike' (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')" - }, - { - "key": "service:bicycle:cleaning:charge", - "description": "Layer 'Bike parking' shows and asks freeform values for key 'service:bicycle:cleaning:charge' (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Bike parking' shows service:bicycle:cleaning:fee=no&service:bicycle:cleaning:charge= with a fixed text, namely 'The cleaning service is free to use' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')", - "value": "no&service:bicycle:cleaning:charge=" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Bike parking' shows service:bicycle:cleaning:fee=no& with a fixed text, namely 'Free to use' (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')", - "value": "no&" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Bike parking' shows service:bicycle:cleaning:fee=yes with a fixed text, namely 'The cleaning service has a fee' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')", - "value": "yes" } ] } \ No newline at end of file diff --git a/Docs/TagInfo/mapcomplete_drinking_water.json b/Docs/TagInfo/mapcomplete_drinking_water.json index 2c687ac59..2db459901 100644 --- a/Docs/TagInfo/mapcomplete_drinking_water.json +++ b/Docs/TagInfo/mapcomplete_drinking_water.json @@ -59,25 +59,6 @@ "key": "bottle", "description": "Layer 'Drinking water' shows bottle=no with a fixed text, namely 'Water bottles may not fit' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Drinking Water')", "value": "no" - }, - { - "key": "service:bicycle:cleaning:charge", - "description": "Layer 'Drinking water' shows and asks freeform values for key 'service:bicycle:cleaning:charge' (in the MapComplete.osm.be theme 'Drinking Water')" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Drinking water' shows service:bicycle:cleaning:fee=no&service:bicycle:cleaning:charge= with a fixed text, namely 'The cleaning service is free to use' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Drinking Water')", - "value": "no&service:bicycle:cleaning:charge=" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Drinking water' shows service:bicycle:cleaning:fee=no& with a fixed text, namely 'Free to use' (in the MapComplete.osm.be theme 'Drinking Water')", - "value": "no&" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Drinking water' shows service:bicycle:cleaning:fee=yes with a fixed text, namely 'The cleaning service has a fee' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Drinking Water')", - "value": "yes" } ] } \ No newline at end of file diff --git a/Docs/TagInfo/mapcomplete_food.json b/Docs/TagInfo/mapcomplete_food.json index 2ae75a8ff..4666bf436 100644 --- a/Docs/TagInfo/mapcomplete_food.json +++ b/Docs/TagInfo/mapcomplete_food.json @@ -76,6 +76,16 @@ "description": "Layer 'Restaurants and fast food' shows payment:cards=yes with a fixed text, namely 'Payment cards are accepted here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Restaurants and fast food')", "value": "yes" }, + { + "key": "payment:app", + "description": "Layer 'Restaurants and fast food' shows payment:app=yes with a fixed text, namely 'Payment is done using a dedicated app' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Restaurants and fast food')", + "value": "yes" + }, + { + "key": "payment:membership_card", + "description": "Layer 'Restaurants and fast food' shows payment:membership_card=yes with a fixed text, namely 'Payment is done using a membership card' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Restaurants and fast food')", + "value": "yes" + }, { "key": "wheelchair", "description": "Layer 'Restaurants and fast food' shows wheelchair=designated with a fixed text, namely 'This place is specially adapated for wheelchair users' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Restaurants and fast food')", diff --git a/Docs/TagInfo/mapcomplete_fritures.json b/Docs/TagInfo/mapcomplete_fritures.json index ae5de3180..54b743f87 100644 --- a/Docs/TagInfo/mapcomplete_fritures.json +++ b/Docs/TagInfo/mapcomplete_fritures.json @@ -81,6 +81,16 @@ "description": "Layer 'Fries shop' shows payment:cards=yes with a fixed text, namely 'Payment cards are accepted here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')", "value": "yes" }, + { + "key": "payment:app", + "description": "Layer 'Fries shop' shows payment:app=yes with a fixed text, namely 'Payment is done using a dedicated app' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')", + "value": "yes" + }, + { + "key": "payment:membership_card", + "description": "Layer 'Fries shop' shows payment:membership_card=yes with a fixed text, namely 'Payment is done using a membership card' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')", + "value": "yes" + }, { "key": "wheelchair", "description": "Layer 'Fries shop' shows wheelchair=designated with a fixed text, namely 'This place is specially adapated for wheelchair users' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')", @@ -396,6 +406,16 @@ "description": "Layer 'Restaurants and fast food' shows payment:cards=yes with a fixed text, namely 'Payment cards are accepted here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')", "value": "yes" }, + { + "key": "payment:app", + "description": "Layer 'Restaurants and fast food' shows payment:app=yes with a fixed text, namely 'Payment is done using a dedicated app' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')", + "value": "yes" + }, + { + "key": "payment:membership_card", + "description": "Layer 'Restaurants and fast food' shows payment:membership_card=yes with a fixed text, namely 'Payment is done using a membership card' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')", + "value": "yes" + }, { "key": "wheelchair", "description": "Layer 'Restaurants and fast food' shows wheelchair=designated with a fixed text, namely 'This place is specially adapated for wheelchair users' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')", diff --git a/Docs/TagInfo/mapcomplete_maps.json b/Docs/TagInfo/mapcomplete_maps.json index 8fe815f50..be108e5c1 100644 --- a/Docs/TagInfo/mapcomplete_maps.json +++ b/Docs/TagInfo/mapcomplete_maps.json @@ -74,25 +74,6 @@ "key": "map_source:attribution", "description": "Layer 'Maps' shows map_source:attribution=no with a fixed text, namely 'There is no attribution at all' (in the MapComplete.osm.be theme 'A map of maps')", "value": "no" - }, - { - "key": "service:bicycle:cleaning:charge", - "description": "Layer 'Maps' shows and asks freeform values for key 'service:bicycle:cleaning:charge' (in the MapComplete.osm.be theme 'A map of maps')" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Maps' shows service:bicycle:cleaning:fee=no&service:bicycle:cleaning:charge= with a fixed text, namely 'The cleaning service is free to use' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'A map of maps')", - "value": "no&service:bicycle:cleaning:charge=" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Maps' shows service:bicycle:cleaning:fee=no& with a fixed text, namely 'Free to use' (in the MapComplete.osm.be theme 'A map of maps')", - "value": "no&" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Maps' shows service:bicycle:cleaning:fee=yes with a fixed text, namely 'The cleaning service has a fee' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'A map of maps')", - "value": "yes" } ] } \ No newline at end of file diff --git a/Docs/TagInfo/mapcomplete_nature.json b/Docs/TagInfo/mapcomplete_nature.json index c6782c08f..75fc1ec2d 100644 --- a/Docs/TagInfo/mapcomplete_nature.json +++ b/Docs/TagInfo/mapcomplete_nature.json @@ -60,25 +60,6 @@ "description": "Layer 'Drinking water' shows bottle=no with a fixed text, namely 'Water bottles may not fit' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'De Natuur in')", "value": "no" }, - { - "key": "service:bicycle:cleaning:charge", - "description": "Layer 'Drinking water' shows and asks freeform values for key 'service:bicycle:cleaning:charge' (in the MapComplete.osm.be theme 'De Natuur in')" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Drinking water' shows service:bicycle:cleaning:fee=no&service:bicycle:cleaning:charge= with a fixed text, namely 'The cleaning service is free to use' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'De Natuur in')", - "value": "no&service:bicycle:cleaning:charge=" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Drinking water' shows service:bicycle:cleaning:fee=no& with a fixed text, namely 'Free to use' (in the MapComplete.osm.be theme 'De Natuur in')", - "value": "no&" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Drinking water' shows service:bicycle:cleaning:fee=yes with a fixed text, namely 'The cleaning service has a fee' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'De Natuur in')", - "value": "yes" - }, { "key": "leisure", "description": "The MapComplete theme De Natuur in has a layer Vogelkijkhutten showing features with this tag", @@ -189,25 +170,6 @@ "description": "Layer 'Vogelkijkhutten' shows operator=Agentschap Natuur en Bos with a fixed text, namely 'Beheer door het Agentschap Natuur en Bos ' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'De Natuur in')", "value": "Agentschap Natuur en Bos" }, - { - "key": "service:bicycle:cleaning:charge", - "description": "Layer 'Vogelkijkhutten' shows and asks freeform values for key 'service:bicycle:cleaning:charge' (in the MapComplete.osm.be theme 'De Natuur in')" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Vogelkijkhutten' shows service:bicycle:cleaning:fee=no&service:bicycle:cleaning:charge= with a fixed text, namely 'The cleaning service is free to use' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'De Natuur in')", - "value": "no&service:bicycle:cleaning:charge=" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Vogelkijkhutten' shows service:bicycle:cleaning:fee=no& with a fixed text, namely 'Free to use' (in the MapComplete.osm.be theme 'De Natuur in')", - "value": "no&" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Vogelkijkhutten' shows service:bicycle:cleaning:fee=yes with a fixed text, namely 'The cleaning service has a fee' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'De Natuur in')", - "value": "yes" - }, { "key": "tourism", "description": "The MapComplete theme De Natuur in has a layer Maps showing features with this tag", @@ -273,25 +235,6 @@ "description": "Layer 'Maps' shows map_source:attribution=no with a fixed text, namely 'There is no attribution at all' (in the MapComplete.osm.be theme 'De Natuur in')", "value": "no" }, - { - "key": "service:bicycle:cleaning:charge", - "description": "Layer 'Maps' shows and asks freeform values for key 'service:bicycle:cleaning:charge' (in the MapComplete.osm.be theme 'De Natuur in')" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Maps' shows service:bicycle:cleaning:fee=no&service:bicycle:cleaning:charge= with a fixed text, namely 'The cleaning service is free to use' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'De Natuur in')", - "value": "no&service:bicycle:cleaning:charge=" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Maps' shows service:bicycle:cleaning:fee=no& with a fixed text, namely 'Free to use' (in the MapComplete.osm.be theme 'De Natuur in')", - "value": "no&" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Maps' shows service:bicycle:cleaning:fee=yes with a fixed text, namely 'The cleaning service has a fee' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'De Natuur in')", - "value": "yes" - }, { "key": "information", "description": "The MapComplete theme De Natuur in has a layer Information boards showing features with this tag", @@ -313,25 +256,6 @@ "key": "wikipedia", "description": "The layer 'Information boards allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary" }, - { - "key": "service:bicycle:cleaning:charge", - "description": "Layer 'Information boards' shows and asks freeform values for key 'service:bicycle:cleaning:charge' (in the MapComplete.osm.be theme 'De Natuur in')" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Information boards' shows service:bicycle:cleaning:fee=no&service:bicycle:cleaning:charge= with a fixed text, namely 'The cleaning service is free to use' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'De Natuur in')", - "value": "no&service:bicycle:cleaning:charge=" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Information boards' shows service:bicycle:cleaning:fee=no& with a fixed text, namely 'Free to use' (in the MapComplete.osm.be theme 'De Natuur in')", - "value": "no&" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Information boards' shows service:bicycle:cleaning:fee=yes with a fixed text, namely 'The cleaning service has a fee' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'De Natuur in')", - "value": "yes" - }, { "key": "leisure", "description": "The MapComplete theme De Natuur in has a layer Natuurgebied showing features with this tag", @@ -505,25 +429,6 @@ "key": "wikidata", "description": "Layer 'Natuurgebied' shows wikidata= with a fixed text, namely 'No Wikipedia page has been linked yet' (in the MapComplete.osm.be theme 'De Natuur in') Picking this answer will delete the key wikidata.", "value": "" - }, - { - "key": "service:bicycle:cleaning:charge", - "description": "Layer 'Natuurgebied' shows and asks freeform values for key 'service:bicycle:cleaning:charge' (in the MapComplete.osm.be theme 'De Natuur in')" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Natuurgebied' shows service:bicycle:cleaning:fee=no&service:bicycle:cleaning:charge= with a fixed text, namely 'The cleaning service is free to use' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'De Natuur in')", - "value": "no&service:bicycle:cleaning:charge=" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Natuurgebied' shows service:bicycle:cleaning:fee=no& with a fixed text, namely 'Free to use' (in the MapComplete.osm.be theme 'De Natuur in')", - "value": "no&" - }, - { - "key": "service:bicycle:cleaning:fee", - "description": "Layer 'Natuurgebied' shows service:bicycle:cleaning:fee=yes with a fixed text, namely 'The cleaning service has a fee' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'De Natuur in')", - "value": "yes" } ] } \ No newline at end of file diff --git a/Docs/TagInfo/mapcomplete_waste_basket.json b/Docs/TagInfo/mapcomplete_waste_basket.json index eeebd2e5b..1bbaca8ca 100644 --- a/Docs/TagInfo/mapcomplete_waste_basket.json +++ b/Docs/TagInfo/mapcomplete_waste_basket.json @@ -44,6 +44,31 @@ "key": "waste", "description": "Layer 'Waste Basket' shows waste=sharps with a fixed text, namely 'A waste basket for needles and other sharp objects' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Waste Basket')", "value": "sharps" + }, + { + "key": "vending", + "description": "Layer 'Waste Basket' shows vending=dog_excrement_bag¬:vending= with a fixed text, namely 'This waste basket has a dispenser for (dog) excrement bags' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Waste Basket')", + "value": "dog_excrement_bag" + }, + { + "key": "not:vending", + "description": "Layer 'Waste Basket' shows vending=dog_excrement_bag¬:vending= with a fixed text, namely 'This waste basket has a dispenser for (dog) excrement bags' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Waste Basket') Picking this answer will delete the key not:vending.", + "value": "" + }, + { + "key": "not:vending", + "description": "Layer 'Waste Basket' shows not:vending=dog_excrement_bag&vending= with a fixed text, namely 'This waste basket does not have a dispenser for (dog) excrement bags' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Waste Basket')", + "value": "dog_excrement_bag" + }, + { + "key": "vending", + "description": "Layer 'Waste Basket' shows not:vending=dog_excrement_bag&vending= with a fixed text, namely 'This waste basket does not have a dispenser for (dog) excrement bags' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Waste Basket') Picking this answer will delete the key vending.", + "value": "" + }, + { + "key": "vending", + "description": "Layer 'Waste Basket' shows vending= with a fixed text, namely 'This waste basket does not have a dispenser for (dog) excrement bags' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Waste Basket') Picking this answer will delete the key vending.", + "value": "" } ] } \ No newline at end of file diff --git a/Docs/URL_Parameters.md b/Docs/URL_Parameters.md index bd363df9b..a27eadc97 100644 --- a/Docs/URL_Parameters.md +++ b/Docs/URL_Parameters.md @@ -20,42 +20,6 @@ the URL-parameters are stated in the part between the `?` and the `#`. There are Finally, the URL-hash is the part after the `#`. It is `node/1234` in this case. - download-control-toggle -------------------------- - - Whether or not the download panel is shown The default value is _false_ - - - filter-toggle ---------------- - - Whether or not the filter view is shown The default value is _false_ - - - tab ------ - - The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >50 changesets) The default value is _0_ - - - z ---- - - The initial/current zoom level The default value is _0_ - - - lat ------ - - The initial/current latitude The default value is _0_ - - - lon ------ - - The initial/current longitude of the app The default value is _0_ - - fs-userbadge -------------- @@ -92,10 +56,10 @@ Finally, the URL-hash is the part after the `#`. It is `node/1234` in this case. Disables/enables the help menu or welcome message The default value is _true_ - fs-iframe ------------ + fs-iframe-popout +------------------ - Disables/Enables the iframe-popup The default value is _false_ + Disables/Enables the iframe-popout button. If in iframe mode and the welcome message is hidden, a popout button to the full mapcomplete instance is shown instead (unless disabled with this switch) The default value is _true_ fs-more-quests @@ -134,6 +98,12 @@ Finally, the URL-hash is the part after the `#`. It is `node/1234` in this case. Enable the PDF download button The default value is _false_ + backend +--------- + + The OSM backend to use - can be used to redirect mapcomplete to the testing backend when using 'osm-test' The default value is _osm_ + + test ------ @@ -152,12 +122,6 @@ Finally, the URL-hash is the part after the `#`. It is `node/1234` in this case. If true, 'dryrun' mode is activated and a fake user account is loaded The default value is _false_ - backend ---------- - - The OSM backend to use - can be used to redirect mapcomplete to the testing backend when using 'osm-test' The default value is _osm_ - - overpassUrl ------------- @@ -170,10 +134,16 @@ Finally, the URL-hash is the part after the `#`. It is `node/1234` in this case. Set a different timeout (in seconds) for queries in overpass The default value is _30_ - custom-css ------------- + overpassMaxZoom +----------------- - If specified, the custom css from the given link will be loaded additionaly The default value is __ + point to switch between OSM-api and overpass The default value is _17_ + + + osmApiTileSize +---------------- + + Tilesize when the OSM-API is used to fetch data within a BBOX The default value is _18_ background diff --git a/Logic/State/FeatureSwitchState.ts b/Logic/State/FeatureSwitchState.ts index 0f5df51f7..d02abe270 100644 --- a/Logic/State/FeatureSwitchState.ts +++ b/Logic/State/FeatureSwitchState.ts @@ -37,7 +37,7 @@ export default class FeatureSwitchState { public readonly osmApiTileSize: UIEventSource; public readonly backgroundLayerId: UIEventSource; - protected constructor(layoutToUse: LayoutConfig) { + public constructor(layoutToUse: LayoutConfig) { this.layoutToUse = layoutToUse; diff --git a/scripts/generateDocs.ts b/scripts/generateDocs.ts index 65741b72f..0ad24f43d 100644 --- a/scripts/generateDocs.ts +++ b/scripts/generateDocs.ts @@ -1,4 +1,5 @@ import {Utils} from "../Utils"; + Utils.runningFromConsole = true; import SpecialVisualizations from "../UI/SpecialVisualizations"; import SimpleMetaTagger from "../Logic/SimpleMetaTagger"; @@ -11,7 +12,8 @@ import {writeFileSync} from "fs"; import State from "../State"; import {QueryParameters} from "../Logic/Web/QueryParameters"; import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"; - +import Minimap from "../UI/Base/Minimap"; +import FeatureSwitchState from "../Logic/State/FeatureSwitchState"; function WriteFile(filename, html: string | BaseUIElement, autogenSource: string[]): void { @@ -25,8 +27,13 @@ WriteFile("./Docs/CalculatedTags.md", new Combine([SimpleMetaTagger.HelpText(), ["SimpleMetaTagger", "ExtraFunction"]) WriteFile("./Docs/SpecialInputElements.md", ValidatedTextField.HelpText(), ["ValidatedTextField.ts"]); +Minimap.createMiniMap = _ => { + console.log("Not creating a minimap, it is disabled"); + return undefined +} -new State(new LayoutConfig({ + +const dummyLayout = new LayoutConfig({ language: ["en"], id: "", maintainer: "pietervdvn", @@ -43,11 +50,15 @@ new State(new LayoutConfig({ id: "", source: { osmTags: "id~*" - } + }, + mapRendering: [] } ] -})) +}) + +new FeatureSwitchState(dummyLayout) + QueryParameters.GetQueryParameter("layer-", "true", "Wether or not the layer with id is shown") WriteFile("./Docs/URL_Parameters.md", QueryParameters.GenerateQueryParameterDocs(), ["QueryParameters"]) From 22ad259e8376b969a8f009b174fcc0466e9ed71b Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 28 Oct 2021 03:21:44 +0200 Subject: [PATCH 41/95] Add taginfo page for etymology theme --- Docs/TagInfo/mapcomplete_etymology.json | 206 ++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 Docs/TagInfo/mapcomplete_etymology.json diff --git a/Docs/TagInfo/mapcomplete_etymology.json b/Docs/TagInfo/mapcomplete_etymology.json new file mode 100644 index 000000000..ae6229db9 --- /dev/null +++ b/Docs/TagInfo/mapcomplete_etymology.json @@ -0,0 +1,206 @@ +{ + "data_format": 1, + "project": { + "name": "MapComplete Open Etymology Map", + "description": "What is the origin of a toponym?", + "project_url": "https://mapcomplete.osm.be/etymology", + "doc_url": "https://github.com/pietervdvn/MapComplete/tree/master/assets/themes/", + "icon_url": "https://mapcomplete.osm.be/assets/layers/etymology/logo.svg", + "contact_name": "Pieter Vander Vennet, ", + "contact_email": "pietervdvn@posteo.net" + }, + "tags": [ + { + "key": "name:etymology:wikidata", + "description": "The MapComplete theme Open Etymology Map has a layer Has etymolgy showing features with this tag" + }, + { + "key": "name:etymology", + "description": "The MapComplete theme Open Etymology Map has a layer Has etymolgy showing features with this tag" + }, + { + "key": "image", + "description": "The layer 'Has etymolgy shows images based on the keys image, image:0, image:1,... and wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "mapillary", + "description": "The layer 'Has etymolgy shows images based on the keys image, image:0, image:1,... and wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "wikidata", + "description": "The layer 'Has etymolgy shows images based on the keys image, image:0, image:1,... and wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "wikipedia", + "description": "The layer 'Has etymolgy shows images based on the keys image, image:0, image:1,... and wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "name:etymology:wikidata", + "description": "Layer 'Has etymolgy' shows and asks freeform values for key 'name:etymology:wikidata' (in the MapComplete.osm.be theme 'Open Etymology Map')" + }, + { + "key": "name:etymology", + "description": "Layer 'Has etymolgy' shows and asks freeform values for key 'name:etymology' (in the MapComplete.osm.be theme 'Open Etymology Map')" + }, + { + "key": "name:etymology", + "description": "Layer 'Has etymolgy' shows name:etymology=unknown with a fixed text, namely 'The origin of this name is unknown in all literature' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Etymology Map')", + "value": "unknown" + }, + { + "key": "image", + "description": "The layer 'Has etymolgy allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "mapillary", + "description": "The layer 'Has etymolgy allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "wikidata", + "description": "The layer 'Has etymolgy allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "wikipedia", + "description": "The layer 'Has etymolgy allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "wikidata", + "description": "Layer 'Has etymolgy' shows and asks freeform values for key 'wikidata' (in the MapComplete.osm.be theme 'Open Etymology Map')" + }, + { + "key": "wikidata", + "description": "Layer 'Has etymolgy' shows wikidata= with a fixed text, namely 'No Wikipedia page has been linked yet' (in the MapComplete.osm.be theme 'Open Etymology Map') Picking this answer will delete the key wikidata.", + "value": "" + }, + { + "key": "name", + "description": "The MapComplete theme Open Etymology Map has a layer Streets without etymology information showing features with this tag" + }, + { + "key": "highway", + "description": "The MapComplete theme Open Etymology Map has a layer Streets without etymology information showing features with this tag" + }, + { + "key": "image", + "description": "The layer 'Streets without etymology information shows images based on the keys image, image:0, image:1,... and wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "mapillary", + "description": "The layer 'Streets without etymology information shows images based on the keys image, image:0, image:1,... and wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "wikidata", + "description": "The layer 'Streets without etymology information shows images based on the keys image, image:0, image:1,... and wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "wikipedia", + "description": "The layer 'Streets without etymology information shows images based on the keys image, image:0, image:1,... and wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "name:etymology:wikidata", + "description": "Layer 'Streets without etymology information' shows and asks freeform values for key 'name:etymology:wikidata' (in the MapComplete.osm.be theme 'Open Etymology Map')" + }, + { + "key": "name:etymology", + "description": "Layer 'Streets without etymology information' shows and asks freeform values for key 'name:etymology' (in the MapComplete.osm.be theme 'Open Etymology Map')" + }, + { + "key": "name:etymology", + "description": "Layer 'Streets without etymology information' shows name:etymology=unknown with a fixed text, namely 'The origin of this name is unknown in all literature' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Etymology Map')", + "value": "unknown" + }, + { + "key": "image", + "description": "The layer 'Streets without etymology information allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "mapillary", + "description": "The layer 'Streets without etymology information allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "wikidata", + "description": "The layer 'Streets without etymology information allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "wikipedia", + "description": "The layer 'Streets without etymology information allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "wikidata", + "description": "Layer 'Streets without etymology information' shows and asks freeform values for key 'wikidata' (in the MapComplete.osm.be theme 'Open Etymology Map')" + }, + { + "key": "wikidata", + "description": "Layer 'Streets without etymology information' shows wikidata= with a fixed text, namely 'No Wikipedia page has been linked yet' (in the MapComplete.osm.be theme 'Open Etymology Map') Picking this answer will delete the key wikidata.", + "value": "" + }, + { + "key": "name", + "description": "The MapComplete theme Open Etymology Map has a layer Parks and forests without etymology information showing features with this tag" + }, + { + "key": "leisure", + "description": "The MapComplete theme Open Etymology Map has a layer Parks and forests without etymology information showing features with this tag", + "value": "park" + }, + { + "key": "landuse", + "description": "The MapComplete theme Open Etymology Map has a layer Parks and forests without etymology information showing features with this tag", + "value": "forest" + }, + { + "key": "image", + "description": "The layer 'Parks and forests without etymology information shows images based on the keys image, image:0, image:1,... and wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "mapillary", + "description": "The layer 'Parks and forests without etymology information shows images based on the keys image, image:0, image:1,... and wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "wikidata", + "description": "The layer 'Parks and forests without etymology information shows images based on the keys image, image:0, image:1,... and wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "wikipedia", + "description": "The layer 'Parks and forests without etymology information shows images based on the keys image, image:0, image:1,... and wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "name:etymology:wikidata", + "description": "Layer 'Parks and forests without etymology information' shows and asks freeform values for key 'name:etymology:wikidata' (in the MapComplete.osm.be theme 'Open Etymology Map')" + }, + { + "key": "name:etymology", + "description": "Layer 'Parks and forests without etymology information' shows and asks freeform values for key 'name:etymology' (in the MapComplete.osm.be theme 'Open Etymology Map')" + }, + { + "key": "name:etymology", + "description": "Layer 'Parks and forests without etymology information' shows name:etymology=unknown with a fixed text, namely 'The origin of this name is unknown in all literature' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Etymology Map')", + "value": "unknown" + }, + { + "key": "image", + "description": "The layer 'Parks and forests without etymology information allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "mapillary", + "description": "The layer 'Parks and forests without etymology information allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "wikidata", + "description": "The layer 'Parks and forests without etymology information allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "wikipedia", + "description": "The layer 'Parks and forests without etymology information allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "wikidata", + "description": "Layer 'Parks and forests without etymology information' shows and asks freeform values for key 'wikidata' (in the MapComplete.osm.be theme 'Open Etymology Map')" + }, + { + "key": "wikidata", + "description": "Layer 'Parks and forests without etymology information' shows wikidata= with a fixed text, namely 'No Wikipedia page has been linked yet' (in the MapComplete.osm.be theme 'Open Etymology Map') Picking this answer will delete the key wikidata.", + "value": "" + } + ] +} \ No newline at end of file From 89f4ab5045d1ff7086992c73feceeb8ab81cafc9 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 28 Oct 2021 03:24:15 +0200 Subject: [PATCH 42/95] Add some example housename plaques --- ...zabeth_House_-_geograph.org.uk_-_2693028.jpg | Bin 0 -> 86987 bytes ...Raphoe_House_-_geograph.org.uk_-_1925685.jpg | Bin 0 -> 67886 bytes ...s_Roddy_House_-_geograph.org.uk_-_2000318.jpg | Bin 0 -> 66650 bytes assets/themes/uk_addresses/uk_addresses.json | 4 ++-- 4 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 assets/themes/uk_addresses/Commemorative_plaque_on_Elizabeth_House_-_geograph.org.uk_-_2693028.jpg create mode 100644 assets/themes/uk_addresses/Plaque,_Raphoe_House_-_geograph.org.uk_-_1925685.jpg create mode 100644 assets/themes/uk_addresses/Plaque,_Séamus_Roddy_House_-_geograph.org.uk_-_2000318.jpg diff --git a/assets/themes/uk_addresses/Commemorative_plaque_on_Elizabeth_House_-_geograph.org.uk_-_2693028.jpg b/assets/themes/uk_addresses/Commemorative_plaque_on_Elizabeth_House_-_geograph.org.uk_-_2693028.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a51b9a8267329fdc352b922681c87bc1c42719b6 GIT binary patch literal 86987 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�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≪rP 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�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 Yq6MxT0Lz9WC%trG_h%k8d#RuQ*&d8EYXATM literal 0 HcmV?d00001 diff --git a/assets/themes/uk_addresses/uk_addresses.json b/assets/themes/uk_addresses/uk_addresses.json index 883ec7c53..e48f8cf7a 100644 --- a/assets/themes/uk_addresses/uk_addresses.json +++ b/assets/themes/uk_addresses/uk_addresses.json @@ -254,8 +254,8 @@ "iconSize": { "render": "40,40,center" } - - }, { + }, + { "color": { "render": "#00f", "mappings": [ From 447131958813c5561f8e08fd0ec1949c49d85b5a Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 28 Oct 2021 03:25:12 +0200 Subject: [PATCH 43/95] Reformatting and regeneration of themes --- .../charging_station/charging_station.json | 148 +++++++++--------- .../left_right_style/left_right_style.json | 20 ++- assets/themes/grb_import/grb.json | 16 +- 3 files changed, 97 insertions(+), 87 deletions(-) diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index b492e7cc2..22d87c33e 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -1681,7 +1681,7 @@ "planned:amenity=", "construction:amenity=", "disused:amenity=", - "operational_status=", + "operational_status=broken", "amenity=charging_station" ] }, @@ -1694,11 +1694,11 @@ { "if": { "and": [ - "planned:amenity=", + "planned:amenity=charging_station", "construction:amenity=", "disused:amenity=", - "operational_status=broken", - "amenity=charging_station" + "operational_status=", + "amenity=" ] }, "then": { @@ -1710,8 +1710,8 @@ { "if": { "and": [ - "planned:amenity=charging_station", - "construction:amenity=", + "planned:amenity=", + "construction:amenity=charging_station", "disused:amenity=", "operational_status=", "amenity=" @@ -1727,8 +1727,8 @@ "if": { "and": [ "planned:amenity=", - "construction:amenity=charging_station", - "disused:amenity=", + "construction:amenity=", + "disused:amenity=charging_station", "operational_status=", "amenity=" ] @@ -1744,9 +1744,9 @@ "and": [ "planned:amenity=", "construction:amenity=", - "disused:amenity=charging_station", + "disused:amenity=", "operational_status=", - "amenity=" + "amenity=charging_station" ] }, "then": { @@ -1793,6 +1793,69 @@ } } ], + "mapRendering": [ + { + "location": [ + "point", + "centroid" + ], + "icon": { + "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", + "mappings": [ + { + "if": "bicycle=yes", + "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" + }, + { + "if": { + "or": [ + "car=yes", + "motorcar=yes" + ] + }, + "then": "pin:#fff;./assets/themes/charging_stations/car.svg" + } + ] + }, + "iconBadges": [ + { + "if": { + "or": [ + "disused:amenity=charging_station", + "operational_status=broken" + ] + }, + "then": "cross:#c22;" + }, + { + "if": { + "or": [ + "proposed:amenity=charging_station", + "planned:amenity=charging_station" + ] + }, + "then": "./assets/layers/charging_station/under_construction.svg" + }, + { + "if": { + "and": [ + "bicycle=yes", + { + "or": [ + "motorcar=yes", + "car=yes" + ] + } + ] + }, + "then": "circle:#fff;./assets/themes/charging_stations/car.svg" + } + ], + "iconSize": { + "render": "50,50,bottom" + } + } + ], "presets": [ { "tags": [ @@ -2243,68 +2306,5 @@ ] }, "neededChangesets": 10 - }, - "mapRendering": [ - { - "location": [ - "point", - "centroid" - ], - "icon": { - "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", - "mappings": [ - { - "if": "bicycle=yes", - "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" - }, - { - "if": { - "or": [ - "car=yes", - "motorcar=yes" - ] - }, - "then": "pin:#fff;./assets/themes/charging_stations/car.svg" - } - ] - }, - "iconBadges": [ - { - "if": { - "or": [ - "disused:amenity=charging_station", - "operational_status=broken" - ] - }, - "then": "cross:#c22;" - }, - { - "if": { - "or": [ - "proposed:amenity=charging_station", - "planned:amenity=charging_station" - ] - }, - "then": "./assets/layers/charging_station/under_construction.svg" - }, - { - "if": { - "and": [ - "bicycle=yes", - { - "or": [ - "motorcar=yes", - "car=yes" - ] - } - ] - }, - "then": "circle:#fff;./assets/themes/charging_stations/car.svg" - } - ], - "iconSize": { - "render": "50,50,bottom" - } - } - ] + } } \ No newline at end of file diff --git a/assets/layers/left_right_style/left_right_style.json b/assets/layers/left_right_style/left_right_style.json index 57ec143ef..91fd76b79 100644 --- a/assets/layers/left_right_style/left_right_style.json +++ b/assets/layers/left_right_style/left_right_style.json @@ -14,17 +14,21 @@ "width": 15, "color": { "render": "#ff000088", - "mappings": [{ - "if": "id=left", - "then": "#0000ff88" - }] + "mappings": [ + { + "if": "id=left", + "then": "#0000ff88" + } + ] }, "offset": { "render": "-15", - "mappings": [{ - "if": "id=right", - "then": "15" - }] + "mappings": [ + { + "if": "id=right", + "then": "15" + } + ] } } ] diff --git a/assets/themes/grb_import/grb.json b/assets/themes/grb_import/grb.json index 144e85ffd..c79f6e5fe 100644 --- a/assets/themes/grb_import/grb.json +++ b/assets/themes/grb_import/grb.json @@ -319,7 +319,9 @@ "title": "CRAB-adres", "mapRendering": [ { - "location": ["point"], + "location": [ + "point" + ], "icon": "circle:#bb3322", "iconSize": "15,15,center" } @@ -334,14 +336,16 @@ "mappings": [ { "if": { - "and":["_embedding_nr:={HUISNR}","_embedding_street:={STRAATNM}"] + "and": [ + "_embedding_nr:={HUISNR}", + "_embedding_street:={STRAATNM}" + ] }, "then": "no" } ] }, "tagRenderings": [ - { "id": "render_crab", "render": "Volgens het CRAB ligt hier {STRAATNM} {HUISNR} (label: {HNRLABEL})" @@ -350,10 +354,12 @@ "id": "render_embedded", "render": "Het omliggende object met addres heeft {_embedding_street} {_embedding_nr}", "condition": { - "and": ["_embedding_street~*","_embedding_nr~*"] + "and": [ + "_embedding_street~*", + "_embedding_nr~*" + ] } }, - "all_tags", { "id": "import-button", From c2682fc56d1ad8b4f7d0438e7f8e1da7149d0b5e Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 29 Oct 2021 01:41:37 +0200 Subject: [PATCH 44/95] Move legacy theme handling into a rewritting class, various small fixes --- Logic/DetermineLayout.ts | 3 + Models/ThemeConfig/LayerConfig.ts | 124 +++++++++---------- Models/ThemeConfig/LegacyJsonConvert.ts | 108 ++++++++++++++++ assets/layers/waste_basket/waste_basket.json | 3 +- assets/themes/grb_import/grb.json | 1 + index.ts | 7 -- scripts/generateTranslations.ts | 3 + scripts/lint.ts | 103 +-------------- 8 files changed, 178 insertions(+), 174 deletions(-) create mode 100644 Models/ThemeConfig/LegacyJsonConvert.ts diff --git a/Logic/DetermineLayout.ts b/Logic/DetermineLayout.ts index 563b8c1a7..b29e5f8b5 100644 --- a/Logic/DetermineLayout.ts +++ b/Logic/DetermineLayout.ts @@ -10,6 +10,7 @@ import {UIEventSource} from "./UIEventSource"; import {LocalStorageSource} from "./Web/LocalStorageSource"; import LZString from "lz-string"; import * as personal from "../assets/themes/personal/personal.json"; +import LegacyJsonConvert from "../Models/ThemeConfig/LegacyJsonConvert"; export default class DetermineLayout { @@ -74,6 +75,7 @@ export default class DetermineLayout { const parsed = await Utils.downloadJson(link) console.log("Got ", parsed) + LegacyJsonConvert.fixThemeConfig(parsed) try { parsed.id = link; return new LayoutConfig(parsed, false).patchImages(link, JSON.stringify(parsed)); @@ -136,6 +138,7 @@ export default class DetermineLayout { } } + LegacyJsonConvert.fixThemeConfig(json) const layoutToUse = new LayoutConfig(json, false); userLayoutParam.setData(layoutToUse.id); return [layoutToUse, btoa(Utils.MinifyJSON(JSON.stringify(json)))]; diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts index b09ffcb60..e30f0a20e 100644 --- a/Models/ThemeConfig/LayerConfig.ts +++ b/Models/ThemeConfig/LayerConfig.ts @@ -1,7 +1,6 @@ import {Translation} from "../../UI/i18n/Translation"; import SourceConfig from "./SourceConfig"; import TagRenderingConfig from "./TagRenderingConfig"; -import {TagsFilter} from "../../Logic/Tags/TagsFilter"; import PresetConfig from "./PresetConfig"; import {LayerConfigJson} from "./Json/LayerConfigJson"; import Translations from "../../UI/i18n/Translations"; @@ -58,56 +57,43 @@ export default class LayerConfig extends WithContextLoader { context = context + "." + json.id; super(json, context) this.id = json.id; - let legacy = undefined; - if (json["overpassTags"] !== undefined) { - // @ts-ignore - legacy = TagUtils.Tag(json["overpassTags"], context + ".overpasstags"); + + if (json.source === undefined) { + throw "Layer " + this.id + " does not define a source section ("+context+")" } - if (json.source !== undefined) { - this.maxAgeOfCache = json.source.maxCacheAge ?? 24 * 60 * 60 * 30 - if (legacy !== undefined) { - throw ( - context + - "Both the legacy 'layer.overpasstags' and the new 'layer.source'-field are defined" - ); - } + if (json.source.osmTags === undefined) { + throw "Layer " + this.id + " does not define a osmTags in the source section - these should always be present, even for geojson layers ("+context+")" - let osmTags: TagsFilter = legacy; - if (json.source["osmTags"]) { - osmTags = TagUtils.Tag( - json.source["osmTags"], - context + "source.osmTags" - ); - } - - if (json.source["geoJsonSource"] !== undefined) { - throw context + "Use 'geoJson' instead of 'geoJsonSource'"; - } - - if (json.source["geojson"] !== undefined) { - throw context + "Use 'geoJson' instead of 'geojson' (the J is a capital letter)"; - } - - this.source = new SourceConfig( - { - osmTags: osmTags, - geojsonSource: json.source["geoJson"], - geojsonSourceLevel: json.source["geoJsonZoomLevel"], - overpassScript: json.source["overpassScript"], - isOsmCache: json.source["isOsmCache"], - mercatorCrs: json.source["mercatorCrs"] - }, - json.id - ); - }else if(legacy === undefined){ - throw "No valid source defined ("+context+")" - } else { - this.source = new SourceConfig({ - osmTags: legacy, - }); } + this.maxAgeOfCache = json.source.maxCacheAge ?? 24 * 60 * 60 * 30 + + const osmTags = TagUtils.Tag( + json.source.osmTags, + context + "source.osmTags" + ); + + if (json.source["geoJsonSource"] !== undefined) { + throw context + "Use 'geoJson' instead of 'geoJsonSource'"; + } + + if (json.source["geojson"] !== undefined) { + throw context + "Use 'geoJson' instead of 'geojson' (the J is a capital letter)"; + } + + this.source = new SourceConfig( + { + osmTags: osmTags, + geojsonSource: json.source["geoJson"], + geojsonSourceLevel: json.source["geoJsonZoomLevel"], + overpassScript: json.source["overpassScript"], + isOsmCache: json.source["isOsmCache"], + mercatorCrs: json.source["mercatorCrs"] + }, + json.id + ); + this.allowSplit = json.allowSplit ?? false; this.name = Translations.T(json.name, context + ".name"); @@ -284,11 +270,13 @@ export default class LayerConfig extends WithContextLoader { const normalTagRenderings: (string | { builtin: string, override: any } | TagRenderingConfigJson)[] = [] - - const renderingsToRewrite: ({ rewrite:{ + + const renderingsToRewrite: ({ + rewrite: { sourceString: string, into: string[] - }, renderings: (string | { builtin: string, override: any } | TagRenderingConfigJson)[] })[] = [] + }, renderings: (string | { builtin: string, override: any } | TagRenderingConfigJson)[] + })[] = [] for (let i = 0; i < json.tagRenderings.length; i++) { const tr = json.tagRenderings[i]; const rewriteDefined = tr["rewrite"] !== undefined @@ -309,17 +297,17 @@ export default class LayerConfig extends WithContextLoader { const allRenderings = this.ParseTagRenderings(normalTagRenderings, false); - if(renderingsToRewrite.length === 0){ + if (renderingsToRewrite.length === 0) { return allRenderings } - - function prepConfig(keyToRewrite: string, target:string, tr: TagRenderingConfigJson){ - - function replaceRecursive(transl: string | any){ - if(typeof transl === "string"){ + + function prepConfig(keyToRewrite: string, target: string, tr: TagRenderingConfigJson) { + + function replaceRecursive(transl: string | any) { + if (typeof transl === "string") { return transl.replace(keyToRewrite, target) } - if(transl.map !== undefined){ + if (transl.map !== undefined) { return transl.map(o => replaceRecursive(o)) } transl = {...transl} @@ -328,39 +316,39 @@ export default class LayerConfig extends WithContextLoader { } return transl } - + const orig = tr; tr = replaceRecursive(tr) - - tr.id = target+"-"+orig.id + + tr.id = target + "-" + orig.id tr.group = target return tr } const rewriteGroups: Map = new Map() for (const rewriteGroup of renderingsToRewrite) { - + const tagRenderings = rewriteGroup.renderings const textToReplace = rewriteGroup.rewrite.sourceString const targets = rewriteGroup.rewrite.into for (const target of targets) { const parsedRenderings = this.ParseTagRenderings(tagRenderings, false, tr => prepConfig(textToReplace, target, tr)) - - if(!rewriteGroups.has(target)){ + + if (!rewriteGroups.has(target)) { rewriteGroups.set(target, []) } - rewriteGroups.get(target).push(... parsedRenderings) + rewriteGroups.get(target).push(...parsedRenderings) } } - - + + rewriteGroups.forEach((group, groupName) => { group.push(new TagRenderingConfig({ - id:"questions", - group:groupName + id: "questions", + group: groupName })) }) - + rewriteGroups.forEach(group => { allRenderings.push(...group) }) diff --git a/Models/ThemeConfig/LegacyJsonConvert.ts b/Models/ThemeConfig/LegacyJsonConvert.ts new file mode 100644 index 000000000..43274643c --- /dev/null +++ b/Models/ThemeConfig/LegacyJsonConvert.ts @@ -0,0 +1,108 @@ +import LineRenderingConfigJson from "./Json/LineRenderingConfigJson"; + +export default class LegacyJsonConvert { + + /** + * Updates the config file in-place + * @param config + * @private + */ + public static fixLayerConfig(config: any): void { + if (config["overpassTags"]) { + config.source = config.source ?? {} + config.source.osmTags = config["overpassTags"] + delete config["overpassTags"] + } + + if (config.tagRenderings !== undefined) { + for (const tagRendering of config.tagRenderings) { + if (tagRendering["#"] !== undefined) { + tagRendering["id"] = tagRendering["#"] + delete tagRendering["#"] + } + if (tagRendering["id"] === undefined) { + if (tagRendering["freeform"]?.key !== undefined) { + tagRendering["id"] = config.id + "-" + tagRendering["freeform"]["key"] + } + } + } + } + + if (config.mapRendering === undefined && config.id !== "sidewalks") { + // This is a legacy format, lets create a pointRendering + let location: ("point" | "centroid")[] = ["point"] + let wayHandling: number = config["wayHandling"] ?? 0 + if (wayHandling === 2) { + location = ["point", "centroid"] + } + config.mapRendering = [ + { + icon: config["icon"], + iconBadges: config["iconOverlays"], + label: config["label"], + iconSize: config["iconSize"], + location, + rotation: config["rotation"] + } + ] + + if (wayHandling !== 1) { + const lineRenderConfig = { + color: config["color"], + width: config["width"], + dashArray: config["dashArray"] + } + if (Object.keys(lineRenderConfig).length > 0) { + config.mapRendering.push(lineRenderConfig) + } + } + + + delete config["color"] + delete config["width"] + delete config["dashArray"] + + delete config["icon"] + delete config["iconOverlays"] + delete config["label"] + delete config["iconSize"] + delete config["rotation"] + delete config["wayHandling"] + + } + + for (const mapRenderingElement of config.mapRendering) { + if (mapRenderingElement["iconOverlays"] !== undefined) { + mapRenderingElement["iconBadges"] = mapRenderingElement["iconOverlays"] + } + for (const overlay of mapRenderingElement["iconBadges"] ?? []) { + if (overlay["badge"] !== true) { + console.log("Warning: non-overlay element for ", config.id) + } + delete overlay["badge"] + } + } + + } + + + /** + * Given an old (parsed) JSON-config, will (in place) fix some issues + * @param oldThemeConfig: the config to update to the latest format + */ + public static fixThemeConfig(oldThemeConfig: any): void { + for (const layerConfig of oldThemeConfig.layers ?? []) { + if (typeof layerConfig === "string" || layerConfig["builtin"] !== undefined) { + continue + } + // @ts-ignore + LegacyJsonConvert.fixLayerConfig(layerConfig) + } + + if (oldThemeConfig["roamingRenderings"] !== undefined && oldThemeConfig["roamingRenderings"].length == 0) { + delete oldThemeConfig["roamingRenderings"] + } + } + + +} \ No newline at end of file diff --git a/assets/layers/waste_basket/waste_basket.json b/assets/layers/waste_basket/waste_basket.json index 71d686f30..3e89c9e5a 100644 --- a/assets/layers/waste_basket/waste_basket.json +++ b/assets/layers/waste_basket/waste_basket.json @@ -90,8 +90,7 @@ "id": "dispensing_dog_bags", "question": { "en": "Does this waste basket have a dispenser for dog excrement bags?", - "nl": "Heeft deze vuilnisbak een verdeler voor hondenpoepzakjes?", - "then": "Heeft deze vuilnisbak een verdeler voor hondenpoepzakjes?" + "nl": "Heeft deze vuilnisbak een verdeler voor hondenpoepzakjes?" }, "condition": { "or": [ diff --git a/assets/themes/grb_import/grb.json b/assets/themes/grb_import/grb.json index c79f6e5fe..057bd5080 100644 --- a/assets/themes/grb_import/grb.json +++ b/assets/themes/grb_import/grb.json @@ -555,6 +555,7 @@ { "id": "GRB", "source": { + "osmTags": "HUISNR~*", "geoJson": "https://betadata.grbosm.site/grb?bbox={x_min},{y_min},{x_max},{y_max}", "geoJsonZoomLevel": 18, "mercatorCrs": true, diff --git a/index.ts b/index.ts index a383eaac2..3ff564ca9 100644 --- a/index.ts +++ b/index.ts @@ -1,7 +1,6 @@ import {FixedUiElement} from "./UI/Base/FixedUiElement"; import {QueryParameters} from "./Logic/Web/QueryParameters"; import Combine from "./UI/Base/Combine"; -import ValidatedTextField from "./UI/Input/ValidatedTextField"; import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers"; import MinimapImplementation from "./UI/Base/MinimapImplementation"; import CountryCoder from "latlon2country/index"; @@ -70,7 +69,6 @@ class Init { window.mapcomplete_state = State.state; new DefaultGUI(State.state, guiState) - if (encoded !== undefined && encoded.length > 10) { // We save the layout to the user settings and local storage State.state.osmConnection.OnLoggedIn(() => { @@ -78,13 +76,8 @@ class Init { .GetLongPreference("installed-theme-" + layoutToUse.id) .setData(encoded); }); - } - - } - - } diff --git a/scripts/generateTranslations.ts b/scripts/generateTranslations.ts index 788060549..760fbbd4e 100644 --- a/scripts/generateTranslations.ts +++ b/scripts/generateTranslations.ts @@ -31,6 +31,9 @@ class TranslationPart { if (!translations.hasOwnProperty(translationsKey)) { continue; } + if(translationsKey == "then"){ + throw "Suspicious translation at "+context + } const v = translations[translationsKey] if (typeof (v) != "string") { console.error("Non-string object in translation while trying to add more translations to '", translationsKey, "': ", v) diff --git a/scripts/lint.ts b/scripts/lint.ts index 565903c23..e883fa3ae 100644 --- a/scripts/lint.ts +++ b/scripts/lint.ts @@ -1,114 +1,23 @@ -/* - * This script reads all theme and layer files and reformats them inplace - * Use with caution, make a commit beforehand! - */ - import ScriptUtils from "./ScriptUtils"; import {writeFileSync} from "fs"; import {LayerConfigJson} from "../Models/ThemeConfig/Json/LayerConfigJson"; import LineRenderingConfigJson from "../Models/ThemeConfig/Json/LineRenderingConfigJson"; +import LegacyJsonConvert from "../Models/ThemeConfig/LegacyJsonConvert"; -/** - * In place fix +/* + * This script reads all theme and layer files and reformats them inplace + * Use with caution, make a commit beforehand! */ -function fixLayerConfig(config: LayerConfigJson): void { - if(config["overpassTags"]){ - config.source.osmTags = config["overpassTags"] - delete config["overpassTags"] - } - - if (config.tagRenderings !== undefined) { - for (const tagRendering of config.tagRenderings) { - if (tagRendering["#"] !== undefined) { - tagRendering["id"] = tagRendering["#"] - delete tagRendering["#"] - } - if (tagRendering["id"] === undefined) { - if (tagRendering["freeform"]?.key !== undefined) { - tagRendering["id"] = config.id + "-" + tagRendering["freeform"]["key"] - } - } - } - } - - if (config.mapRendering === undefined && config.id !== "sidewalks") { - // This is a legacy format, lets create a pointRendering - let location: ("point" | "centroid")[] = ["point"] - let wayHandling: number = config["wayHandling"] ?? 0 - if (wayHandling === 2) { - location = ["point", "centroid"] - } - config.mapRendering = [ - { - icon: config["icon"], - iconBadges: config["iconOverlays"], - label: config["label"], - iconSize: config["iconSize"], - location, - rotation: config["rotation"] - } - ] - - if (wayHandling !== 1) { - const lineRenderConfig = { - color: config["color"], - width: config["width"], - dashArray: config["dashArray"] - } - if (Object.keys(lineRenderConfig).length > 0) { - config.mapRendering.push(lineRenderConfig) - } - } - - - delete config["color"] - delete config["width"] - delete config["dashArray"] - - delete config["icon"] - delete config["iconOverlays"] - delete config["label"] - delete config["iconSize"] - delete config["rotation"] - delete config["wayHandling"] - - } - - for (const mapRenderingElement of config.mapRendering) { - if (mapRenderingElement["iconOverlays"] !== undefined) { - mapRenderingElement["iconBadges"] = mapRenderingElement["iconOverlays"] - } - for (const overlay of mapRenderingElement["iconBadges"] ?? []) { - if (overlay["badge"] !== true) { - console.log("Warning: non-overlay element for ", config.id) - } - delete overlay["badge"] - } - } - -} const layerFiles = ScriptUtils.getLayerFiles(); for (const layerFile of layerFiles) { - fixLayerConfig(layerFile.parsed) + LegacyJsonConvert. fixLayerConfig(layerFile.parsed) writeFileSync(layerFile.path, JSON.stringify(layerFile.parsed, null, " ")) } const themeFiles = ScriptUtils.getThemeFiles() for (const themeFile of themeFiles) { - for (const layerConfig of themeFile.parsed.layers ?? []) { - if (typeof layerConfig === "string" || layerConfig["builtin"] !== undefined) { - continue - } - // @ts-ignore - fixLayerConfig(layerConfig) - } - - if (themeFile.parsed["roamingRenderings"] !== undefined && themeFile.parsed["roamingRenderings"].length == 0) { - delete themeFile.parsed["roamingRenderings"] - } - + LegacyJsonConvert.fixThemeConfig(themeFile.parsed) writeFileSync(themeFile.path, JSON.stringify(themeFile.parsed, null, " ")) } -//*/ From a2306c2c6f670e87defca53bbc20a20a68cfec51 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 29 Oct 2021 03:42:33 +0200 Subject: [PATCH 45/95] Add the possibility to define a postfix and prefix for opening hours --- Docs/CalculatedTags.md | 2 +- Docs/SpecialInputElements.md | 68 +++++- Docs/SpecialRenderings.md | 8 +- UI/Input/InputElementMap.ts | 2 +- UI/Input/ValidatedTextField.ts | 217 +++++++++++++------ UI/OpeningHours/OpeningHoursInput.ts | 38 +++- UI/OpeningHours/OpeningHoursVisualization.ts | 10 +- UI/SpecialVisualizations.ts | 11 +- UI/SubstitutedTranslation.ts | 4 +- assets/layers/bike_shop/bike_shop.json | 2 +- 10 files changed, 281 insertions(+), 81 deletions(-) diff --git a/Docs/CalculatedTags.md b/Docs/CalculatedTags.md index b9bfe2104..9e0ce6fa7 100644 --- a/Docs/CalculatedTags.md +++ b/Docs/CalculatedTags.md @@ -100,7 +100,7 @@ Adds the time that the data got loaded - pretty much the time of downloading fro -### _last_edit:contributor, _last_edit:contributor:uid, _last_edit:changeset, _last_edit:timestamp, _version_number +### _last_edit:contributor, _last_edit:contributor:uid, _last_edit:changeset, _last_edit:timestamp, _version_number, _backend diff --git a/Docs/SpecialInputElements.md b/Docs/SpecialInputElements.md index 3c05b4c87..5a70109de 100644 --- a/Docs/SpecialInputElements.md +++ b/Docs/SpecialInputElements.md @@ -24,7 +24,40 @@ A geographical length in meters (rounded at two points). Will give an extra mini ## wikidata -A wikidata identifier, e.g. Q42. Input helper arguments: [ key: the value of this tag will initialize search (default: name), options: { removePrefixes: string[], removePostfixes: string[] } these prefixes and postfixes will be removed from the initial search value] +A wikidata identifier, e.g. Q42. +### Helper arguments + + + +name | doc +------ | ----- +key | the value of this tag will initialize search (default: name) +options | A JSON-object of type `{ removePrefixes: string[], removePostfixes: string[] }`. + +subarg | doc +-------- | ----- +removePrefixes | remove these snippets of text from the start of the passed string to search +removePostfixes | remove these snippets of text from the end of the passed string to search + + +### Example usage + + The following is the 'freeform'-part of a layer config which will trigger a search for the wikidata item corresponding with the name of the selected feature. It will also remove '-street', '-square', ... if found at the end of the name```"freeform": { + "key": "name:etymology:wikidata", + "type": "wikidata", + "helperArgs": [ + "name", + { + "removePostfixes": [ + "street", + "boulevard", + "path", + "square", + "plaza", + ] + } + ] + },``` ## int @@ -60,7 +93,38 @@ A phone number ## opening_hours -Has extra elements to easily input when a POI is opened +Has extra elements to easily input when a POI is opened. +### Helper arguments + + + +name | doc +------ | ----- +options | A JSON-object of type `{ prefix: string, postfix: string }`. + +subarg | doc +-------- | ----- +prefix | Piece of text that will always be added to the front of the generated opening hours. If the OSM-data does not start with this, it will fail to parse +postfix | Piece of text that will always be added to the end of the generated opening hours + + +### Example usage + + To add a conditional (based on time) access restriction: + +``` + "freeform": { + "key": "access:conditional", + "type": "opening_hours", + "helperArgs": [ + { + "prefix":"no @ (", + "postfix":")" + } + ] + },``` + +*Don't forget to pass these in the rendering as well*: `{opening_hours_table(opening_hours,yes @ &LPARENS, &RPARENS )` ## color diff --git a/Docs/SpecialRenderings.md b/Docs/SpecialRenderings.md index 7fff50797..77b5592e3 100644 --- a/Docs/SpecialRenderings.md +++ b/Docs/SpecialRenderings.md @@ -14,11 +14,11 @@ name | default | description ------ | --------- | ------------- -image key/prefix (multiple values allowed if comma-seperated) | image | The keys given to the images, e.g. if image is given, the first picture URL will be added as image, the second as image:0, the third as image:1, etc... +image key/prefix (multiple values allowed if comma-seperated) | image,mapillary,image,wikidata,wikimedia_commons,image,image | The keys given to the images, e.g. if image is given, the first picture URL will be added as image, the second as image:0, the third as image:1, etc... #### Example usage - `{image_carousel(image)}` + `{image_carousel(image,mapillary,image,wikidata,wikimedia_commons,image,image)}` ### image_upload Creates a button where a user can upload an image to IMGUR @@ -73,10 +73,12 @@ fallback | undefined | The identifier to use, if tags[subjectKey] as spec name | default | description ------ | --------- | ------------- key | opening_hours | The tagkey from which the table is constructed. +prefix | | Remove this string from the start of the value before parsing. __Note: use `&LPARENs` to indicate `(` if needed__ +postfix | | Remove this string from the end of the value before parsing. __Note: use `&RPARENs` to indicate `)` if needed__ #### Example usage - `{opening_hours_table(opening_hours)}` + A normal opening hours table can be invoked with `{opening_hours_table()}`. A table for e.g. conditional access with opening hours can be `{opening_hours_table(access:conditional, no @ &LPARENS, &RPARENS)}` ### live Downloads a JSON from the given URL, e.g. '{live(example.org/data.json, shorthand:x.y.z, other:a.b.c, shorthand)}' will download the given file, will create an object {shorthand: json[x][y][z], other: json[a][b][c] out of it and will return 'other' or 'json[a][b][c]. This is made to use in combination with tags, e.g. {live({url}, {url:format}, needed_value)} diff --git a/UI/Input/InputElementMap.ts b/UI/Input/InputElementMap.ts index 548e50363..a2a50f9d3 100644 --- a/UI/Input/InputElementMap.ts +++ b/UI/Input/InputElementMap.ts @@ -25,8 +25,8 @@ export default class InputElementMap extends InputElement { const self = this; this._value = inputElement.GetValue().map( (t => { - const currentX = self.GetValue()?.data; const newX = toX(t); + const currentX = self.GetValue()?.data; if (isSame(currentX, newX)) { return currentX; } diff --git a/UI/Input/ValidatedTextField.ts b/UI/Input/ValidatedTextField.ts index ad5554346..71db4a5b3 100644 --- a/UI/Input/ValidatedTextField.ts +++ b/UI/Input/ValidatedTextField.ts @@ -19,6 +19,9 @@ import {FixedInputElement} from "./FixedInputElement"; import WikidataSearchBox from "../Wikipedia/WikidataSearchBox"; import Wikidata from "../../Logic/Web/Wikidata"; import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"; +import Table from "../Base/Table"; +import Combine from "../Base/Combine"; +import Title from "../Base/Title"; interface TextFieldDef { name: string, @@ -28,12 +31,159 @@ interface TextFieldDef { inputHelper?: (value: UIEventSource, options?: { location: [number, number], mapBackgroundLayer?: UIEventSource, - args: (string | number | boolean)[] + args: (string | number | boolean | any)[] feature?: any }) => InputElement, inputmode?: string } +class WikidataTextField implements TextFieldDef { + name = "wikidata" + explanation = + new Combine([ + "A wikidata identifier, e.g. Q42.", + new Title("Helper arguments"), + new Table(["name", "doc"], + [ + ["key", "the value of this tag will initialize search (default: name)"], + ["options", new Combine(["A JSON-object of type `{ removePrefixes: string[], removePostfixes: string[] }`.", + new Table( + ["subarg", "doc"], + [["removePrefixes", "remove these snippets of text from the start of the passed string to search"], + ["removePostfixes", "remove these snippets of text from the end of the passed string to search"], + ] + )]) + ]]), + new Title("Example usage"), + "The following is the 'freeform'-part of a layer config which will trigger a search for the wikidata item corresponding with the name of the selected feature. It will also remove '-street', '-square', ... if found at the end of the name```" + `"freeform": { + "key": "name:etymology:wikidata", + "type": "wikidata", + "helperArgs": [ + "name", + { + "removePostfixes": [ + "street", + "boulevard", + "path", + "square", + "plaza", + ] + } + ] + },` + "```" + ]).AsMarkdown() + + + public isValid(str) { + + if (str === undefined) { + return false; + } + if (str.length <= 2) { + return false; + } + return !str.split(";").some(str => Wikidata.ExtractKey(str) === undefined) + } + + public reformat(str) { + if (str === undefined) { + return undefined; + } + let out = str.split(";").map(str => Wikidata.ExtractKey(str)).join("; ") + if (str.endsWith(";")) { + out = out + ";" + } + return out; + } + + public inputHelper(currentValue, inputHelperOptions) { + const args = inputHelperOptions.args ?? [] + const searchKey = args[0] ?? "name" + + let searchFor = inputHelperOptions.feature?.properties[searchKey]?.toLowerCase() + + const options = args[1] + if (searchFor !== undefined && options !== undefined) { + const prefixes = options["removePrefixes"] + const postfixes = options["removePostfixes"] + for (const postfix of postfixes ?? []) { + if (searchFor.endsWith(postfix)) { + searchFor = searchFor.substring(0, searchFor.length - postfix.length) + break; + } + } + + for (const prefix of prefixes ?? []) { + if (searchFor.startsWith(prefix)) { + searchFor = searchFor.substring(prefix.length) + break; + } + } + + } + + return new WikidataSearchBox({ + value: currentValue, + searchText: new UIEventSource(searchFor) + }) + } +} + +class OpeningHoursTextField implements TextFieldDef { + name = "opening_hours" + explanation = + new Combine([ + "Has extra elements to easily input when a POI is opened.", + new Title("Helper arguments"), + new Table(["name", "doc"], + [ + ["options", new Combine([ + "A JSON-object of type `{ prefix: string, postfix: string }`. ", + new Table(["subarg", "doc"], + [ + ["prefix", "Piece of text that will always be added to the front of the generated opening hours. If the OSM-data does not start with this, it will fail to parse"], + ["postfix", "Piece of text that will always be added to the end of the generated opening hours"], + ]) + + ]) + ] + ]), + new Title("Example usage"), + "To add a conditional (based on time) access restriction:\n\n```" + ` + "freeform": { + "key": "access:conditional", + "type": "opening_hours", + "helperArgs": [ + { + "prefix":"no @ (", + "postfix":")" + } + ] + },` + "```\n\n*Don't forget to pass these in the rendering as well*: `{opening_hours_table(opening_hours,yes @ &LPARENS, &RPARENS )`"]).AsMarkdown() + + + isValid() { + return true + } + + reformat(str) { + return str + } + + inputHelper(value: UIEventSource, inputHelperOptions: { + location: [number, number], + mapBackgroundLayer?: UIEventSource, + args: (string | number | boolean | any)[] + feature?: any + }) { + const args = (inputHelperOptions.args ?? [])[0] + const prefix = args?.prefix ?? "" + const postfix = args?.postfix ?? "" + + return new OpeningHoursInput(value, prefix, postfix) + } +} + export default class ValidatedTextField { public static tpList: TextFieldDef[] = [ @@ -146,60 +296,7 @@ export default class ValidatedTextField { }, "decimal" ), - ValidatedTextField.tp( - "wikidata", - "A wikidata identifier, e.g. Q42. Input helper arguments: [ key: the value of this tag will initialize search (default: name), options: { removePrefixes: string[], removePostfixes: string[] } these prefixes and postfixes will be removed from the initial search value]", - (str) => { - if (str === undefined) { - return false; - } - if(str.length <= 2){ - return false; - } - return !str.split(";").some(str => Wikidata.ExtractKey(str) === undefined) - }, - (str) => { - if (str === undefined) { - return undefined; - } - let out = str.split(";").map(str => Wikidata.ExtractKey(str)).join("; ") - if(str.endsWith(";")){ - out = out + ";" - } - return out; - }, - (currentValue, inputHelperOptions) => { - const args = inputHelperOptions.args ?? [] - const searchKey = args[0] ?? "name" - - let searchFor = inputHelperOptions.feature?.properties[searchKey]?.toLowerCase() - - const options = args[1] - if (searchFor !== undefined && options !== undefined) { - const prefixes = options["removePrefixes"] - const postfixes = options["removePostfixes"] - for (const postfix of postfixes ?? []) { - if (searchFor.endsWith(postfix)) { - searchFor = searchFor.substring(0, searchFor.length - postfix.length) - break; - } - } - - for (const prefix of prefixes ?? []) { - if (searchFor.startsWith(prefix)) { - searchFor = searchFor.substring(prefix.length) - break; - } - } - - } - - return new WikidataSearchBox({ - value: currentValue, - searchText: new UIEventSource(searchFor) - }) - } - ), + new WikidataTextField(), ValidatedTextField.tp( "int", @@ -299,15 +396,7 @@ export default class ValidatedTextField { undefined, "tel" ), - ValidatedTextField.tp( - "opening_hours", - "Has extra elements to easily input when a POI is opened", - () => true, - str => str, - (value) => { - return new OpeningHoursInput(value); - } - ), + new OpeningHoursTextField(), ValidatedTextField.tp( "color", "Shows a color picker", diff --git a/UI/OpeningHours/OpeningHoursInput.ts b/UI/OpeningHours/OpeningHoursInput.ts index d8d5ce16b..88b061a3d 100644 --- a/UI/OpeningHours/OpeningHoursInput.ts +++ b/UI/OpeningHours/OpeningHoursInput.ts @@ -23,11 +23,39 @@ export default class OpeningHoursInput extends InputElement { private readonly _value: UIEventSource; private readonly _element: BaseUIElement; - constructor(value: UIEventSource = new UIEventSource("")) { + constructor(value: UIEventSource = new UIEventSource(""), prefix = "", postfix = "") { super(); this._value = value; + let valueWithoutPrefix = value + if (prefix !== "" && postfix !== "") { + + valueWithoutPrefix = value.map(str => { + if (str === undefined) { + return undefined; + } + if(str === ""){ + return "" + } + if (str.startsWith(prefix) && str.endsWith(postfix)) { + return str.substring(prefix.length, str.length - postfix.length) + } + return str + }, [], noPrefix => { + if (noPrefix === undefined) { + return undefined; + } + if(noPrefix === ""){ + return "" + } + if (noPrefix.startsWith(prefix) && noPrefix.endsWith(postfix)) { + return noPrefix + } - const leftoverRules = value.map(str => { + return prefix + noPrefix + postfix + }) + } + + const leftoverRules = valueWithoutPrefix.map(str => { if (str === undefined) { return [] } @@ -45,9 +73,9 @@ export default class OpeningHoursInput extends InputElement { return leftOvers; }) // Note: MUST be bound AFTER the leftover rules! - const rulesFromOhPicker = value.map(OH.Parse); + const rulesFromOhPicker = valueWithoutPrefix.map(OH.Parse); - const ph = value.map(str => { + const ph = valueWithoutPrefix.map(str => { if (str === undefined) { return "" } @@ -68,7 +96,7 @@ export default class OpeningHoursInput extends InputElement { ...leftoverRules.data, ph.data ] - value.setData(Utils.NoEmpty(rules).join(";")); + valueWithoutPrefix.setData(Utils.NoEmpty(rules).join(";")); } rulesFromOhPicker.addCallback(update); diff --git a/UI/OpeningHours/OpeningHoursVisualization.ts b/UI/OpeningHours/OpeningHoursVisualization.ts index 785d046a9..fb8ae05d6 100644 --- a/UI/OpeningHours/OpeningHoursVisualization.ts +++ b/UI/OpeningHours/OpeningHoursVisualization.ts @@ -23,10 +23,16 @@ export default class OpeningHoursVisualization extends Toggle { Translations.t.general.weekdays.abbreviations.sunday, ] - constructor(tags: UIEventSource, key: string) { + constructor(tags: UIEventSource, key: string, prefix = "", postfix = "") { const tagsDirect = tags.data; const ohTable = new VariableUiElement(tags - .map(tags => tags[key]) // This mapping will absorb all other changes to tags in order to prevent regeneration + .map(tags => { + const value : string = tags[key]; + if(value.startsWith(prefix) && value.endsWith(postfix)){ + return value.substring(prefix.length, value.length - postfix.length) + } + return value; + }) // This mapping will absorb all other changes to tags in order to prevent regeneration .map(ohtext => { try { // noinspection JSPotentiallyInvalidConstructorUsage diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index 680b3e571..fa3362107 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -253,9 +253,18 @@ export default class SpecialVisualizations { name: "key", defaultValue: "opening_hours", doc: "The tagkey from which the table is constructed." + },{ + name: "prefix", + defaultValue: "", + doc:"Remove this string from the start of the value before parsing. __Note: use `&LPARENs` to indicate `(` if needed__" + },{ + name: "postfix", + defaultValue: "", + doc:"Remove this string from the end of the value before parsing. __Note: use `&RPARENs` to indicate `)` if needed__" }], + example: "A normal opening hours table can be invoked with `{opening_hours_table()}`. A table for e.g. conditional access with opening hours can be `{opening_hours_table(access:conditional, no @ &LPARENS, &RPARENS)}`", constr: (state: State, tagSource: UIEventSource, args) => { - return new OpeningHoursVisualization(tagSource, args[0]) + return new OpeningHoursVisualization(tagSource, args[0], args[1], args[2]) } }, { diff --git a/UI/SubstitutedTranslation.ts b/UI/SubstitutedTranslation.ts index 361540e65..ff2d3a447 100644 --- a/UI/SubstitutedTranslation.ts +++ b/UI/SubstitutedTranslation.ts @@ -85,7 +85,9 @@ export class SubstitutedTranslation extends VariableUiElement { const partAfter = SubstitutedTranslation.ExtractSpecialComponents(matched[4], extraMappings); const args = knownSpecial.args.map(arg => arg.defaultValue ?? ""); if (argument.length > 0) { - const realArgs = argument.split(",").map(str => str.trim()); + const realArgs = argument.split(",").map(str => str.trim() + .replace(/&LPARENS/g, '(') + .replace(/&RPARENS/g, ')')); for (let i = 0; i < realArgs.length; i++) { if (args.length <= i) { args.push(realArgs[i]); diff --git a/assets/layers/bike_shop/bike_shop.json b/assets/layers/bike_shop/bike_shop.json index 6cd8890c3..1be9adee9 100644 --- a/assets/layers/bike_shop/bike_shop.json +++ b/assets/layers/bike_shop/bike_shop.json @@ -298,7 +298,7 @@ "id": "bike_shop-email" }, { - "render": "{opening_hours_table(opening_hours)}", + "render": "{opening_hours_table()}", "question": "When is this shop opened?", "freeform": { "key": "opening_hours", From 219f4777487f0fea5a8aa96bc44ef036aac31d53 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 29 Oct 2021 03:45:02 +0200 Subject: [PATCH 46/95] Regenerate docs --- .../mapcomplete_charging_stations.json | 90 +++++++++---------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/Docs/TagInfo/mapcomplete_charging_stations.json b/Docs/TagInfo/mapcomplete_charging_stations.json index c4fcc5c4d..d1c40a915 100644 --- a/Docs/TagInfo/mapcomplete_charging_stations.json +++ b/Docs/TagInfo/mapcomplete_charging_stations.json @@ -543,129 +543,129 @@ }, { "key": "planned:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.", "value": "" }, { "key": "construction:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.", "value": "" }, { "key": "disused:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.", "value": "" }, { "key": "operational_status", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.", - "value": "" - }, - { - "key": "amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "charging_station" - }, - { - "key": "planned:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.", - "value": "" - }, - { - "key": "construction:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.", - "value": "" - }, - { - "key": "disused:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.", - "value": "" - }, - { - "key": "operational_status", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "broken" }, { "key": "amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "charging_station" }, { "key": "planned:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "charging_station" }, { "key": "construction:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.", "value": "" }, { "key": "disused:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.", "value": "" }, { "key": "operational_status", - "description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.", + "description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.", "value": "" }, { "key": "amenity", - "description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key amenity.", "value": "" }, { "key": "planned:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.", "value": "" }, { "key": "construction:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "charging_station" }, { "key": "disused:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.", "value": "" }, { "key": "operational_status", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.", "value": "" }, { "key": "amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key amenity.", "value": "" }, { "key": "planned:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.", "value": "" }, { "key": "construction:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.", "value": "" }, { "key": "disused:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "charging_station" }, { "key": "operational_status", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.", "value": "" }, { "key": "amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key amenity.", "value": "" }, + { + "key": "planned:amenity", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.", + "value": "" + }, + { + "key": "construction:amenity", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.", + "value": "" + }, + { + "key": "disused:amenity", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.", + "value": "" + }, + { + "key": "operational_status", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.", + "value": "" + }, + { + "key": "amenity", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "charging_station" + }, { "key": "parking:fee", "description": "Layer 'Charging stations' shows parking:fee=no with a fixed text, namely 'No additional parking cost while charging' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", From 28429df81974ec017016c092c3f88c0efa2f1f13 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 29 Oct 2021 03:59:28 +0200 Subject: [PATCH 47/95] Documentation Formatting --- Docs/SpecialInputElements.md | 11 ++++++++--- UI/Input/ValidatedTextField.ts | 12 ++++++++---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Docs/SpecialInputElements.md b/Docs/SpecialInputElements.md index 5a70109de..34e19aba6 100644 --- a/Docs/SpecialInputElements.md +++ b/Docs/SpecialInputElements.md @@ -42,7 +42,10 @@ removePostfixes | remove these snippets of text from the end of the passed strin ### Example usage - The following is the 'freeform'-part of a layer config which will trigger a search for the wikidata item corresponding with the name of the selected feature. It will also remove '-street', '-square', ... if found at the end of the name```"freeform": { + The following is the 'freeform'-part of a layer config which will trigger a search for the wikidata item corresponding with the name of the selected feature. It will also remove '-street', '-square', ... if found at the end of the name + +``` +"freeform": { "key": "name:etymology:wikidata", "type": "wikidata", "helperArgs": [ @@ -57,7 +60,8 @@ removePostfixes | remove these snippets of text from the end of the passed strin ] } ] - },``` + } + ``` ## int @@ -113,6 +117,7 @@ postfix | Piece of text that will always be added to the end of the generated op To add a conditional (based on time) access restriction: ``` + "freeform": { "key": "access:conditional", "type": "opening_hours", @@ -122,7 +127,7 @@ postfix | Piece of text that will always be added to the end of the generated op "postfix":")" } ] - },``` + }``` *Don't forget to pass these in the rendering as well*: `{opening_hours_table(opening_hours,yes @ &LPARENS, &RPARENS )` diff --git a/UI/Input/ValidatedTextField.ts b/UI/Input/ValidatedTextField.ts index 75cf02e69..7896b52cd 100644 --- a/UI/Input/ValidatedTextField.ts +++ b/UI/Input/ValidatedTextField.ts @@ -55,7 +55,10 @@ class WikidataTextField implements TextFieldDef { )]) ]]), new Title("Example usage"), - "The following is the 'freeform'-part of a layer config which will trigger a search for the wikidata item corresponding with the name of the selected feature. It will also remove '-street', '-square', ... if found at the end of the name```" + `"freeform": { + `The following is the 'freeform'-part of a layer config which will trigger a search for the wikidata item corresponding with the name of the selected feature. It will also remove '-street', '-square', ... if found at the end of the name + +\`\`\` +"freeform": { "key": "name:etymology:wikidata", "type": "wikidata", "helperArgs": [ @@ -70,7 +73,8 @@ class WikidataTextField implements TextFieldDef { ] } ] - },` + "```" + } + \`\`\`` ]).AsMarkdown() @@ -149,7 +153,7 @@ class OpeningHoursTextField implements TextFieldDef { ] ]), new Title("Example usage"), - "To add a conditional (based on time) access restriction:\n\n```" + ` + "To add a conditional (based on time) access restriction:\n\n```\n" + ` "freeform": { "key": "access:conditional", "type": "opening_hours", @@ -159,7 +163,7 @@ class OpeningHoursTextField implements TextFieldDef { "postfix":")" } ] - },` + "```\n\n*Don't forget to pass these in the rendering as well*: `{opening_hours_table(opening_hours,yes @ &LPARENS, &RPARENS )`"]).AsMarkdown() + }` + "```\n\n*Don't forget to pass these in the rendering as well*: `{opening_hours_table(opening_hours,yes @ &LPARENS, &RPARENS )`"]).AsMarkdown() isValid() { From 2d5d3fbd964f219da7723907915ca931b377d8ee Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 29 Oct 2021 13:35:33 +0200 Subject: [PATCH 48/95] Formatting fix --- Docs/SpecialInputElements.md | 55 +++++++++++++++++----------------- UI/Input/ValidatedTextField.ts | 52 ++++++++++++++++---------------- 2 files changed, 54 insertions(+), 53 deletions(-) diff --git a/Docs/SpecialInputElements.md b/Docs/SpecialInputElements.md index 34e19aba6..80e812661 100644 --- a/Docs/SpecialInputElements.md +++ b/Docs/SpecialInputElements.md @@ -46,22 +46,22 @@ removePostfixes | remove these snippets of text from the end of the passed strin ``` "freeform": { - "key": "name:etymology:wikidata", - "type": "wikidata", - "helperArgs": [ - "name", - { - "removePostfixes": [ - "street", - "boulevard", - "path", - "square", - "plaza", - ] - } - ] - } - ``` + "key": "name:etymology:wikidata", + "type": "wikidata", + "helperArgs": [ + "name", + { + "removePostfixes": [ + "street", + "boulevard", + "path", + "square", + "plaza", + ] + } + ] +} +``` ## int @@ -118,18 +118,19 @@ postfix | Piece of text that will always be added to the end of the generated op ``` - "freeform": { - "key": "access:conditional", - "type": "opening_hours", - "helperArgs": [ - { - "prefix":"no @ (", - "postfix":")" - } - ] - }``` +"freeform": { + "key": "access:conditional", + "type": "opening_hours", + "helperArgs": [ + { + "prefix":"no @ (", + "postfix":")" + } + ] +} +``` -*Don't forget to pass these in the rendering as well*: `{opening_hours_table(opening_hours,yes @ &LPARENS, &RPARENS )` +*Don't forget to pass the prefix and postfix in the rendering as well*: `{opening_hours_table(opening_hours,yes @ &LPARENS, &RPARENS )` ## color diff --git a/UI/Input/ValidatedTextField.ts b/UI/Input/ValidatedTextField.ts index 7896b52cd..353851c89 100644 --- a/UI/Input/ValidatedTextField.ts +++ b/UI/Input/ValidatedTextField.ts @@ -59,22 +59,22 @@ class WikidataTextField implements TextFieldDef { \`\`\` "freeform": { - "key": "name:etymology:wikidata", - "type": "wikidata", - "helperArgs": [ - "name", - { - "removePostfixes": [ - "street", - "boulevard", - "path", - "square", - "plaza", - ] - } - ] - } - \`\`\`` + "key": "name:etymology:wikidata", + "type": "wikidata", + "helperArgs": [ + "name", + { + "removePostfixes": [ + "street", + "boulevard", + "path", + "square", + "plaza", + ] + } + ] +} +\`\`\`` ]).AsMarkdown() @@ -154,16 +154,16 @@ class OpeningHoursTextField implements TextFieldDef { ]), new Title("Example usage"), "To add a conditional (based on time) access restriction:\n\n```\n" + ` - "freeform": { - "key": "access:conditional", - "type": "opening_hours", - "helperArgs": [ - { - "prefix":"no @ (", - "postfix":")" - } - ] - }` + "```\n\n*Don't forget to pass these in the rendering as well*: `{opening_hours_table(opening_hours,yes @ &LPARENS, &RPARENS )`"]).AsMarkdown() +"freeform": { + "key": "access:conditional", + "type": "opening_hours", + "helperArgs": [ + { + "prefix":"no @ (", + "postfix":")" + } + ] +}` + "\n```\n\n*Don't forget to pass the prefix and postfix in the rendering as well*: `{opening_hours_table(opening_hours,yes @ &LPARENS, &RPARENS )`"]).AsMarkdown() isValid() { From e4cd93ffb02423bcc32545b3d0e0ed89c25a4de5 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 29 Oct 2021 13:53:00 +0200 Subject: [PATCH 49/95] Documentation updates --- Docs/SpecialInputElements.md | 6 +- Docs/SpecialRenderings.md | 116 +++++++++++++++++++++--------- UI/Input/ValidatedTextField.ts | 6 +- UI/SpecialVisualizations.ts | 19 ++++- assets/themes/grb_import/grb.json | 3 + 5 files changed, 112 insertions(+), 38 deletions(-) diff --git a/Docs/SpecialInputElements.md b/Docs/SpecialInputElements.md index 80e812661..d6648fa3b 100644 --- a/Docs/SpecialInputElements.md +++ b/Docs/SpecialInputElements.md @@ -1,4 +1,8 @@ -# Available types for text fields + + Available types for text fields +================================= + + The listed types here trigger a special input element. Use them in `tagrendering.freeform.type` of your tagrendering to activate them diff --git a/Docs/SpecialRenderings.md b/Docs/SpecialRenderings.md index 48750973a..7480a053c 100644 --- a/Docs/SpecialRenderings.md +++ b/Docs/SpecialRenderings.md @@ -1,13 +1,39 @@ ### Special tag renderings - In a tagrendering, some special values are substituted by an advanced UI-element. This allows advanced features and visualizations to be reused by custom themes or even to query third-party API's. General usage is `{func_name()}`, `{func_name(arg, someotherarg)}` or `{func_name(args):cssStyle}`. Note that you _do not_fcs need to use quotes around your arguments, the comma is enough to separate them. This also implies you cannot use a comma in your args + + +In a tagrendering, some special values are substituted by an advanced UI-element. This allows advanced features and visualizations to be reused by custom themes or even to query third-party API's. + +General usage is `{func_name()}`, `{func_name(arg, someotherarg)}` or `{func_name(args):cssStyle}`. Note that you _do not_ need to use quotes around your arguments, the comma is enough to separate them. This also implies you cannot use a comma in your args + + + + - [all_tags](#all_tags) + - [image_carousel](#image_carousel) + - [image_upload](#image_upload) + - [wikipedia](#wikipedia) + - [minimap](#minimap) + - [sided_minimap](#sided_minimap) + - [reviews](#reviews) + - [opening_hours_table](#opening_hours_table) + - [live](#live) + - [histogram](#histogram) + - [share_link](#share_link) + - [canonical](#canonical) + - [import_button](#import_button) + - [multi_apply](#multi_apply) + + + ### all_tags Prints all key-value pairs of the object - used for debugging #### Example usage - `{all_tags()}` + `{all_tags()}` + + ### image_carousel Creates an image carousel for the given sources. An attempt will be made to guess what source is used. Supported: Wikidata identifiers, Wikipedia pages, Wikimedia categories, IMGUR (with attribution, direct links) @@ -18,7 +44,9 @@ image key/prefix (multiple values allowed if comma-seperated) | image,mapillary, #### Example usage - `{image_carousel(image,mapillary,image,wikidata,wikimedia_commons,image,image)}` + `{image_carousel(image,mapillary,image,wikidata,wikimedia_commons,image,image)}` + + ### image_upload Creates a button where a user can upload an image to IMGUR @@ -30,7 +58,9 @@ label | Add image | The text to show on the button #### Example usage - `{image_upload(image,Add image)}` + `{image_upload(image,Add image)}` + + ### wikipedia A box showing the corresponding wikipedia article - based on the wikidata tag @@ -41,7 +71,9 @@ keyToShowWikipediaFor | wikidata | Use the wikidata entry from this key to show #### Example usage - `{wikipedia()}` is a basic example, `{wikipedia(name:etymology:wikidata)}` to show the wikipedia page of whom the feature was named after. Also remember that these can be styled, e.g. `{wikipedia():max-height: 10rem}` to limit the height + `{wikipedia()}` is a basic example, `{wikipedia(name:etymology:wikidata)}` to show the wikipedia page of whom the feature was named after. Also remember that these can be styled, e.g. `{wikipedia():max-height: 10rem}` to limit the height + + ### minimap A small map showing the selected feature. @@ -53,18 +85,22 @@ idKey | id | (Matches all resting arguments) This argument should be the key of #### Example usage - `{minimap()}`, `{minimap(17, id, _list_of_embedded_feature_ids_calculated_by_calculated_tag):height:10rem; border: 2px solid black}` + `{minimap()}`, `{minimap(17, id, _list_of_embedded_feature_ids_calculated_by_calculated_tag):height:10rem; border: 2px solid black}` + + ### sided_minimap A small map showing _only one side_ the selected feature. *This features requires to have linerenderings with offset* as only linerenderings with a postive or negative offset will be shown. Note: in most cases, this map will be automatically introduced name | default | description ------ | --------- | ------------- -side | undefined | The side to show, either `left` or `right` +side | _undefined_ | The side to show, either `left` or `right` #### Example usage - `{sided_minimap(left)}` + `{sided_minimap(left)}` + + ### reviews Adds an overview of the mangrove-reviews of this object. Mangrove.Reviews needs - in order to identify the reviewed object - a coordinate and a name. By default, the name of the object is given, but this can be overwritten @@ -72,11 +108,13 @@ side | undefined | The side to show, either `left` or `right` name | default | description ------ | --------- | ------------- subjectKey | name | The key to use to determine the subject. If specified, the subject will be tags[subjectKey] -fallback | undefined | The identifier to use, if tags[subjectKey] as specified above is not available. This is effectively a fallback value +fallback | _undefined_ | The identifier to use, if tags[subjectKey] as specified above is not available. This is effectively a fallback value #### Example usage - `{reviews()}` for a vanilla review, `{reviews(name, play_forest)}` to review a play forest. If a name is known, the name will be used as identifier, otherwise 'play_forest' is used + `{reviews()}` for a vanilla review, `{reviews(name, play_forest)}` to review a play forest. If a name is known, the name will be used as identifier, otherwise 'play_forest' is used + + ### opening_hours_table Creates an opening-hours table. Usage: {opening_hours_table(opening_hours)} to create a table of the tag 'opening_hours'. @@ -84,61 +122,71 @@ fallback | undefined | The identifier to use, if tags[subjectKey] as spec name | default | description ------ | --------- | ------------- key | opening_hours | The tagkey from which the table is constructed. -prefix | | Remove this string from the start of the value before parsing. __Note: use `&LPARENs` to indicate `(` if needed__ -postfix | | Remove this string from the end of the value before parsing. __Note: use `&RPARENs` to indicate `)` if needed__ +prefix | _empty string_ | Remove this string from the start of the value before parsing. __Note: use `&LPARENs` to indicate `(` if needed__ +postfix | _empty string_ | Remove this string from the end of the value before parsing. __Note: use `&RPARENs` to indicate `)` if needed__ #### Example usage - A normal opening hours table can be invoked with `{opening_hours_table()}`. A table for e.g. conditional access with opening hours can be `{opening_hours_table(access:conditional, no @ &LPARENS, &RPARENS)}` + A normal opening hours table can be invoked with `{opening_hours_table()}`. A table for e.g. conditional access with opening hours can be `{opening_hours_table(access:conditional, no @ &LPARENS, &RPARENS)}` + + ### live Downloads a JSON from the given URL, e.g. '{live(example.org/data.json, shorthand:x.y.z, other:a.b.c, shorthand)}' will download the given file, will create an object {shorthand: json[x][y][z], other: json[a][b][c] out of it and will return 'other' or 'json[a][b][c]. This is made to use in combination with tags, e.g. {live({url}, {url:format}, needed_value)} name | default | description ------ | --------- | ------------- -Url | undefined | The URL to load -Shorthands | undefined | A list of shorthands, of the format 'shorthandname:path.path.path'. separated by ; -path | undefined | The path (or shorthand) that should be returned +Url | _undefined_ | The URL to load +Shorthands | _undefined_ | A list of shorthands, of the format 'shorthandname:path.path.path'. separated by ; +path | _undefined_ | The path (or shorthand) that should be returned #### Example usage - {live({url},{url:format},hour)} {live(https://data.mobility.brussels/bike/api/counts/?request=live&featureID=CB2105,hour:data.hour_cnt;day:data.day_cnt;year:data.year_cnt,hour)} + {live({url},{url:format},hour)} {live(https://data.mobility.brussels/bike/api/counts/?request=live&featureID=CB2105,hour:data.hour_cnt;day:data.day_cnt;year:data.year_cnt,hour)} + + ### histogram Create a histogram for a list of given values, read from the properties. name | default | description ------ | --------- | ------------- -key | undefined | The key to be read and to generate a histogram from -title | | The text to put above the given values column -countHeader | | The text to put above the counts -colors* | undefined | (Matches all resting arguments - optional) Matches a regex onto a color value, e.g. `3[a-zA-Z+-]*:#33cc33` +key | _undefined_ | The key to be read and to generate a histogram from +title | _empty string_ | The text to put above the given values column +countHeader | _empty string_ | The text to put above the counts +colors* | _undefined_ | (Matches all resting arguments - optional) Matches a regex onto a color value, e.g. `3[a-zA-Z+-]*:#33cc33` #### Example usage - `{histogram('some_key')}` with properties being `{some_key: ['a','b','a','c']} to create a histogram + `{histogram('some_key')}` with properties being `{some_key: ['a','b','a','c']} to create a histogram + + ### share_link Creates a link that (attempts to) open the native 'share'-screen name | default | description ------ | --------- | ------------- -url | undefined | The url to share (default: current URL) +url | _undefined_ | The url to share (default: current URL) #### Example usage - {share_link()} to share the current page, {share_link()} to share the given url + {share_link()} to share the current page, {share_link()} to share the given url + + ### canonical Converts a short, canonical value into the long, translated text name | default | description ------ | --------- | ------------- -key | undefined | The key of the tag to give the canonical text for +key | _undefined_ | The key of the tag to give the canonical text for #### Example usage - {canonical(length)} will give 42 metre (in french) + {canonical(length)} will give 42 metre (in french) + + ### import_button This button will copy the data from an external dataset into OpenStreetMap. It is only functional in official themes but can be tested in unofficial themes. @@ -161,25 +209,27 @@ There are also some technicalities in your theme to keep in mind: name | default | description ------ | --------- | ------------- -tags | undefined | Tags to copy-specification. This contains one or more pairs (seperated by a `;`), e.g. `amenity=fast_food; addr:housenumber=$number`. This new point will then have the tags `amenity=fast_food` and `addr:housenumber` with the value that was saved in `number` in the original feature. (Hint: prepare these values, e.g. with calculatedTags) +tags | _undefined_ | Tags to copy-specification. This contains one or more pairs (seperated by a `;`), e.g. `amenity=fast_food; addr:housenumber=$number`. This new point will then have the tags `amenity=fast_food` and `addr:housenumber` with the value that was saved in `number` in the original feature. (Hint: prepare these values, e.g. with calculatedTags) text | Import this data into OpenStreetMap | The text to show on the button icon | ./assets/svg/addSmall.svg | A nice icon to show in the button minzoom | 18 | How far the contributor must zoom in before being able to import the point #### Example usage - `{import_button(,Import this data into OpenStreetMap,./assets/svg/addSmall.svg,18)}` + `{import_button(,Import this data into OpenStreetMap,./assets/svg/addSmall.svg,18)}` + + ### multi_apply A button to apply the tagging of this object onto a list of other features. This is an advanced feature for which you'll need calculatedTags name | default | description ------ | --------- | ------------- -feature_ids | undefined | A JSOn-serialized list of IDs of features to apply the tagging on -keys | undefined | One key (or multiple keys, seperated by ';') of the attribute that should be copied onto the other features. -text | undefined | The text to show on the button -autoapply | undefined | A boolean indicating wether this tagging should be applied automatically if the relevant tags on this object are changed. A visual element indicating the multi_apply is still shown -overwrite | undefined | If set to 'true', the tags on the other objects will always be overwritten. The default behaviour will be to only change the tags on other objects if they are either undefined or had the same value before the change +feature_ids | _undefined_ | A JSOn-serialized list of IDs of features to apply the tagging on +keys | _undefined_ | One key (or multiple keys, seperated by ';') of the attribute that should be copied onto the other features. +text | _undefined_ | The text to show on the button +autoapply | _undefined_ | A boolean indicating wether this tagging should be applied automatically if the relevant tags on this object are changed. A visual element indicating the multi_apply is still shown +overwrite | _undefined_ | If set to 'true', the tags on the other objects will always be overwritten. The default behaviour will be to only change the tags on other objects if they are either undefined or had the same value before the change #### Example usage diff --git a/UI/Input/ValidatedTextField.ts b/UI/Input/ValidatedTextField.ts index 353851c89..77cc5c433 100644 --- a/UI/Input/ValidatedTextField.ts +++ b/UI/Input/ValidatedTextField.ts @@ -552,7 +552,11 @@ export default class ValidatedTextField { public static HelpText(): string { const explanations = ValidatedTextField.tpList.map(type => ["## " + type.name, "", type.explanation].join("\n")).join("\n\n") - return "# Available types for text fields\n\nThe listed types here trigger a special input element. Use them in `tagrendering.freeform.type` of your tagrendering to activate them\n\n" + explanations + return new Combine([ + new Title("Available types for text fields", 1), + "The listed types here trigger a special input element. Use them in `tagrendering.freeform.type` of your tagrendering to activate them", + explanations + ]).SetClass("flex flex-col").AsMarkdown() } private static tp(name: string, diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index 8d52fbf78..0f13f5df5 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -31,6 +31,8 @@ import SimpleMetaTagger from "../Logic/SimpleMetaTagger"; import MultiApply from "./Popup/MultiApply"; import AllKnownLayers from "../Customizations/AllKnownLayers"; import ShowDataLayer from "./ShowDataLayer/ShowDataLayer"; +import Link from "./Base/Link"; +import List from "./Base/List"; export interface SpecialVisualization { funcName: string, @@ -611,7 +613,13 @@ There are also some technicalities in your theme to keep in mind: new Title(viz.funcName, 3), viz.docs, viz.args.length > 0 ? new Table(["name", "default", "description"], - viz.args.map(arg => [arg.name, arg.defaultValue ?? "undefined", arg.doc]) + viz.args.map(arg => { + let defaultArg = arg.defaultValue ?? "_undefined_" + if(defaultArg == ""){ + defaultArg = "_empty string_" + } + return [arg.name, defaultArg, arg.doc]; + }) ) : undefined, new Title("Example usage", 4), new FixedUiElement( @@ -621,13 +629,18 @@ There are also some technicalities in your theme to keep in mind: ] )); + + const toc = new List( + SpecialVisualizations.specialVisualizations.map(viz => new Link(viz.funcName, "#"+viz.funcName)) + ) return new Combine([ new Title("Special tag renderings", 3), "In a tagrendering, some special values are substituted by an advanced UI-element. This allows advanced features and visualizations to be reused by custom themes or even to query third-party API's.", - "General usage is `{func_name()}`, `{func_name(arg, someotherarg)}` or `{func_name(args):cssStyle}`. Note that you _do not_fcs need to use quotes around your arguments, the comma is enough to separate them. This also implies you cannot use a comma in your args", + "General usage is `{func_name()}`, `{func_name(arg, someotherarg)}` or `{func_name(args):cssStyle}`. Note that you _do not_ need to use quotes around your arguments, the comma is enough to separate them. This also implies you cannot use a comma in your args", + toc, ...helpTexts ] - ); + ).SetClass("flex flex-col"); } } \ No newline at end of file diff --git a/assets/themes/grb_import/grb.json b/assets/themes/grb_import/grb.json index 057bd5080..69e4673d5 100644 --- a/assets/themes/grb_import/grb.json +++ b/assets/themes/grb_import/grb.json @@ -573,6 +573,9 @@ "_imported_osm_still_fresh= feat.properties['_osm_obj:source:date'] == feat.properties._grb_date" ], "tagRenderings": [ + { + "render": "{import_button(Upload this geometry to OpenStreetMap)}" + }, "all_tags" ], "isShown": { From 5941c41bbb38b45a280178b69894cf9ba32a840a Mon Sep 17 00:00:00 2001 From: Tobias Date: Fri, 29 Oct 2021 16:32:01 +0200 Subject: [PATCH 50/95] Readme: Add link to theme list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I am always looking for this link as an entry point to the theme list … --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 566ab402a..78de006ea 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ It is possible to quickly make and distribute your own theme ## Examples +- [An overview of all official themes](https://pietervdvn.github.io/mc/develop/index.html). - [Buurtnatuur.be](http://buurtnatuur.be), developed for the Belgian [Green party](https://www.groen.be/). They also funded the initial development! - [Cyclofix](https://pietervdvn.github.io/MapComplete/index.html?layout=cyclofix), further development @@ -43,7 +44,7 @@ It is possible to quickly make and distribute your own theme - [Map of Maps](https://pietervdvn.github.io/MapComplete/index.html?layout=maps&z=14&lat=50.650&lon=4.2668#element), after a tweet -There are plenty more. Discover them in the app. +There are plenty more. [Discover them in the app](https://pietervdvn.github.io/mc/develop/index.html). ### Statistics From 7f973a8b8d715ada097e9bb85b608ede5f18df20 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Fri, 29 Oct 2021 16:35:29 +0200 Subject: [PATCH 51/95] Use production instance --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 78de006ea..6f05efe8f 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ It is possible to quickly make and distribute your own theme - [Map of Maps](https://pietervdvn.github.io/MapComplete/index.html?layout=maps&z=14&lat=50.650&lon=4.2668#element), after a tweet -There are plenty more. [Discover them in the app](https://pietervdvn.github.io/mc/develop/index.html). +There are plenty more. [Discover them in the app](https://mapcomplete.osm.be/index.html). ### Statistics From da65bbbc864a80f43d37eed8507faac920a1ad95 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 29 Oct 2021 16:38:33 +0200 Subject: [PATCH 52/95] Add createNewWay-action, more work on GRB import theme, add import button --- Logic/ExtraFunction.ts | 6 +- Logic/Osm/Actions/ChangeDescription.ts | 5 +- Logic/Osm/Actions/CreateNewNodeAction.ts | 61 +++++++++++-- Logic/Osm/Actions/CreateNewWayAction.ts | 74 ++++++++++++++++ Logic/Osm/Changes.ts | 92 ++++++++++---------- UI/BigComponents/ImportButton.ts | 106 +++++++++++++++++------ UI/SpecialVisualizations.ts | 64 +++++++++++--- assets/themes/grb_import/grb.json | 30 +++++-- langs/en.json | 3 +- 9 files changed, 341 insertions(+), 100 deletions(-) create mode 100644 Logic/Osm/Actions/CreateNewWayAction.ts diff --git a/Logic/ExtraFunction.ts b/Logic/ExtraFunction.ts index d9fb6ede8..df7e3a585 100644 --- a/Logic/ExtraFunction.ts +++ b/Logic/ExtraFunction.ts @@ -57,13 +57,14 @@ export class ExtraFunction { doc: "Gives a list of features from the specified layer which this feature (partly) overlaps with. " + "If the current feature is a point, all features that embed the point are given. " + "The returned value is `{ feat: GeoJSONFeature, overlap: number}[]` where `overlap` is the overlapping surface are (in m²) for areas, the overlapping length (in meter) if the current feature is a line or `undefined` if the current feature is a point.\n" + + "The resulting list is sorted in descending order by overlap. The feature with the most overlap will thus be the first in the list" + "\n" + "For example to get all objects which overlap or embed from a layer, use `_contained_climbing_routes_properties=feat.overlapWith('climbing_route')`", args: ["...layerIds - one or more layer ids of the layer from which every feature is checked for overlap)"] }, (params, feat) => { return (...layerIds: string[]) => { - const result = [] + const result : {feat:any, overlap: number}[]= [] const bbox = BBox.get(feat) @@ -79,6 +80,9 @@ export class ExtraFunction { result.push(...GeoOperations.calculateOverlap(feat, otherLayer)); } } + + result.sort((a, b) => b.overlap - a.overlap) + return result; } } diff --git a/Logic/Osm/Actions/ChangeDescription.ts b/Logic/Osm/Actions/ChangeDescription.ts index 44804bc32..c8a262d6f 100644 --- a/Logic/Osm/Actions/ChangeDescription.ts +++ b/Logic/Osm/Actions/ChangeDescription.ts @@ -16,7 +16,7 @@ export interface ChangeDescription { /** * The type of the change */ - changeType: "answer" | "create" | "split" | "delete" | "move" | string + changeType: "answer" | "create" | "split" | "delete" | "move" | "import" | string | null /** * THe motivation for the change, e.g. 'deleted because does not exist anymore' */ @@ -51,7 +51,8 @@ export interface ChangeDescription { lat: number, lon: number } | { - // Coordinates are only used for rendering. They should be LAT, LON + /* Coordinates are only used for rendering. They should be LON, LAT + * */ coordinates: [number, number][] nodes: number[], } | { diff --git a/Logic/Osm/Actions/CreateNewNodeAction.ts b/Logic/Osm/Actions/CreateNewNodeAction.ts index e644eb2c6..b8e0f0171 100644 --- a/Logic/Osm/Actions/CreateNewNodeAction.ts +++ b/Logic/Osm/Actions/CreateNewNodeAction.ts @@ -8,20 +8,29 @@ import {GeoOperations} from "../../GeoOperations"; export default class CreateNewNodeAction extends OsmChangeAction { + /** + * Maps previously created points onto their assigned ID, to reuse the point if uplaoded + * "lat,lon" --> id + */ + private static readonly previouslyCreatedPoints = new Map() public newElementId: string = undefined + public newElementIdNumber: number = undefined private readonly _basicTags: Tag[]; private readonly _lat: number; private readonly _lon: number; private readonly _snapOnto: OsmWay; private readonly _reusePointDistance: number; private meta: { changeType: "create" | "import"; theme: string }; + private readonly _reusePreviouslyCreatedPoint: boolean; constructor(basicTags: Tag[], lat: number, lon: number, options: { - snapOnto?: OsmWay, - reusePointWithinMeters?: number, - theme: string, changeType: "create" | "import" }) { + allowReuseOfPreviouslyCreatedPoints?: boolean, + snapOnto?: OsmWay, + reusePointWithinMeters?: number, + theme: string, changeType: "create" | "import" | null + }) { super() this._basicTags = basicTags; this._lat = lat; @@ -31,18 +40,47 @@ export default class CreateNewNodeAction extends OsmChangeAction { } this._snapOnto = options?.snapOnto; this._reusePointDistance = options?.reusePointWithinMeters ?? 1 + this._reusePreviouslyCreatedPoint = options?.allowReuseOfPreviouslyCreatedPoints ?? (basicTags.length === 0) this.meta = { theme: options.theme, changeType: options.changeType } } + public static registerIdRewrites(mappings: Map) { + const toAdd: [string, number][] = [] + + this.previouslyCreatedPoints.forEach((oldId, key) => { + if (!mappings.has("node/" + oldId)) { + return; + } + + const newId = Number(mappings.get("node/" + oldId).substr("node/".length)) + toAdd.push([key, newId]) + }) + for (const [key, newId] of toAdd) { + CreateNewNodeAction.previouslyCreatedPoints.set(key, newId) + } + } + async CreateChangeDescriptions(changes: Changes): Promise { + if (this._reusePreviouslyCreatedPoint) { + + const key = this._lat + "," + this._lon + const prev = CreateNewNodeAction.previouslyCreatedPoints + if (prev.has(key)) { + this.newElementIdNumber = prev.get(key) + this.newElementId = "node/" + this.newElementIdNumber + return [] + } + } + + const id = changes.getNewID() const properties = { id: "node/" + id } - this.newElementId = "node/" + id + this.setElementId(id) for (const kv of this._basicTags) { if (typeof kv.value !== "string") { throw "Invalid value: don't use a regex in a preset" @@ -84,8 +122,7 @@ export default class CreateNewNodeAction extends OsmChangeAction { } if (reusedPointId !== undefined) { console.log("Reusing an existing point:", reusedPointId) - this.newElementId = "node/" + reusedPointId - + this.setElementId(reusedPointId) return [{ tags: new And(this._basicTags).asChange(properties), type: "node", @@ -112,10 +149,20 @@ export default class CreateNewNodeAction extends OsmChangeAction { coordinates: locations, nodes: ids }, - meta:this.meta + meta: this.meta } ] } + private setElementId(id: number) { + this.newElementIdNumber = id; + this.newElementId = "node/"+id + if (!this._reusePreviouslyCreatedPoint) { + return + } + const key = this._lat + "," + this._lon + CreateNewNodeAction.previouslyCreatedPoints.set(key, id) + } + } \ No newline at end of file diff --git a/Logic/Osm/Actions/CreateNewWayAction.ts b/Logic/Osm/Actions/CreateNewWayAction.ts new file mode 100644 index 000000000..0fa9d9912 --- /dev/null +++ b/Logic/Osm/Actions/CreateNewWayAction.ts @@ -0,0 +1,74 @@ +import {ChangeDescription} from "./ChangeDescription"; +import OsmChangeAction from "./OsmChangeAction"; +import {Changes} from "../Changes"; +import {Tag} from "../../Tags/Tag"; +import CreateNewNodeAction from "./CreateNewNodeAction"; +import {And} from "../../Tags/And"; + +export default class CreateNewWayAction extends OsmChangeAction { + public newElementId: string = undefined + private readonly coordinates: ({ nodeId?: number, lat: number, lon: number })[]; + private readonly tags: Tag[]; + private readonly _options: { theme: string }; + + + /*** + * Creates a new way to upload to OSM + * @param tags: the tags to apply to the wya + * @param coordinates: the coordinates. Might have a nodeId, in this case, this node will be used + * @param options + */ + constructor(tags: Tag[], coordinates: ({ nodeId?: number, lat: number, lon: number })[], options: { + theme: string + }) { + super() + this.coordinates = coordinates; + this.tags = tags; + this._options = options; + } + + protected async CreateChangeDescriptions(changes: Changes): Promise { + + const newElements: ChangeDescription[] = [] + + const pointIds: number[] = [] + for (const coordinate of this.coordinates) { + if (coordinate.nodeId !== undefined) { + pointIds.push(coordinate.nodeId) + continue + } + + const newPoint = new CreateNewNodeAction([], coordinate.lat, coordinate.lon, { + changeType: null, + theme: this._options.theme + }) + await changes.applyAction(newPoint) + pointIds.push(newPoint.newElementIdNumber) + } + + // We have all created (or reused) all the points! + // Time to create the actual way + + + const id = changes.getNewID() + + const newWay = { + id, + type: "way", + meta:{ + theme: this._options.theme, + changeType: "import" + }, + tags: new And(this.tags).asChange({}), + changes: { + nodes: pointIds, + coordinates: this.coordinates.map(c => [c.lon, c.lat]) + } + } + newElements.push(newWay) + this.newElementId = "way/"+id + return newElements + } + + +} \ No newline at end of file diff --git a/Logic/Osm/Changes.ts b/Logic/Osm/Changes.ts index f17697770..48fd10cf7 100644 --- a/Logic/Osm/Changes.ts +++ b/Logic/Osm/Changes.ts @@ -7,6 +7,7 @@ import {ChangeDescription} from "./Actions/ChangeDescription"; import {Utils} from "../../Utils"; import {LocalStorageSource} from "../Web/LocalStorageSource"; import SimpleMetaTagger from "../SimpleMetaTagger"; +import CreateNewNodeAction from "./Actions/CreateNewNodeAction"; /** * Handles all changes made to OSM. @@ -14,22 +15,20 @@ import SimpleMetaTagger from "../SimpleMetaTagger"; */ export class Changes { - - private _nextId: number = -1; // Newly assigned ID's are negative public readonly name = "Newly added features" /** * All the newly created features as featureSource + all the modified features */ public features = new UIEventSource<{ feature: any, freshness: Date }[]>([]); - public readonly pendingChanges: UIEventSource = LocalStorageSource.GetParsed("pending-changes", []) public readonly allChanges = new UIEventSource(undefined) + private _nextId: number = -1; // Newly assigned ID's are negative private readonly isUploading = new UIEventSource(false); private readonly previouslyCreated: OsmObject[] = [] private readonly _leftRightSensitive: boolean; - constructor(leftRightSensitive : boolean = false) { + constructor(leftRightSensitive: boolean = false) { this._leftRightSensitive = leftRightSensitive; // We keep track of all changes just as well this.allChanges.setData([...this.pendingChanges.data]) @@ -114,21 +113,34 @@ export class Changes { }) } + public async applyAction(action: OsmChangeAction): Promise { + const changes = await action.Perform(this) + console.log("Received changes:", changes) + this.pendingChanges.data.push(...changes); + this.pendingChanges.ping(); + this.allChanges.data.push(...changes) + this.allChanges.ping() + } + + public registerIdRewrites(mappings: Map): void { + CreateNewNodeAction.registerIdRewrites(mappings) + } + /** * UPload the selected changes to OSM. * Returns 'true' if successfull and if they can be removed * @param pending * @private */ - private async flushSelectChanges(pending: ChangeDescription[]): Promise{ + private async flushSelectChanges(pending: ChangeDescription[]): Promise { const self = this; const neededIds = Changes.GetNeededIds(pending) const osmObjects = await Promise.all(neededIds.map(id => OsmObject.DownloadObjectAsync(id))); - - if(this._leftRightSensitive){ + + if (this._leftRightSensitive) { osmObjects.forEach(obj => SimpleMetaTagger.removeBothTagging(obj.tags)) } - + console.log("Got the fresh objects!", osmObjects, "pending: ", pending) const changes: { newObjects: OsmObject[], @@ -137,35 +149,38 @@ export class Changes { } = self.CreateChangesetObjects(pending, osmObjects) if (changes.newObjects.length + changes.deletedObjects.length + changes.modifiedObjects.length === 0) { console.log("No changes to be made") - return true + return true } const meta = pending[0].meta - - const perType = Array.from(Utils.Hist(pending.map(descr => descr.meta.changeType)), ([key, count]) => ({ - key: key, - value: count, - aggregate: true - })) + + const perType = Array.from( + Utils.Hist(pending.filter(descr => descr.meta.changeType !== undefined && descr.meta.changeType !== null) + .map(descr => descr.meta.changeType)), ([key, count]) => ( + { + key: key, + value: count, + aggregate: true + })) const motivations = pending.filter(descr => descr.meta.specialMotivation !== undefined) .map(descr => ({ - key: descr.meta.changeType+":"+descr.type+"/"+descr.id, - value: descr.meta.specialMotivation + key: descr.meta.changeType + ":" + descr.type + "/" + descr.id, + value: descr.meta.specialMotivation })) const metatags = [{ key: "comment", - value: "Adding data with #MapComplete for theme #"+meta.theme + value: "Adding data with #MapComplete for theme #" + meta.theme }, { - key:"theme", - value:meta.theme + key: "theme", + value: meta.theme }, ...perType, ...motivations ] - + await State.state.osmConnection.changesetHandler.UploadChangeset( - (csId) => Changes.createChangesetFor(""+csId, changes), + (csId) => Changes.createChangesetFor("" + csId, changes), metatags ) @@ -178,27 +193,27 @@ export class Changes { try { // At last, we build the changeset and upload const pending = self.pendingChanges.data; - + const pendingPerTheme = new Map() for (const changeDescription of pending) { const theme = changeDescription.meta.theme - if(!pendingPerTheme.has(theme)){ + if (!pendingPerTheme.has(theme)) { pendingPerTheme.set(theme, []) } pendingPerTheme.get(theme).push(changeDescription) } - - const successes = await Promise.all(Array.from(pendingPerTheme, ([key , value]) => value) + + const successes = await Promise.all(Array.from(pendingPerTheme, ([key, value]) => value) .map(async pendingChanges => { - try{ + try { return await self.flushSelectChanges(pendingChanges); - }catch(e){ - console.error("Could not upload some changes:",e) + } catch (e) { + console.error("Could not upload some changes:", e) return false } })) - - if(!successes.some(s => s == false)){ + + if (!successes.some(s => s == false)) { // All changes successfull, we clear the data! this.pendingChanges.setData([]); } @@ -206,22 +221,13 @@ export class Changes { } catch (e) { console.error("Could not handle changes - probably an old, pending changeset in localstorage with an invalid format; erasing those", e) self.pendingChanges.setData([]) - }finally { + } finally { self.isUploading.setData(false) } } - public async applyAction(action: OsmChangeAction): Promise { - const changes = await action.Perform(this) - console.log("Received changes:", changes) - this.pendingChanges.data.push(...changes); - this.pendingChanges.ping(); - this.allChanges.data.push(...changes) - this.allChanges.ping() - } - private CreateChangesetObjects(changes: ChangeDescription[], downloadedOsmObjects: OsmObject[]): { newObjects: OsmObject[], modifiedObjects: OsmObject[] @@ -373,8 +379,4 @@ export class Changes { return result } - - public registerIdRewrites(mappings: Map): void { - - } } \ No newline at end of file diff --git a/UI/BigComponents/ImportButton.ts b/UI/BigComponents/ImportButton.ts index 197f1f122..362237e00 100644 --- a/UI/BigComponents/ImportButton.ts +++ b/UI/BigComponents/ImportButton.ts @@ -4,20 +4,34 @@ import {UIEventSource} from "../../Logic/UIEventSource"; import Combine from "../Base/Combine"; import {VariableUiElement} from "../Base/VariableUIElement"; import Translations from "../i18n/Translations"; -import State from "../../State"; import Constants from "../../Models/Constants"; import Toggle from "../Input/Toggle"; import CreateNewNodeAction from "../../Logic/Osm/Actions/CreateNewNodeAction"; import {Tag} from "../../Logic/Tags/Tag"; import Loading from "../Base/Loading"; +import OsmChangeAction from "../../Logic/Osm/Actions/OsmChangeAction"; +import CreateNewWayAction from "../../Logic/Osm/Actions/CreateNewWayAction"; +import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; +import {OsmConnection} from "../../Logic/Osm/OsmConnection"; +import {Changes} from "../../Logic/Osm/Changes"; +import {ElementStorage} from "../../Logic/ElementStorage"; +import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline"; export default class ImportButton extends Toggle { - constructor(imageUrl: string | BaseUIElement, message: string | BaseUIElement, + constructor(imageUrl: string | BaseUIElement, + message: string | BaseUIElement, originalTags: UIEventSource, - newTags: UIEventSource, - lat: number, lon: number, + newTags: UIEventSource, + feature: any, minZoom: number, - state: { + state: { + featureSwitchUserbadge: UIEventSource; + featurePipeline: FeaturePipeline; + allElements: ElementStorage; + selectedElement: UIEventSource; + layoutToUse: LayoutConfig, + osmConnection: OsmConnection, + changes: Changes, locationControl: UIEventSource<{ zoom: number }> }) { const t = Translations.t.general.add; @@ -32,7 +46,7 @@ export default class ImportButton extends Toggle { const txt = parts.join(" & ") return t.presetInfo.Subs({tags: txt}).SetClass("subtle") })), undefined, - State.state.osmConnection.userDetails.map(ud => ud.csCount >= Constants.userJourney.tagsVisibleAt) + state.osmConnection.userDetails.map(ud => ud.csCount >= Constants.userJourney.tagsVisibleAt) ) const button = new SubtleButton(imageUrl, message) @@ -44,15 +58,12 @@ export default class ImportButton extends Toggle { } originalTags.data["_imported"] = "yes" originalTags.ping() // will set isImported as per its definition - const newElementAction = new CreateNewNodeAction(newTags.data, lat, lon, { - theme: State.state.layoutToUse.id, - changeType: "import" - }) - await State.state.changes.applyAction(newElementAction) - State.state.selectedElement.setData(State.state.allElements.ContainingFeatures.get( + const newElementAction = ImportButton.createAddActionForFeature(newTags.data, feature, state.layoutToUse.id) + await state.changes.applyAction(newElementAction) + state.selectedElement.setData(state.allElements.ContainingFeatures.get( newElementAction.newElementId )) - console.log("Did set selected element to", State.state.allElements.ContainingFeatures.get( + console.log("Did set selected element to", state.allElements.ContainingFeatures.get( newElementAction.newElementId )) @@ -60,25 +71,70 @@ export default class ImportButton extends Toggle { }) const withLoadingCheck = new Toggle(new Toggle( - new Loading(t.stillLoading.Clone()), - new Combine([button, appliedTags]).SetClass("flex flex-col"), - State.state.featurePipeline.runningQuery - ),t.zoomInFurther.Clone(), - state.locationControl.map(l => l.zoom >= minZoom) - ) + new Loading(t.stillLoading.Clone()), + new Combine([button, appliedTags]).SetClass("flex flex-col"), + state.featurePipeline.runningQuery + ), t.zoomInFurther.Clone(), + state.locationControl.map(l => l.zoom >= minZoom) + ) const importButton = new Toggle(t.hasBeenImported, withLoadingCheck, isImported) const pleaseLoginButton = new Toggle(t.pleaseLogin.Clone() - .onClick(() => State.state.osmConnection.AttemptLogin()) + .onClick(() => state.osmConnection.AttemptLogin()) .SetClass("login-button-friendly"), undefined, - State.state.featureSwitchUserbadge) - + state.featureSwitchUserbadge) - super(importButton, - pleaseLoginButton, - State.state.osmConnection.isLoggedIn + + super(new Toggle(importButton, + pleaseLoginButton, + state.osmConnection.isLoggedIn + ), + t.wrongType, + new UIEventSource(ImportButton.canBeImported(feature)) ) } + + + private static canBeImported(feature: any) { + const type = feature.geometry.type + return type === "Point" || type === "LineString" || (type === "Polygon" && feature.geometry.coordinates.length === 1) + } + + private static createAddActionForFeature(newTags: Tag[], feature: any, theme: string): OsmChangeAction & { newElementId: string } { + const geometry = feature.geometry + const type = geometry.type + if (type === "Point") { + const lat = geometry.coordinates[1] + const lon = geometry.coordinates[0]; + return new CreateNewNodeAction(newTags, lat, lon, { + theme, + changeType: "import" + }) + } + + if (type === "LineString") { + return new CreateNewWayAction( + newTags, + geometry.coordinates.map(coor => ({lon: coor[0], lat: coor[1]})), + { + theme + } + ) + } + + if (type === "Polygon") { + return new CreateNewWayAction( + newTags, + geometry.coordinates[0].map(coor => ({lon: coor[0], lat: coor[1]})), + { + theme + } + ) + } + + return undefined; + + } } \ No newline at end of file diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index 0f13f5df5..72b0ece70 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -33,6 +33,7 @@ import AllKnownLayers from "../Customizations/AllKnownLayers"; import ShowDataLayer from "./ShowDataLayer/ShowDataLayer"; import Link from "./Base/Link"; import List from "./Base/List"; +import {OsmConnection} from "../Logic/Osm/OsmConnection"; export interface SpecialVisualization { funcName: string, @@ -480,7 +481,7 @@ export default class SpecialVisualizations { args: [ { name: "tags", - doc: "Tags to copy-specification. This contains one or more pairs (seperated by a `;`), e.g. `amenity=fast_food; addr:housenumber=$number`. This new point will then have the tags `amenity=fast_food` and `addr:housenumber` with the value that was saved in `number` in the original feature. (Hint: prepare these values, e.g. with calculatedTags)" + doc: "The tags to add onto the new object - see specification above" }, { name: "text", @@ -499,6 +500,8 @@ export default class SpecialVisualizations { }], docs: `This button will copy the data from an external dataset into OpenStreetMap. It is only functional in official themes but can be tested in unofficial themes. +#### Importing a dataset into OpenStreetMap: requirements + If you want to import a dataset, make sure that: 1. The dataset to import has a suitable license @@ -507,17 +510,41 @@ If you want to import a dataset, make sure that: There are also some technicalities in your theme to keep in mind: -1. The new point will be added and will flow through the program as any other new point as if it came from OSM. +1. The new feature will be added and will flow through the program as any other new point as if it came from OSM. This means that there should be a layer which will match the new tags and which will display it. -2. The original point from your geojson layer will gain the tag '_imported=yes'. +2. The original feature from your geojson layer will gain the tag '_imported=yes'. This should be used to change the appearance or even to hide it (eg by changing the icon size to zero) 3. There should be a way for the theme to detect previously imported points, even after reloading. - A reference number to the original dataset is an excellen way to do this + A reference number to the original dataset is an excellent way to do this +4. When importing ways, the theme creator is also responsible of avoiding overlapping ways. + +#### Disabled in unofficial themes + +The import button can be tested in an unofficial theme by adding \`test=true\` or \`backend=osm-test\` as [URL-paramter](URL_Parameters.md). +The import button will show up then. If in testmode, you can read the changeset-XML directly in the web console. +In the case that MapComplete is pointed to the testing grounds, the edit will be made on ${OsmConnection.oauth_configs["osm-test"].url} + + +#### Specifying which tags to copy or add + +The first argument of the import button takes a \`;\`-seperated list of tags to add. + +These can either be a tag to add, such as \`amenity=fast_food\` or can use a substitution, e.g. \`addr:housenumber=$number\`. +This new point will then have the tags \`amenity=fast_food\` and \`addr:housenumber\` with the value that was saved in \`number\` in the original feature. + +If a value to substitute is undefined, empty string will be used instead. + +This supports multiple values, e.g. \`ref=$source:geometry:type/$source:geometry:ref\` + +Remark that the syntax is slightly different then expected; it uses '$' to note a value to copy, followed by a name (matched with \`[a-zA-Z0-9_:]*\`). Sadly, delimiting with \`{}\` as these already mark the boundaries of the special rendering... + +Note that these values can be prepare with javascript in the theme by using a [calculatedTag](calculatedTags.md#calculating-tags-with-javascript) + `, constr: (state, tagSource, args) => { - if (!state.layoutToUse.official && !state.featureSwitchIsTesting.data) { + if (!state.layoutToUse.official && !(state.featureSwitchIsTesting.data || state.osmConnection._oauth_config.url === OsmConnection.oauth_configs["osm-test"].url)) { return new Combine([new FixedUiElement("The import button is disabled for unofficial themes to prevent accidents.").SetClass("alert"), - new FixedUiElement("To test, add 'test=true' to the URL. The changeset will be printed in the console. Please open a PR to officialize this theme to actually enable the import button.")]) + new FixedUiElement("To test, add test=true or backend=osm-test to the URL. The changeset will be printed in the console. Please open a PR to officialize this theme to actually enable the import button.")]) } const tgsSpec = args[0].split(";").map(spec => { const kv = spec.split("=").map(s => s.trim()); @@ -529,9 +556,18 @@ There are also some technicalities in your theme to keep in mind: const rewrittenTags: UIEventSource = tagSource.map(tags => { const newTags: Tag [] = [] for (const [key, value] of tgsSpec) { - if (value.startsWith('$')) { - const origKey = value.substring(1) - newTags.push(new Tag(key, tags[origKey])) + if (value.indexOf('$') >= 0) { + + let parts = value.split("$") + // THe first of the split won't start with a '$', so no substitution needed + let actualValue = parts[0] + parts.shift() + + for (const part of parts) { + const [_, varName, leftOver] = part.match(/([a-zA-Z0-9_:]*)(.*)/) + actualValue += (tags[varName] ?? "") + leftOver + } + newTags.push(new Tag(key, actualValue)) } else { newTags.push(new Tag(key, value)) } @@ -540,12 +576,12 @@ There are also some technicalities in your theme to keep in mind: }) const id = tagSource.data.id; const feature = state.allElements.ContainingFeatures.get(id) - if (feature.geometry.type !== "Point") { - return new FixedUiElement("Error: can only import point objects").SetClass("alert") - } - const [lon, lat] = feature.geometry.coordinates; + const minzoom = Number(args[3]) + const message = args[1] + const image = args[2] + return new ImportButton( - args[2], args[1], tagSource, rewrittenTags, lat, lon, Number(args[3]), state + image, message, tagSource, rewrittenTags, feature, minzoom, state ) } }, diff --git a/assets/themes/grb_import/grb.json b/assets/themes/grb_import/grb.json index 69e4673d5..57d99c145 100644 --- a/assets/themes/grb_import/grb.json +++ b/assets/themes/grb_import/grb.json @@ -565,16 +565,36 @@ "title": "GRB outline", "minzoom": 16, "calculatedTags": [ - "_overlaps_with=feat.overlapWith('OSM-buildings').filter(f => f.overlap > 1 && feat.properties._surface - f.overlap < 5)[0] ?? null", - "_osm_obj:source:ref=JSON.parse(feat.properties._overlaps_with)?.feat?.properties['source:geometry:ref']", - "_osm_obj:source:date=JSON.parse(feat.properties._overlaps_with)?.feat?.properties['source:geometry:date'].replace(/\\//g, '-')", - "_imported_osm_object_found= feat.properties['_osm_obj:source:ref'] == feat.properties['source:geometry:entity'] + '/' + feat.properties['source:geometry:oidn']", + "_overlaps_with=feat.overlapWith('OSM-buildings').filter(f => f.overlap > 1 && (feat.get('_surface') < 20 || f.overlap / feat.get('_surface')) > 0.9)[0] ?? null", + "_overlap_absolute=feat.get('_overlaps_with')?.overlap", + "_overlap_percentage=Math.round(100 * feat.get('_overlap_absolute') / feat.get('_surface')) ", + "_osm_obj:source:ref=feat.get('_overlaps_with')?.feat?.properties['source:geometry:ref']", + "_osm_obj:source:date=feat.get('_overlaps_with')?.feat?.properties['source:geometry:date'].replace(/\\//g, '-')", + "_osm_obj:building=feat.get('_overlaps_with')?.feat?.properties.building", + "_osm_obj:id=feat.get('_overlaps_with')?.feat?.properties.id", + "_grb_ref=feat.properties['source:geometry:entity'] + '/' + feat.properties['source:geometry:oidn']", + "_imported_osm_object_found= feat.properties['_osm_obj:source:ref'] == feat.properties._grb_ref", "_grb_date=feat.properties['source:geometry:date'].replace(/\\//g,'-')", "_imported_osm_still_fresh= feat.properties['_osm_obj:source:date'] == feat.properties._grb_date" ], "tagRenderings": [ { - "render": "{import_button(Upload this geometry to OpenStreetMap)}" + "id": "Building info", + "render": "This is a {building} detected by {detection_method}" + }, + { + "id": "overlapping building type", + "render": "
The overlapping openstreetmap-building is a {_osm_obj:building} and covers {_overlap_percentage}% of the GRB building

GRB geometry:

{minimap(21, id):height:10rem;border-radius:1rem;overflow:hidden}

OSM geometry:

{minimap(21,_osm_obj:id):height:10rem;border-radius:1rem;overflow:hidden}", + "condition": "_overlaps_with!=null" + }, + { + "id": "Import-button", + "render": "{import_button(building=$building; source:geometry:date=$_grb_date; source:geometry:ref=$_grb_ref, Upload this building to OpenStreetMap)}", + "mappings": [ + { + "if": "_overlaps_with!=null", + "then": "Cannot be imported directly, there is a nearly identical building geometry in OpenStreetMap" + }] }, "all_tags" ], diff --git a/langs/en.json b/langs/en.json index cee2bae0c..02dd71dbb 100644 --- a/langs/en.json +++ b/langs/en.json @@ -108,7 +108,8 @@ "openLayerControl": "Open the layer control box", "layerNotEnabled": "The layer {layer} is not enabled. Enable this layer to add a point", "hasBeenImported": "This point has already been imported", - "zoomInMore": "Zoom in more to import this feature" + "zoomInMore": "Zoom in more to import this feature", + "wrongType": "This element is not a point or a way and can not be imported" }, "pickLanguage": "Choose a language: ", "about": "Easily edit and add OpenStreetMap for a certain theme", From 6c39f563b6bc26a7d8c1b4ae5b1421e4e7905612 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 29 Oct 2021 18:16:51 +0200 Subject: [PATCH 53/95] More work on GRB theme, add 'apply_action' button --- Docs/CalculatedTags.md | 2 +- Docs/SpecialRenderings.md | 67 ++++++++++- UI/SpecialVisualizations.ts | 182 +++++++++++++++++++++--------- assets/themes/grb_import/grb.json | 84 ++++++++++++-- langs/en.json | 4 + scripts/generateDocs.ts | 2 +- 6 files changed, 275 insertions(+), 66 deletions(-) diff --git a/Docs/CalculatedTags.md b/Docs/CalculatedTags.md index 5ca15250d..02345f1b7 100644 --- a/Docs/CalculatedTags.md +++ b/Docs/CalculatedTags.md @@ -183,7 +183,7 @@ Some advanced functions are available on **feat** as well: ### overlapWith Gives a list of features from the specified layer which this feature (partly) overlaps with. If the current feature is a point, all features that embed the point are given. The returned value is `{ feat: GeoJSONFeature, overlap: number}[]` where `overlap` is the overlapping surface are (in m²) for areas, the overlapping length (in meter) if the current feature is a line or `undefined` if the current feature is a point. - +The resulting list is sorted in descending order by overlap. The feature with the most overlap will thus be the first in the list For example to get all objects which overlap or embed from a layer, use `_contained_climbing_routes_properties=feat.overlapWith('climbing_route')` 0. ...layerIds - one or more layer ids of the layer from which every feature is checked for overlap) diff --git a/Docs/SpecialRenderings.md b/Docs/SpecialRenderings.md index 7480a053c..af4a3948b 100644 --- a/Docs/SpecialRenderings.md +++ b/Docs/SpecialRenderings.md @@ -23,6 +23,7 @@ General usage is `{func_name()}`, `{func_name(arg, someotherarg)}` or `{func_nam - [canonical](#canonical) - [import_button](#import_button) - [multi_apply](#multi_apply) + - [tag_apply](#tag_apply) @@ -191,6 +192,8 @@ key | _undefined_ | The key of the tag to give the canonical text for This button will copy the data from an external dataset into OpenStreetMap. It is only functional in official themes but can be tested in unofficial themes. +#### Importing a dataset into OpenStreetMap: requirements + If you want to import a dataset, make sure that: 1. The dataset to import has a suitable license @@ -199,17 +202,42 @@ If you want to import a dataset, make sure that: There are also some technicalities in your theme to keep in mind: -1. The new point will be added and will flow through the program as any other new point as if it came from OSM. +1. The new feature will be added and will flow through the program as any other new point as if it came from OSM. This means that there should be a layer which will match the new tags and which will display it. -2. The original point from your geojson layer will gain the tag '_imported=yes'. +2. The original feature from your geojson layer will gain the tag '_imported=yes'. This should be used to change the appearance or even to hide it (eg by changing the icon size to zero) 3. There should be a way for the theme to detect previously imported points, even after reloading. - A reference number to the original dataset is an excellen way to do this + A reference number to the original dataset is an excellent way to do this +4. When importing ways, the theme creator is also responsible of avoiding overlapping ways. + +#### Disabled in unofficial themes + +The import button can be tested in an unofficial theme by adding `test=true` or `backend=osm-test` as [URL-paramter](URL_Parameters.md). +The import button will show up then. If in testmode, you can read the changeset-XML directly in the web console. +In the case that MapComplete is pointed to the testing grounds, the edit will be made on https://master.apis.dev.openstreetmap.org + + +#### Specifying which tags to copy or add + +The first argument of the import button takes a `;`-seperated list of tags to add. + +These can either be a tag to add, such as `amenity=fast_food` or can use a substitution, e.g. `addr:housenumber=$number`. +This new point will then have the tags `amenity=fast_food` and `addr:housenumber` with the value that was saved in `number` in the original feature. + +If a value to substitute is undefined, empty string will be used instead. + +This supports multiple values, e.g. `ref=$source:geometry:type/$source:geometry:ref` + +Remark that the syntax is slightly different then expected; it uses '$' to note a value to copy, followed by a name (matched with `[a-zA-Z0-9_:]*`). Sadly, delimiting with `{}` as these already mark the boundaries of the special rendering... + +Note that these values can be prepare with javascript in the theme by using a [calculatedTag](calculatedTags.md#calculating-tags-with-javascript) + + name | default | description ------ | --------- | ------------- -tags | _undefined_ | Tags to copy-specification. This contains one or more pairs (seperated by a `;`), e.g. `amenity=fast_food; addr:housenumber=$number`. This new point will then have the tags `amenity=fast_food` and `addr:housenumber` with the value that was saved in `number` in the original feature. (Hint: prepare these values, e.g. with calculatedTags) +tags | _undefined_ | The tags to add onto the new object - see specification above text | Import this data into OpenStreetMap | The text to show on the button icon | ./assets/svg/addSmall.svg | A nice icon to show in the button minzoom | 18 | How far the contributor must zoom in before being able to import the point @@ -233,4 +261,33 @@ overwrite | _undefined_ | If set to 'true', the tags on the other objects will a #### Example usage - {multi_apply(_features_with_the_same_name_within_100m, name:etymology:wikidata;name:etymology, Apply etymology information on all nearby objects with the same name)} Generated from UI/SpecialVisualisations.ts \ No newline at end of file + {multi_apply(_features_with_the_same_name_within_100m, name:etymology:wikidata;name:etymology, Apply etymology information on all nearby objects with the same name)} + + +### tag_apply + + Shows a big button; clicking this button will apply certain tags onto the feature. + +The first argument takes a specification of which tags to add. +These can either be a tag to add, such as `amenity=fast_food` or can use a substitution, e.g. `addr:housenumber=$number`. +This new point will then have the tags `amenity=fast_food` and `addr:housenumber` with the value that was saved in `number` in the original feature. + +If a value to substitute is undefined, empty string will be used instead. + +This supports multiple values, e.g. `ref=$source:geometry:type/$source:geometry:ref` + +Remark that the syntax is slightly different then expected; it uses '$' to note a value to copy, followed by a name (matched with `[a-zA-Z0-9_:]*`). Sadly, delimiting with `{}` as these already mark the boundaries of the special rendering... + +Note that these values can be prepare with javascript in the theme by using a [calculatedTag](calculatedTags.md#calculating-tags-with-javascript) + + +name | default | description +------ | --------- | ------------- +tags_to_apply | _undefined_ | A specification of the tags to apply +message | _undefined_ | The text to show to the contributor +image | _undefined_ | An image to show to the contributor on the button +id_of_object_to_apply_this_one | _undefined_ | If specified, applies the the tags onto _another_ object. The id will be read from properties[id_of_object_to_apply_this_one] of the selected object. The tags are still calculated based on the tags of the _selected_ element + +#### Example usage + + `{tag_apply(survey_date:=$_now:date, Surveyed today!)}` Generated from UI/SpecialVisualisations.ts \ No newline at end of file diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index 72b0ece70..87af2ab7d 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -34,6 +34,10 @@ import ShowDataLayer from "./ShowDataLayer/ShowDataLayer"; import Link from "./Base/Link"; import List from "./Base/List"; import {OsmConnection} from "../Logic/Osm/OsmConnection"; +import {SubtleButton} from "./Base/SubtleButton"; +import ChangeTagAction from "../Logic/Osm/Actions/ChangeTagAction"; +import {And} from "../Logic/Tags/And"; +import Toggle from "./Input/Toggle"; export interface SpecialVisualization { funcName: string, @@ -45,6 +49,17 @@ export interface SpecialVisualization { export default class SpecialVisualizations { + private static tagsToApplyHelpText = `These can either be a tag to add, such as \`amenity=fast_food\` or can use a substitution, e.g. \`addr:housenumber=$number\`. +This new point will then have the tags \`amenity=fast_food\` and \`addr:housenumber\` with the value that was saved in \`number\` in the original feature. + +If a value to substitute is undefined, empty string will be used instead. + +This supports multiple values, e.g. \`ref=$source:geometry:type/$source:geometry:ref\` + +Remark that the syntax is slightly different then expected; it uses '$' to note a value to copy, followed by a name (matched with \`[a-zA-Z0-9_:]*\`). Sadly, delimiting with \`{}\` as these already mark the boundaries of the special rendering... + +Note that these values can be prepare with javascript in the theme by using a [calculatedTag](calculatedTags.md#calculating-tags-with-javascript) + ` public static specialVisualizations: SpecialVisualization[] = [ { @@ -222,7 +237,6 @@ export default class SpecialVisualizations { return minimap; } }, - { funcName: "sided_minimap", docs: "A small map showing _only one side_ the selected feature. *This features requires to have linerenderings with offset* as only linerenderings with a postive or negative offset will be shown. Note: in most cases, this map will be automatically introduced", @@ -305,14 +319,14 @@ export default class SpecialVisualizations { name: "key", defaultValue: "opening_hours", doc: "The tagkey from which the table is constructed." - },{ + }, { name: "prefix", defaultValue: "", - doc:"Remove this string from the start of the value before parsing. __Note: use `&LPARENs` to indicate `(` if needed__" - },{ + doc: "Remove this string from the start of the value before parsing. __Note: use `&LPARENs` to indicate `(` if needed__" + }, { name: "postfix", defaultValue: "", - doc:"Remove this string from the end of the value before parsing. __Note: use `&RPARENs` to indicate `)` if needed__" + doc: "Remove this string from the end of the value before parsing. __Note: use `&RPARENs` to indicate `)` if needed__" }], example: "A normal opening hours table can be invoked with `{opening_hours_table()}`. A table for e.g. conditional access with opening hours can be `{opening_hours_table(access:conditional, no @ &LPARENS, &RPARENS)}`", constr: (state: State, tagSource: UIEventSource, args) => { @@ -529,57 +543,21 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be The first argument of the import button takes a \`;\`-seperated list of tags to add. -These can either be a tag to add, such as \`amenity=fast_food\` or can use a substitution, e.g. \`addr:housenumber=$number\`. -This new point will then have the tags \`amenity=fast_food\` and \`addr:housenumber\` with the value that was saved in \`number\` in the original feature. - -If a value to substitute is undefined, empty string will be used instead. - -This supports multiple values, e.g. \`ref=$source:geometry:type/$source:geometry:ref\` - -Remark that the syntax is slightly different then expected; it uses '$' to note a value to copy, followed by a name (matched with \`[a-zA-Z0-9_:]*\`). Sadly, delimiting with \`{}\` as these already mark the boundaries of the special rendering... - -Note that these values can be prepare with javascript in the theme by using a [calculatedTag](calculatedTags.md#calculating-tags-with-javascript) - +${SpecialVisualizations.tagsToApplyHelpText} + `, constr: (state, tagSource, args) => { if (!state.layoutToUse.official && !(state.featureSwitchIsTesting.data || state.osmConnection._oauth_config.url === OsmConnection.oauth_configs["osm-test"].url)) { return new Combine([new FixedUiElement("The import button is disabled for unofficial themes to prevent accidents.").SetClass("alert"), new FixedUiElement("To test, add test=true or backend=osm-test to the URL. The changeset will be printed in the console. Please open a PR to officialize this theme to actually enable the import button.")]) } - const tgsSpec = args[0].split(";").map(spec => { - const kv = spec.split("=").map(s => s.trim()); - if (kv.length != 2) { - throw "Invalid key spec: multiple '=' found in " + spec - } - return kv - }) - const rewrittenTags: UIEventSource = tagSource.map(tags => { - const newTags: Tag [] = [] - for (const [key, value] of tgsSpec) { - if (value.indexOf('$') >= 0) { - - let parts = value.split("$") - // THe first of the split won't start with a '$', so no substitution needed - let actualValue = parts[0] - parts.shift() - - for (const part of parts) { - const [_, varName, leftOver] = part.match(/([a-zA-Z0-9_:]*)(.*)/) - actualValue += (tags[varName] ?? "") + leftOver - } - newTags.push(new Tag(key, actualValue)) - } else { - newTags.push(new Tag(key, value)) - } - } - return newTags - }) + const rewrittenTags = SpecialVisualizations.generateTagsToApply(args[0], tagSource) const id = tagSource.data.id; const feature = state.allElements.ContainingFeatures.get(id) const minzoom = Number(args[3]) - const message = args[1] + const message = args[1] const image = args[2] - + return new ImportButton( image, message, tagSource, rewrittenTags, feature, minzoom, state ) @@ -636,12 +614,113 @@ Note that these values can be prepare with javascript in the theme by using a [c ); } + }, + { + funcName: "tag_apply", + docs: "Shows a big button; clicking this button will apply certain tags onto the feature.\n\nThe first argument takes a specification of which tags to add.\n" + SpecialVisualizations.tagsToApplyHelpText, + args: [ + { + name: "tags_to_apply", + doc: "A specification of the tags to apply" + }, + { + name: "message", + doc: "The text to show to the contributor" + }, + { + name: "image", + doc: "An image to show to the contributor on the button" + }, + { + name: "id_of_object_to_apply_this_one", + defaultValue: undefined, + doc: "If specified, applies the the tags onto _another_ object. The id will be read from properties[id_of_object_to_apply_this_one] of the selected object. The tags are still calculated based on the tags of the _selected_ element" + } + ], + example: "`{tag_apply(survey_date:=$_now:date, Surveyed today!)}`", + constr: (state, tags, args) => { + const tagsToApply = SpecialVisualizations.generateTagsToApply(args[0], tags) + const msg = args[1] + let image = args[2]?.trim() + if (image === "" || image === "undefined") { + image = undefined + } + const targetIdKey = args[3] + const t = Translations.t.general.apply_button + + const tagsExplanation = new VariableUiElement(tagsToApply.map(tagsToApply => { + const tagsStr = tagsToApply.map(t => t.asHumanString(false, true)).join("&"); + let el: BaseUIElement = new FixedUiElement(tagsStr) + if(targetIdKey !== undefined){ + const targetId = tags.data[targetIdKey] ?? tags.data.id + el = t.appliedOnAnotherObject.Subs({tags: tagsStr , id: targetId }) + } + return el; + } + )).SetClass("subtle") + + const applied = new UIEventSource(false) + const applyButton = new SubtleButton(image, new Combine([msg, tagsExplanation]).SetClass("flex flex-col")) + .onClick(() => { + const targetId = tags.data[ targetIdKey] ?? tags.data.id + const changeAction = new ChangeTagAction(targetId, + new And(tagsToApply.data), + tags.data, // We pass in the tags of the selected element, not the tags of the target element! + { + theme: state.layoutToUse.id, + changeType: "answer" + } + ) + state.changes.applyAction(changeAction) + applied.setData(true) + }) + + + return new Toggle( + new Toggle( + t.isApplied.SetClass("thanks"), + applyButton, + applied + ) + , undefined, state.osmConnection.isLoggedIn) + } } ] - static HelpMessage: BaseUIElement = SpecialVisualizations.GenHelpMessage(); + private static generateTagsToApply(spec: string, tagSource: UIEventSource): UIEventSource { - private static GenHelpMessage() { + const tgsSpec = spec.split(";").map(spec => { + const kv = spec.split("=").map(s => s.trim()); + if (kv.length != 2) { + throw "Invalid key spec: multiple '=' found in " + spec + } + return kv + }) + return tagSource.map(tags => { + const newTags: Tag [] = [] + for (const [key, value] of tgsSpec) { + if (value.indexOf('$') >= 0) { + + let parts = value.split("$") + // THe first of the split won't start with a '$', so no substitution needed + let actualValue = parts[0] + parts.shift() + + for (const part of parts) { + const [_, varName, leftOver] = part.match(/([a-zA-Z0-9_:]*)(.*)/) + actualValue += (tags[varName] ?? "") + leftOver + } + newTags.push(new Tag(key, actualValue)) + } else { + newTags.push(new Tag(key, value)) + } + } + return newTags + }) + + } + + public static HelpMessage() { const helpTexts = SpecialVisualizations.specialVisualizations.map(viz => new Combine( @@ -651,7 +730,7 @@ Note that these values can be prepare with javascript in the theme by using a [c viz.args.length > 0 ? new Table(["name", "default", "description"], viz.args.map(arg => { let defaultArg = arg.defaultValue ?? "_undefined_" - if(defaultArg == ""){ + if (defaultArg == "") { defaultArg = "_empty string_" } return [arg.name, defaultArg, arg.doc]; @@ -665,9 +744,9 @@ Note that these values can be prepare with javascript in the theme by using a [c ] )); - + const toc = new List( - SpecialVisualizations.specialVisualizations.map(viz => new Link(viz.funcName, "#"+viz.funcName)) + SpecialVisualizations.specialVisualizations.map(viz => new Link(viz.funcName, "#" + viz.funcName)) ) return new Combine([ @@ -679,4 +758,5 @@ Note that these values can be prepare with javascript in the theme by using a [c ] ).SetClass("flex flex-col"); } + } \ No newline at end of file diff --git a/assets/themes/grb_import/grb.json b/assets/themes/grb_import/grb.json index 57d99c145..7979fe324 100644 --- a/assets/themes/grb_import/grb.json +++ b/assets/themes/grb_import/grb.json @@ -7,7 +7,8 @@ "nl": "Grb Fixup" }, "description": { - "nl": "GRB Fixup" + "nl": "GRB Fixup", + "en": "This theme is an attempt to help automating the GRB import.
Note that this is very hacky and 'steals' the GRB data from an external site; in order to do this, you need to install and activate
this firefox extension for it to work." }, "language": [ "nl" @@ -23,6 +24,9 @@ "clustering": { "maxZoom": 15 }, + "overrideAll": { + "minzoom": 18 + }, "layers": [ { "id": "OSM-buildings", @@ -31,7 +35,6 @@ "osmTags": "building~*", "maxCacheAge": 0 }, - "minzoom": 16, "mapRendering": [ { "width": { @@ -67,6 +70,55 @@ ], "title": "OSM-gebouw", "tagRenderings": [ + { + "id": "building type", + "freeform": { + "key": "building" + }, + "render": "The building type is {building}", + "mappings": [ + { + "if": "building=house", + "then": "A normal house" + }, + { + "if": "building=detached", + "then": "A house detached from other building" + }, + { + "if": "building=semidetached_house", + "then": "A house sharing only one wall with another house" + }, + { + "if": "building=apartments", + "then": "An apartment building - highrise for living" + }, + { + "if": "building=office", + "then": "An office building - highrise for work" + }, + { + "if": "building=apartments", + "then": "An apartment building" + }, + { + "if": "building=shed", + "then": "A small shed, e.g. in a garden" + }, + { + "if": "building=garage", + "then": "A single garage to park a car" + }, + { + "if": "building=garages", + "then": "A building containing only garages; typically they are all identical" + }, + { + "if": "building=yes", + "then": "A building - no specification" + } + ] + }, "all_tags" ] }, @@ -99,7 +151,6 @@ }, "maxCacheAge": 0 }, - "minzoom": 18, "mapRendering": [ { "color": { @@ -124,7 +175,6 @@ "name": { "nl": "Fixmes op gebouwen" }, - "minzoom": 21, "source": { "maxCacheAge": 0, "osmTags": { @@ -314,7 +364,6 @@ "geoJsonZoomLevel": 18, "maxCacheAge": 0 }, - "minzoom": 16, "name": "CRAB-addressen", "title": "CRAB-adres", "mapRendering": [ @@ -372,7 +421,6 @@ "name": { "nl": "Fixmes op gebouwen" }, - "minzoom": 16, "source": { "maxCacheAge": 0, "osmTags": { @@ -563,7 +611,6 @@ }, "name": "GRB geometries", "title": "GRB outline", - "minzoom": 16, "calculatedTags": [ "_overlaps_with=feat.overlapWith('OSM-buildings').filter(f => f.overlap > 1 && (feat.get('_surface') < 20 || f.overlap / feat.get('_surface')) > 0.9)[0] ?? null", "_overlap_absolute=feat.get('_overlaps_with')?.overlap", @@ -587,6 +634,26 @@ "render": "
The overlapping openstreetmap-building is a {_osm_obj:building} and covers {_overlap_percentage}% of the GRB building

GRB geometry:

{minimap(21, id):height:10rem;border-radius:1rem;overflow:hidden}

OSM geometry:

{minimap(21,_osm_obj:id):height:10rem;border-radius:1rem;overflow:hidden}", "condition": "_overlaps_with!=null" }, + { + "id": "apply-id", + "render": "{tag_apply(source:geometry:date=$_grb_date; source:geometry:ref=$_grb_ref,Mark the OSM-building as imported,,_osm_obj:id)}", + "condition": { + "and": [ + "_overlaps_with!=null" + ] + } + }, + { + "id": "apply-building-type", + "render": "{tag_apply(building=$building,Use the building type from GRB,,_osm_obj:id)}", + "condition": { + "and": [ + "_overlaps_with!=null", + "_osm_obj:building=yes", + "building!=yes" + ] + } + }, { "id": "Import-button", "render": "{import_button(building=$building; source:geometry:date=$_grb_date; source:geometry:ref=$_grb_ref, Upload this building to OpenStreetMap)}", @@ -594,7 +661,8 @@ { "if": "_overlaps_with!=null", "then": "Cannot be imported directly, there is a nearly identical building geometry in OpenStreetMap" - }] + } + ] }, "all_tags" ], diff --git a/langs/en.json b/langs/en.json index 02dd71dbb..0e8640fde 100644 --- a/langs/en.json +++ b/langs/en.json @@ -131,6 +131,10 @@ "previouslyHiddenTitle": "Previously visited hidden themes", "hiddenExplanation": "These themes are only visible if you know the link. You have discovered {hidden_discovered} out of {total_hidden} hidden themes" }, + "apply_button": { + "isApplied": "The changes are applied", + "appliedOnAnotherObject": "The object {id} will receive {tags}" + }, "sharescreen": { "intro": "

Share this map

Share this map by copying the link below and sending it to friends and family:", "addToHomeScreen": "

Add to your home screen

You can easily add this website to your smartphone home screen for a native feel. Click the 'add to home screen' button in the URL bar to do this.", diff --git a/scripts/generateDocs.ts b/scripts/generateDocs.ts index 0ad24f43d..9d7951d0f 100644 --- a/scripts/generateDocs.ts +++ b/scripts/generateDocs.ts @@ -22,7 +22,7 @@ function WriteFile(filename, html: string | BaseUIElement, autogenSource: string ]).AsMarkdown()); } -WriteFile("./Docs/SpecialRenderings.md", SpecialVisualizations.HelpMessage, ["UI/SpecialVisualisations.ts"]) +WriteFile("./Docs/SpecialRenderings.md", SpecialVisualizations.HelpMessage(), ["UI/SpecialVisualisations.ts"]) WriteFile("./Docs/CalculatedTags.md", new Combine([SimpleMetaTagger.HelpText(), ExtraFunction.HelpText()]).SetClass("flex-col"), ["SimpleMetaTagger", "ExtraFunction"]) WriteFile("./Docs/SpecialInputElements.md", ValidatedTextField.HelpText(), ["ValidatedTextField.ts"]); From 395ab498b5d91ec4c0be3fe266d94aaab42dae39 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Fri, 29 Oct 2021 19:38:27 +0200 Subject: [PATCH 54/95] Create FUNDING.yml --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..9615220ed --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +liberapay: pietervdvn From 64adf13d42689cb51ab0cbebe0a4d44220dc1fb4 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 30 Oct 2021 01:55:32 +0200 Subject: [PATCH 55/95] Rework copyright panel, fix filter view toggle --- Logic/Actors/SelectedFeatureHandler.ts | 2 +- UI/BigComponents/Attribution.ts | 1 + ...{AttributionPanel.ts => CopyrightPanel.ts} | 106 +++++++++++++++--- UI/BigComponents/LeftControls.ts | 6 +- UI/DefaultGUI.ts | 2 +- assets/svg/liberapay.svg | 1 + assets/svg/license_info.json | 10 ++ assets/themes/grb_import/grb.json | 3 +- langs/en.json | 8 ++ langs/themes/en.json | 3 + 10 files changed, 120 insertions(+), 22 deletions(-) rename UI/BigComponents/{AttributionPanel.ts => CopyrightPanel.ts} (50%) create mode 100644 assets/svg/liberapay.svg diff --git a/Logic/Actors/SelectedFeatureHandler.ts b/Logic/Actors/SelectedFeatureHandler.ts index 44a2940d3..aba97ecb1 100644 --- a/Logic/Actors/SelectedFeatureHandler.ts +++ b/Logic/Actors/SelectedFeatureHandler.ts @@ -10,7 +10,7 @@ import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; * Makes sure the hash shows the selected element and vice-versa. */ export default class SelectedFeatureHandler { - private static readonly _no_trigger_on = new Set(["welcome", "copyright", "layers", "new", "filter","", undefined]) + private static readonly _no_trigger_on = new Set(["welcome", "copyright", "layers", "new", "filters","", undefined]) private readonly hash: UIEventSource; private readonly state: { selectedElement: UIEventSource, diff --git a/UI/BigComponents/Attribution.ts b/UI/BigComponents/Attribution.ts index 1bcdcbc9e..267bd776c 100644 --- a/UI/BigComponents/Attribution.ts +++ b/UI/BigComponents/Attribution.ts @@ -56,6 +56,7 @@ export default class Attribution extends Combine { ) ) super([mapComplete, reportBug, stats, editHere, editWithJosm, mapillary]); + this.SetClass("flex") } diff --git a/UI/BigComponents/AttributionPanel.ts b/UI/BigComponents/CopyrightPanel.ts similarity index 50% rename from UI/BigComponents/AttributionPanel.ts rename to UI/BigComponents/CopyrightPanel.ts index c18c8d287..bcf7f3784 100644 --- a/UI/BigComponents/AttributionPanel.ts +++ b/UI/BigComponents/CopyrightPanel.ts @@ -12,23 +12,99 @@ import {VariableUiElement} from "../Base/VariableUIElement"; import * as contributors from "../../assets/contributors.json" import BaseUIElement from "../BaseUIElement"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; +import Title from "../Base/Title"; +import {SubtleButton} from "../Base/SubtleButton"; +import Svg from "../../Svg"; +import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline"; +import {BBox} from "../../Logic/BBox"; +import Loc from "../../Models/Loc"; +import Toggle from "../Input/Toggle"; +import {OsmConnection} from "../../Logic/Osm/OsmConnection"; +import Constants from "../../Models/Constants"; /** * The attribution panel shown on mobile */ -export default class AttributionPanel extends Combine { +export default class CopyrightPanel extends Combine { - private static LicenseObject = AttributionPanel.GenerateLicenses(); + private static LicenseObject = CopyrightPanel.GenerateLicenses(); - constructor(layoutToUse: LayoutConfig, contributions: UIEventSource>) { + constructor(state: { + layoutToUse: LayoutConfig, + featurePipeline: FeaturePipeline, + currentBounds: UIEventSource, + locationControl: UIEventSource, + osmConnection: OsmConnection + }, contributions: UIEventSource>) { + + const t =Translations.t.general.attribution + const layoutToUse = state.layoutToUse + const josmState = new UIEventSource(undefined) + // Reset after 15s + josmState.stabilized(15000).addCallbackD(_ => josmState.setData(undefined)) + const iconStyle = "height: 1.5rem; width: auto" + const actionButtons = [ + new SubtleButton(Svg.liberapay_ui().SetStyle(iconStyle), t.donate, { + url: "https://liberapay.com/pietervdvn/", + newTab: true + }), + new SubtleButton(Svg.bug_ui().SetStyle(iconStyle), t.openIssueTracker, { + url: "https://github.com/pietervdvn/MapComplete/issues", + newTab: true + }), + new SubtleButton(Svg.statistics_ui().SetStyle(iconStyle), t.openOsmcha.Subs({theme: state.layoutToUse.title}), { + url: Utils.OsmChaLinkFor(31, state.layoutToUse.id), + newTab: true + }), + new VariableUiElement(state.locationControl.map(location => { + const idLink = `https://www.openstreetmap.org/edit?editor=id#map=${location?.zoom ?? 0}/${location?.lat ?? 0}/${location?.lon ?? 0}` + return new SubtleButton(Svg.pencil_ui().SetStyle(iconStyle), t.editId, {url: idLink, newTab: true}) + })), + + new VariableUiElement(state.locationControl.map(location => { + const mapillaryLink = `https://www.mapillary.com/app/?focus=map&lat=${location?.lat ?? 0}&lng=${location?.lon ?? 0}&z=${Math.max((location?.zoom ?? 2) - 1, 1)}` + return new SubtleButton(Svg.mapillary_black_ui().SetStyle(iconStyle), t.openMapillary, {url: mapillaryLink, newTab: true}) + })), + new VariableUiElement(josmState.map(state => { + if(state === undefined){ + return undefined + } + state = state.toUpperCase() + if(state === "OK"){ + return t.josmOpened.SetClass("thanks") + } + return t.josmNotOpened.SetClass("alert") + })), + new Toggle( + new SubtleButton(Svg.josm_logo_ui().SetStyle(iconStyle) , t.editJosm).onClick(() => { + const bounds: any = state.currentBounds.data; + if (bounds === undefined) { + return undefined + } + const top = bounds.getNorth(); + const bottom = bounds.getSouth(); + const right = bounds.getEast(); + const left = bounds.getWest(); + const josmLink = `http://127.0.0.1:8111/load_and_zoom?left=${left}&right=${right}&top=${top}&bottom=${bottom}` + Utils.download(josmLink).then(answer => josmState.setData(answer.replace(/\n/g, '').trim())).catch(_ => josmState.setData("ERROR")) + }), undefined, state.osmConnection.userDetails.map(ud => ud.loggedIn && ud.csCount >= Constants.userJourney.historyLinkVisible)), + + ].map(button => button.SetStyle("max-height: 3rem")) + + const iconAttributions = Utils.NoNull(Array.from(layoutToUse.ExtractImages())) + .map(CopyrightPanel.IconAttribution) + + let maintainer : BaseUIElement= undefined + if(layoutToUse.maintainer !== undefined && layoutToUse.maintainer !== "" && layoutToUse.maintainer.toLowerCase() !== "mapcomplete"){ + maintainer = Translations.t.general.attribution.themeBy.Subs({author: layoutToUse.maintainer}) + } + super([ Translations.t.general.attribution.attributionContent, - ((layoutToUse.maintainer ?? "") == "") ? "" : Translations.t.general.attribution.themeBy.Subs({author: layoutToUse.maintainer}), - layoutToUse.credits, - "
", + maintainer, + new Combine(actionButtons).SetClass("block w-full"), + new FixedUiElement(layoutToUse.credits), new Attribution(State.state.locationControl, State.state.osmConnection.userDetails, State.state.layoutToUse, State.state.currentBounds), - "
", - new VariableUiElement(contributions.map(contributions => { if(contributions === undefined){ return "" @@ -62,14 +138,12 @@ export default class AttributionPanel extends Combine { })), - "
", - AttributionPanel.CodeContributors(), - "

", Translations.t.general.attribution.iconAttribution.title.Clone().SetClass("pt-6 pb-3"), "

", - ...Utils.NoNull(Array.from(layoutToUse.ExtractImages())) - .map(AttributionPanel.IconAttribution) - ]); + CopyrightPanel.CodeContributors(), + new Title(t.iconAttribution.title, 3), + ...iconAttributions + ].map(e => e?.SetClass("mt-4"))); this.SetClass("flex flex-col link-underline overflow-hidden") - this.SetStyle("max-width: calc(100vw - 5em); width: 40rem;") + this.SetStyle("max-width: calc(100vw - 5em); width: 40rem; margin-left: 0.75rem; margin-right: 0.5rem") } private static CodeContributors(): BaseUIElement { @@ -97,7 +171,7 @@ export default class AttributionPanel extends Combine { iconPath = "." + new URL(iconPath).pathname; } - const license: SmallLicense = AttributionPanel.LicenseObject[iconPath] + const license: SmallLicense = CopyrightPanel.LicenseObject[iconPath] if (license == undefined) { return undefined; } diff --git a/UI/BigComponents/LeftControls.ts b/UI/BigComponents/LeftControls.ts index cc8b9e4c0..f2459d002 100644 --- a/UI/BigComponents/LeftControls.ts +++ b/UI/BigComponents/LeftControls.ts @@ -1,7 +1,7 @@ import Combine from "../Base/Combine"; import ScrollableFullScreen from "../Base/ScrollableFullScreen"; import Translations from "../i18n/Translations"; -import AttributionPanel from "./AttributionPanel"; +import CopyrightPanel from "./CopyrightPanel"; import ContributorCount from "../../Logic/ContributorCount"; import Toggle from "../Input/Toggle"; import MapControlButton from "../MapControlButton"; @@ -37,8 +37,8 @@ export default class LeftControls extends Combine { const toggledCopyright = new ScrollableFullScreen( () => Translations.t.general.attribution.attributionTitle.Clone(), () => - new AttributionPanel( - state.layoutToUse, + new CopyrightPanel( + state, new ContributorCount(state).Contributors ), "copyright", diff --git a/UI/DefaultGUI.ts b/UI/DefaultGUI.ts index f6c551663..a4de2d460 100644 --- a/UI/DefaultGUI.ts +++ b/UI/DefaultGUI.ts @@ -65,7 +65,7 @@ export class DefaultGuiState { if(Hash.hash.data === "download"){ this.downloadControlIsOpened.setData(true) } - if(Hash.hash.data === "filter"){ + if(Hash.hash.data === "filters"){ this.filterViewIsOpened.setData(true) } if(Hash.hash.data === "copyright"){ diff --git a/assets/svg/liberapay.svg b/assets/svg/liberapay.svg new file mode 100644 index 000000000..23a0df206 --- /dev/null +++ b/assets/svg/liberapay.svg @@ -0,0 +1 @@ + diff --git a/assets/svg/license_info.json b/assets/svg/license_info.json index 837704d79..a3e38008d 100644 --- a/assets/svg/license_info.json +++ b/assets/svg/license_info.json @@ -879,6 +879,16 @@ ], "sources": [] }, + { + "path": "liberapay.svg", + "license": "Logo (all rights reserved)", + "authors": [ + "LiberaPay" + ], + "sources": [ + "https://liberapay.com/" + ] + }, { "path": "loading.svg", "license": "CC0; trivial", diff --git a/assets/themes/grb_import/grb.json b/assets/themes/grb_import/grb.json index 7979fe324..6f3cbd5db 100644 --- a/assets/themes/grb_import/grb.json +++ b/assets/themes/grb_import/grb.json @@ -11,7 +11,8 @@ "en": "This theme is an attempt to help automating the GRB import.
Note that this is very hacky and 'steals' the GRB data from an external site; in order to do this, you need to install and activate this firefox extension for it to work." }, "language": [ - "nl" + "nl", + "en" ], "maintainer": "", "icon": "./assets/svg/bug.svg", diff --git a/langs/en.json b/langs/en.json index 0e8640fde..f91d76a4e 100644 --- a/langs/en.json +++ b/langs/en.json @@ -161,6 +161,14 @@ "iconAttribution": { "title": "Used icons" }, + "openIssueTracker": "File a bug", + "editJosm": "Edit here with JOSM", + "josmOpened": "JOSM is opened", + "josmNotOpened": "JOSM could not be reached. Make sure it is opened and remote control is enabled", + "editId": "Open the OpenStreetMap online editor here", + "openMapillary": "Open Mapillary here", + "donate": "Support MapComplete financially", + "openOsmcha": "See latest edits made with {theme}", "mapContributionsBy": "The current visible data has edits made by {contributors}", "mapContributionsByAndHidden": "The current visible data has edits made by {contributors} and {hiddenCount} more contributors", "codeContributionsBy": "MapComplete has been built by {contributors} and {hiddenCount} more contributors" diff --git a/langs/themes/en.json b/langs/themes/en.json index 283c144aa..7d5a45d34 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -792,6 +792,9 @@ "description": "A ghost bike is a memorial for a cyclist who died in a traffic accident, in the form of a white bicycle placed permanently near the accident location.

On this map, one can see all the ghost bikes which are known by OpenStreetMap. Is a ghost bike missing? Everyone can add or update information here - you only need to have a (free) OpenStreetMap account.", "title": "Ghost bikes" }, + "grb": { + "description": "This theme is an attempt to help automating the GRB import.
Note that this is very hacky and 'steals' the GRB data from an external site; in order to do this, you need to install and activate this firefox extension for it to work." + }, "hackerspaces": { "description": "On this map you can see hackerspaces, add a new hackerspace or update data directly", "layers": { From d8a0b3d3b69ed005185eb541fa16aa33fd61d12b Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 30 Oct 2021 01:59:17 +0200 Subject: [PATCH 56/95] Fix OsmCha link --- Utils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Utils.ts b/Utils.ts index 4c80de5b0..40fb518ee 100644 --- a/Utils.ts +++ b/Utils.ts @@ -457,11 +457,11 @@ export class Utils { const now = new Date() const lastWeek = new Date(now.getTime() - daysInThePast * 24 * 60 * 60 * 1000) const date = lastWeek.getFullYear() + "-" + Utils.TwoDigits(lastWeek.getMonth() + 1) + "-" + Utils.TwoDigits(lastWeek.getDate()) - let osmcha_link = `{"date__gte":[{"label":"${date}","value":"${date}"}],"editor":[{"label":"mapcomplete","value":"mapcomplete"}]}` + let osmcha_link = `"date__gte":[{"label":"${date}","value":"${date}"}],"editor":[{"label":"mapcomplete","value":"mapcomplete"}]` if (theme !== undefined) { - osmcha_link = osmcha_link + "," + `{"comment":[{"label":"#${theme}","value":"#${theme}"}]` + osmcha_link = osmcha_link + "," + `"comment":[{"label":"#${theme}","value":"#${theme}"}]` } - return "https://osmcha.org/?filters=" + encodeURIComponent(osmcha_link) + return "https://osmcha.org/?filters=" + encodeURIComponent("{"+osmcha_link+"}") } private static colorDiff(c0: { r: number, g: number, b: number }, c1: { r: number, g: number, b: number }) { From f5d6441b70efb1797ddc4c13539599d9c4bbd4ad Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 30 Oct 2021 02:34:16 +0200 Subject: [PATCH 57/95] Add layer icon to filter view --- Logic/Osm/Actions/CreateNewWayAction.ts | 1 + Logic/Tags/TagUtils.ts | 18 ++++++------ Models/ThemeConfig/PointRenderingConfig.ts | 11 ++++++-- UI/BigComponents/FilterView.ts | 29 +++++++++++++++----- assets/themes/ghostbikes/ghostbikes.json | 3 ++ css/index-tailwind-output.css | 32 ++++++++-------------- 6 files changed, 55 insertions(+), 39 deletions(-) diff --git a/Logic/Osm/Actions/CreateNewWayAction.ts b/Logic/Osm/Actions/CreateNewWayAction.ts index 0fa9d9912..098115636 100644 --- a/Logic/Osm/Actions/CreateNewWayAction.ts +++ b/Logic/Osm/Actions/CreateNewWayAction.ts @@ -39,6 +39,7 @@ export default class CreateNewWayAction extends OsmChangeAction { } const newPoint = new CreateNewNodeAction([], coordinate.lat, coordinate.lon, { + allowReuseOfPreviouslyCreatedPoints: true, changeType: null, theme: this._options.theme }) diff --git a/Logic/Tags/TagUtils.ts b/Logic/Tags/TagUtils.ts index 06949c570..0ff6896bb 100644 --- a/Logic/Tags/TagUtils.ts +++ b/Logic/Tags/TagUtils.ts @@ -19,16 +19,6 @@ export class TagUtils { [">", (a, b) => a > b], ] - static ApplyTemplate(template: string, tags: any): string { - for (const k in tags) { - while (template.indexOf("{" + k + "}") >= 0) { - const escaped = tags[k].replace(//g, '>'); - template = template.replace("{" + k + "}", escaped); - } - } - return template; - } - static KVtoProperties(tags: Tag[]): any { const properties = {}; for (const tag of tags) { @@ -37,6 +27,14 @@ export class TagUtils { return properties; } + static changeAsProperties(kvs : {k: string, v: string}[]): any { + const tags = {} + for (const kv of kvs) { + tags[kv.k] = kv.v + } + return tags + } + /** * Given two hashes of {key --> values[]}, makes sure that every neededTag is present in availableTags */ diff --git a/Models/ThemeConfig/PointRenderingConfig.ts b/Models/ThemeConfig/PointRenderingConfig.ts index 4eb6d2f46..666f9d047 100644 --- a/Models/ThemeConfig/PointRenderingConfig.ts +++ b/Models/ThemeConfig/PointRenderingConfig.ts @@ -187,7 +187,10 @@ export default class PointRenderingConfig extends WithContextLoader { public GenerateLeafletStyle( tags: UIEventSource, - clickable: boolean + clickable: boolean, + options?: { + noSize: false | boolean + } ): { html: BaseUIElement; @@ -237,9 +240,13 @@ export default class PointRenderingConfig extends WithContextLoader { const iconAndBadges = new Combine([this.GetSimpleIcon(tags), this.GetBadges(tags)]) - .SetStyle(`width: ${iconW}px; height: ${iconH}px`) .SetClass("block relative") + if(!options?.noSize){ + iconAndBadges.SetStyle(`width: ${iconW}px; height: ${iconH}px`) + }else{ + iconAndBadges.SetClass("w-full h-full") + } return { html: new Combine([iconAndBadges, this.GetLabel(tags)]).SetStyle("flex flex-col"), diff --git a/UI/BigComponents/FilterView.ts b/UI/BigComponents/FilterView.ts index 71506c398..c44a61c70 100644 --- a/UI/BigComponents/FilterView.ts +++ b/UI/BigComponents/FilterView.ts @@ -14,6 +14,7 @@ import FilteredLayer from "../../Models/FilteredLayer"; import BackgroundSelector from "./BackgroundSelector"; import FilterConfig from "../../Models/ThemeConfig/FilterConfig"; import TilesourceConfig from "../../Models/ThemeConfig/TilesourceConfig"; +import {TagUtils} from "../../Logic/Tags/TagUtils"; export default class FilterView extends VariableUiElement { constructor(filteredLayer: UIEventSource, tileLayers: { config: TilesourceConfig, isDisplayed: UIEventSource }[]) { @@ -42,9 +43,8 @@ export default class FilterView extends VariableUiElement { ); const name: Translation = config.config.name; - const styledNameChecked = name.Clone().SetStyle("font-size:large;padding-left:1.25rem"); - - const styledNameUnChecked = name.Clone().SetStyle("font-size:large;padding-left:1.25rem"); + const styledNameChecked = name.Clone().SetStyle("font-size:large").SetClass("ml-2"); + const styledNameUnChecked = name.Clone().SetStyle("font-size:large").SetClass("ml-2"); const zoomStatus = new Toggle( @@ -82,6 +82,8 @@ export default class FilterView extends VariableUiElement { const iconStyle = "width:1.5rem;height:1.5rem;margin-left:1.25rem;flex-shrink: 0;"; const icon = new Combine([Svg.checkbox_filled]).SetStyle(iconStyle); + const layer = filteredLayer.layerDef + const iconUnselected = new Combine([Svg.checkbox_empty]).SetStyle( iconStyle ); @@ -95,9 +97,9 @@ export default class FilterView extends VariableUiElement { filteredLayer.layerDef.name ); - const styledNameChecked = name.Clone().SetStyle("font-size:large;padding-left:1.25rem"); + const styledNameChecked = name.Clone().SetStyle("font-size:large").SetClass("ml-3"); - const styledNameUnChecked = name.Clone().SetStyle("font-size:large;padding-left:1.25rem"); + const styledNameUnChecked = name.Clone().SetStyle("font-size:large").SetClass("ml-3"); const zoomStatus = new Toggle( @@ -111,11 +113,24 @@ export default class FilterView extends VariableUiElement { const style = "display:flex;align-items:center;padding:0.5rem 0;"; - const layerChecked = new Combine([icon, styledNameChecked, zoomStatus]) + const mapRendering = layer.mapRendering.filter(r => r.location.has("point"))[0] + let layerIcon = undefined + let layerIconUnchecked = undefined + try { + if (mapRendering !== undefined) { + const defaultTags = new UIEventSource( TagUtils.changeAsProperties(layer.source.osmTags.asChange({id: "node/-1"}))) + layerIcon = mapRendering.GenerateLeafletStyle(defaultTags, false, {noSize: true}).html.SetClass("w-8 h-8 ml-2") + layerIconUnchecked = mapRendering.GenerateLeafletStyle(defaultTags, false, {noSize: true}).html.SetClass("opacity-50 w-8 h-8 ml-2") + } + } catch (e) { + console.error(e) + } + + const layerChecked = new Combine([icon, layerIcon, styledNameChecked, zoomStatus]) .SetStyle(style) .onClick(() => filteredLayer.isDisplayed.setData(false)); - const layerNotChecked = new Combine([iconUnselected, styledNameUnChecked]) + const layerNotChecked = new Combine([iconUnselected, layerIconUnchecked, styledNameUnChecked]) .SetStyle(style) .onClick(() => filteredLayer.isDisplayed.setData(true)); diff --git a/assets/themes/ghostbikes/ghostbikes.json b/assets/themes/ghostbikes/ghostbikes.json index 97c580617..f163271ca 100644 --- a/assets/themes/ghostbikes/ghostbikes.json +++ b/assets/themes/ghostbikes/ghostbikes.json @@ -53,6 +53,9 @@ "startLat": 0, "startLon": 0, "widenFactor": 5, + "clustering": { + "maxZoom": 0 + }, "layers": [ "ghost_bike" ], diff --git a/css/index-tailwind-output.css b/css/index-tailwind-output.css index 0245a30f9..658e97769 100644 --- a/css/index-tailwind-output.css +++ b/css/index-tailwind-output.css @@ -868,6 +868,14 @@ video { margin-right: 1rem; } +.mt-4 { + margin-top: 1rem; +} + +.ml-2 { + margin-left: 0.5rem; +} + .mt-3 { margin-top: 0.75rem; } @@ -892,18 +900,10 @@ video { margin-top: 0px; } -.ml-2 { - margin-left: 0.5rem; -} - .mb-4 { margin-bottom: 1rem; } -.mt-4 { - margin-top: 1rem; -} - .mb-8 { margin-bottom: 2rem; } @@ -1417,14 +1417,6 @@ video { padding-right: 0.25rem; } -.pt-6 { - padding-top: 1.5rem; -} - -.pb-3 { - padding-bottom: 0.75rem; -} - .pl-5 { padding-left: 1.25rem; } @@ -1593,14 +1585,14 @@ video { text-decoration: underline; } -.opacity-0 { - opacity: 0; -} - .opacity-50 { opacity: 0.5; } +.opacity-0 { + opacity: 0; +} + .opacity-40 { opacity: 0.4; } From 23ae9d39c88bb368dc359906793e3b8d9a0edec2 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sun, 31 Oct 2021 02:08:39 +0100 Subject: [PATCH 58/95] Add the possibility to snap onto another layer with imports, add location confirm on input, add metalayer exporting all nodes, various fixes --- Logic/FeatureSource/FeaturePipeline.ts | 14 + .../Sources/SimpleFeatureSource.ts | 2 - .../FullNodeDatabaseSource.ts | 70 +++++ .../TiledFeatureSource/OsmFeatureSource.ts | 8 +- Logic/GeoOperations.ts | 7 + Logic/Osm/Actions/CreateNewWayAction.ts | 39 ++- Logic/Osm/OsmObject.ts | 2 +- Models/ThemeConfig/LayerConfig.ts | 17 +- Models/ThemeConfig/LayoutConfig.ts | 27 +- UI/BigComponents/FilterView.ts | 17 +- UI/BigComponents/ImportButton.ts | 271 +++++++++++++++--- UI/BigComponents/SimpleAddUI.ts | 213 ++------------ UI/DefaultGUI.ts | 1 + UI/Input/LocationInput.ts | 5 + UI/NewPoint/ConfirmLocationOfPoint.ts | 184 ++++++++++++ UI/SpecialVisualizations.ts | 95 +----- UI/SubstitutedTranslation.ts | 3 +- Utils.ts | 12 + assets/layers/type_node/type_node.json | 12 + assets/themes/grb_import/grb.json | 17 +- assets/themes/uk_addresses/uk_addresses.json | 2 +- index.ts | 2 + scripts/generateLayerOverview.ts | 31 +- test.ts | 146 +++++++++- 24 files changed, 807 insertions(+), 390 deletions(-) create mode 100644 Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource.ts create mode 100644 UI/NewPoint/ConfirmLocationOfPoint.ts create mode 100644 assets/layers/type_node/type_node.json diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index 0f2fa19ef..ecec7cfde 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -26,6 +26,7 @@ import {OsmConnection} from "../Osm/OsmConnection"; import {Tiles} from "../../Models/TileRange"; import TileFreshnessCalculator from "./TileFreshnessCalculator"; import {ElementStorage} from "../ElementStorage"; +import FullNodeDatabaseSource from "./TiledFeatureSource/FullNodeDatabaseSource"; /** @@ -146,6 +147,11 @@ export default class FeaturePipeline { this.freshnesses.set(id, new TileFreshnessCalculator()) + if(id === "type_node"){ + // Handles by the 'FullNodeDatabaseSource' + continue; + } + if (source.geojsonSource === undefined) { // This is an OSM layer // We load the cached values and register them @@ -220,6 +226,14 @@ export default class FeaturePipeline { self.freshnesses.get(flayer.layerDef.id).addTileLoad(tileId, new Date()) }) }) + + if(state.layoutToUse.trackAllNodes){ + new FullNodeDatabaseSource(state, osmFeatureSource, tile => { + new RegisteringAllFromFeatureSourceActor(tile) + perLayerHierarchy.get(tile.layer.layerDef.id).registerTile(tile) + tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile)) + }) + } const updater = this.initOverpassUpdater(state, useOsmApi) diff --git a/Logic/FeatureSource/Sources/SimpleFeatureSource.ts b/Logic/FeatureSource/Sources/SimpleFeatureSource.ts index b06aae6d2..fd98ad92e 100644 --- a/Logic/FeatureSource/Sources/SimpleFeatureSource.ts +++ b/Logic/FeatureSource/Sources/SimpleFeatureSource.ts @@ -1,8 +1,6 @@ import {UIEventSource} from "../../UIEventSource"; import FilteredLayer from "../../../Models/FilteredLayer"; import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; -import {Utils} from "../../../Utils"; -import {Tiles} from "../../../Models/TileRange"; import {BBox} from "../../BBox"; export default class SimpleFeatureSource implements FeatureSourceForLayer, Tiled { diff --git a/Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource.ts b/Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource.ts new file mode 100644 index 000000000..20d7bee07 --- /dev/null +++ b/Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource.ts @@ -0,0 +1,70 @@ +import TileHierarchy from "./TileHierarchy"; +import FeatureSource, {FeatureSourceForLayer, Tiled} from "../FeatureSource"; +import {OsmNode, OsmObject, OsmWay} from "../../Osm/OsmObject"; +import SimpleFeatureSource from "../Sources/SimpleFeatureSource"; +import {UIEventSource} from "../../UIEventSource"; +import FilteredLayer from "../../../Models/FilteredLayer"; + + +export default class FullNodeDatabaseSource implements TileHierarchy { + public readonly loadedTiles = new Map() + private readonly onTileLoaded: (tile: (Tiled & FeatureSourceForLayer)) => void; + private readonly layer : FilteredLayer + + constructor( + state: { + readonly filteredLayers: UIEventSource}, + osmFeatureSource: { rawDataHandlers: ((data: any, tileId: number) => void)[] }, + onTileLoaded: ((tile: Tiled & FeatureSourceForLayer) => void)) { + this.onTileLoaded = onTileLoaded + this.layer = state.filteredLayers.data.filter(l => l.layerDef.id === "type_node")[0] + if(this.layer === undefined){ + throw "Weird: tracking all nodes, but layer 'type_node' is not defined" + } + const self = this + osmFeatureSource.rawDataHandlers.push((osmJson, tileId) => self.handleOsmXml(osmJson, tileId)) + } + + private handleOsmXml(osmJson: any, tileId: number) { + + const allObjects = OsmObject.ParseObjects(osmJson.elements) + const nodesById = new Map() + + for (const osmObj of allObjects) { + if (osmObj.type !== "node") { + continue + } + const osmNode = osmObj; + nodesById.set(osmNode.id, osmNode) + } + + const parentWaysByNodeId = new Map() + for (const osmObj of allObjects) { + if (osmObj.type !== "way") { + continue + } + const osmWay = osmObj; + for (const nodeId of osmWay.nodes) { + + if (!parentWaysByNodeId.has(nodeId)) { + parentWaysByNodeId.set(nodeId, []) + } + parentWaysByNodeId.get(nodeId).push(osmWay) + } + } + parentWaysByNodeId.forEach((allWays, nodeId) => { + nodesById.get(nodeId).tags["parent_ways"] = JSON.stringify(allWays.map(w => w.tags)) + }) + const now = new Date() + const asGeojsonFeatures = Array.from(nodesById.values()).map(osmNode => ({ + feature: osmNode.asGeoJson(),freshness: now + })) + + const featureSource = new SimpleFeatureSource(this.layer, tileId) + featureSource.features.setData(asGeojsonFeatures) + this.loadedTiles.set(tileId, featureSource) + this.onTileLoaded(featureSource) + + } + +} \ No newline at end of file diff --git a/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts b/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts index a4bdc8ca5..29e26bd90 100644 --- a/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts +++ b/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts @@ -30,6 +30,8 @@ export default class OsmFeatureSource { }; public readonly downloadedTiles = new Set() private readonly allowedTags: TagsFilter; + + public rawDataHandlers: ((osmJson: any, tileId: number) => void)[] = [] constructor(options: { handleTile: (tile: FeatureSourceForLayer & Tiled) => void; @@ -94,11 +96,11 @@ export default class OsmFeatureSource { try { console.log("Attempting to get tile", z, x, y, "from the osm api") - const osmXml = await Utils.download(url, {"accept": "application/xml"}) + const osmJson = await Utils.downloadJson(url) try { - const parsed = new DOMParser().parseFromString(osmXml, "text/xml"); console.log("Got tile", z, x, y, "from the osm api") - const geojson = OsmToGeoJson.default(parsed, + this.rawDataHandlers.forEach(handler => handler(osmJson, Tiles.tile_index(z, x, y))) + const geojson = OsmToGeoJson.default(osmJson, // @ts-ignore { flatProperties: true diff --git a/Logic/GeoOperations.ts b/Logic/GeoOperations.ts index 09a288117..f94f9944a 100644 --- a/Logic/GeoOperations.ts +++ b/Logic/GeoOperations.ts @@ -235,6 +235,13 @@ export class GeoOperations { * @param point Point defined as [lon, lat] */ public static nearestPoint(way, point: [number, number]) { + if(way.geometry.type === "Polygon"){ + way = {...way} + way.geometry = {...way.geometry} + way.geometry.type = "LineString" + way.geometry.coordinates = way.geometry.coordinates[0] + } + return turf.nearestPointOnLine(way, point, {units: "kilometers"}); } diff --git a/Logic/Osm/Actions/CreateNewWayAction.ts b/Logic/Osm/Actions/CreateNewWayAction.ts index 098115636..ec5486121 100644 --- a/Logic/Osm/Actions/CreateNewWayAction.ts +++ b/Logic/Osm/Actions/CreateNewWayAction.ts @@ -4,23 +4,40 @@ import {Changes} from "../Changes"; import {Tag} from "../../Tags/Tag"; import CreateNewNodeAction from "./CreateNewNodeAction"; import {And} from "../../Tags/And"; +import {TagsFilter} from "../../Tags/TagsFilter"; export default class CreateNewWayAction extends OsmChangeAction { public newElementId: string = undefined private readonly coordinates: ({ nodeId?: number, lat: number, lon: number })[]; private readonly tags: Tag[]; - private readonly _options: { theme: string }; + private readonly _options: { + theme: string, existingPointHandling?: { + withinRangeOfM: number, + ifMatches?: TagsFilter, + mode: "reuse_osm_point" | "move_osm_point" + } [] + }; /*** * Creates a new way to upload to OSM * @param tags: the tags to apply to the wya * @param coordinates: the coordinates. Might have a nodeId, in this case, this node will be used - * @param options + * @param options */ - constructor(tags: Tag[], coordinates: ({ nodeId?: number, lat: number, lon: number })[], options: { - theme: string - }) { + constructor(tags: Tag[], coordinates: ({ nodeId?: number, lat: number, lon: number })[], + options: { + theme: string, + /** + * IF specified, an existing OSM-point within this range and satisfying the condition 'ifMatches' will be used instead of a new coordinate. + * If multiple points are possible, only the closest point is considered + */ + existingPointHandling?: { + withinRangeOfM: number, + ifMatches?: TagsFilter, + mode: "reuse_osm_point" | "move_osm_point" + } [] + }) { super() this.coordinates = coordinates; this.tags = tags; @@ -49,14 +66,14 @@ export default class CreateNewWayAction extends OsmChangeAction { // We have all created (or reused) all the points! // Time to create the actual way - - + + const id = changes.getNewID() - - const newWay = { + + const newWay = { id, type: "way", - meta:{ + meta: { theme: this._options.theme, changeType: "import" }, @@ -67,7 +84,7 @@ export default class CreateNewWayAction extends OsmChangeAction { } } newElements.push(newWay) - this.newElementId = "way/"+id + this.newElementId = "way/" + id return newElements } diff --git a/Logic/Osm/OsmObject.ts b/Logic/Osm/OsmObject.ts index faf1ee79d..cfc9c5234 100644 --- a/Logic/Osm/OsmObject.ts +++ b/Logic/Osm/OsmObject.ts @@ -206,7 +206,7 @@ export abstract class OsmObject { return result; } - private static ParseObjects(elements: any[]): OsmObject[] { + public static ParseObjects(elements: any[]): OsmObject[] { const objects: OsmObject[] = []; const allNodes: Map = new Map() diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts index e30f0a20e..3ef796cd7 100644 --- a/Models/ThemeConfig/LayerConfig.ts +++ b/Models/ThemeConfig/LayerConfig.ts @@ -15,6 +15,8 @@ import LineRenderingConfig from "./LineRenderingConfig"; import PointRenderingConfigJson from "./Json/PointRenderingConfigJson"; import LineRenderingConfigJson from "./Json/LineRenderingConfigJson"; import {TagRenderingConfigJson} from "./Json/TagRenderingConfigJson"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import BaseUIElement from "../../UI/BaseUIElement"; export default class LayerConfig extends WithContextLoader { @@ -59,11 +61,11 @@ export default class LayerConfig extends WithContextLoader { this.id = json.id; if (json.source === undefined) { - throw "Layer " + this.id + " does not define a source section ("+context+")" + throw "Layer " + this.id + " does not define a source section (" + context + ")" } if (json.source.osmTags === undefined) { - throw "Layer " + this.id + " does not define a osmTags in the source section - these should always be present, even for geojson layers ("+context+")" + throw "Layer " + this.id + " does not define a osmTags in the source section - these should always be present, even for geojson layers (" + context + ")" } @@ -262,6 +264,15 @@ export default class LayerConfig extends WithContextLoader { } } + public defaultIcon() : BaseUIElement | undefined{ + const mapRendering = this.mapRendering.filter(r => r.location.has("point"))[0] + if (mapRendering === undefined) { + return undefined + } + const defaultTags = new UIEventSource(TagUtils.changeAsProperties(this.source.osmTags.asChange({id: "node/-1"}))) + return mapRendering.GenerateLeafletStyle(defaultTags, false, {noSize: true}).html + } + public ExtractLayerTagRenderings(json: LayerConfigJson): TagRenderingConfig[] { if (json.tagRenderings === undefined) { @@ -358,7 +369,6 @@ export default class LayerConfig extends WithContextLoader { } - public CustomCodeSnippets(): string[] { if (this.calculatedTags === undefined) { return []; @@ -366,7 +376,6 @@ export default class LayerConfig extends WithContextLoader { return this.calculatedTags.map((code) => code[1]); } - public ExtractImages(): Set { const parts: Set[] = []; parts.push(...this.tagRenderings?.map((tr) => tr.ExtractImages(false))); diff --git a/Models/ThemeConfig/LayoutConfig.ts b/Models/ThemeConfig/LayoutConfig.ts index bb492e047..8702a3cbf 100644 --- a/Models/ThemeConfig/LayoutConfig.ts +++ b/Models/ThemeConfig/LayoutConfig.ts @@ -1,7 +1,5 @@ import {Translation} from "../../UI/i18n/Translation"; -import TagRenderingConfig from "./TagRenderingConfig"; import {LayoutConfigJson} from "./Json/LayoutConfigJson"; -import SharedTagRenderings from "../../Customizations/SharedTagRenderings"; import AllKnownLayers from "../../Customizations/AllKnownLayers"; import {Utils} from "../../Utils"; import LayerConfig from "./LayerConfig"; @@ -54,6 +52,7 @@ export default class LayoutConfig { public readonly overpassMaxZoom: number public readonly osmApiTileSize: number public readonly official: boolean; + public readonly trackAllNodes : boolean; constructor(json: LayoutConfigJson, official = true, context?: string) { this.official = official; @@ -63,6 +62,8 @@ export default class LayoutConfig { this.credits = json.credits; this.version = json.version; this.language = []; + this.trackAllNodes = false + if (typeof json.language === "string") { this.language = [json.language]; } else { @@ -92,12 +93,16 @@ export default class LayoutConfig { if(json.widenFactor > 20){ throw "Widenfactor is very big, use a value between 1 and 5 (current value is "+json.widenFactor+") at "+context } + this.widenFactor = json.widenFactor ?? 1.5; this.defaultBackgroundId = json.defaultBackgroundId; this.tileLayerSources = (json.tileLayerSources??[]).map((config, i) => new TilesourceConfig(config, `${this.id}.tileLayerSources[${i}]`)) - this.layers = LayoutConfig.ExtractLayers(json, official, context); - + const layerInfo = LayoutConfig.ExtractLayers(json, official, context); + this.layers = layerInfo.layers + this.trackAllNodes = layerInfo.extractAllNodes + + this.clustering = { maxZoom: 16, minNeededElements: 25, @@ -147,10 +152,11 @@ export default class LayoutConfig { } - private static ExtractLayers(json: LayoutConfigJson, official: boolean, context: string): LayerConfig[] { + private static ExtractLayers(json: LayoutConfigJson, official: boolean, context: string): {layers: LayerConfig[], extractAllNodes: boolean} { const result: LayerConfig[] = [] - + let exportAllNodes = false json.layers.forEach((layer, i) => { + if (typeof layer === "string") { if (AllKnownLayers.sharedLayersJson.get(layer) !== undefined) { if (json.overrideAll !== undefined) { @@ -177,12 +183,19 @@ export default class LayoutConfig { result.push(newLayer) return } + // @ts-ignore let names = layer.builtin; if (typeof names === "string") { names = [names] } names.forEach(name => { + + if(name === "type_node"){ + // This is a very special layer which triggers special behaviour + exportAllNodes = true; + } + const shared = AllKnownLayers.sharedLayersJson.get(name); if (shared === undefined) { throw `Unknown shared/builtin layer ${name} at ${context}.layers[${i}]. Available layers are ${Array.from(AllKnownLayers.sharedLayersJson.keys()).join(", ")}`; @@ -199,7 +212,7 @@ export default class LayoutConfig { }); - return result + return {layers: result, extractAllNodes: exportAllNodes} } public CustomCodeSnippets(): string[] { diff --git a/UI/BigComponents/FilterView.ts b/UI/BigComponents/FilterView.ts index c44a61c70..3bbfccb90 100644 --- a/UI/BigComponents/FilterView.ts +++ b/UI/BigComponents/FilterView.ts @@ -14,7 +14,6 @@ import FilteredLayer from "../../Models/FilteredLayer"; import BackgroundSelector from "./BackgroundSelector"; import FilterConfig from "../../Models/ThemeConfig/FilterConfig"; import TilesourceConfig from "../../Models/ThemeConfig/TilesourceConfig"; -import {TagUtils} from "../../Logic/Tags/TagUtils"; export default class FilterView extends VariableUiElement { constructor(filteredLayer: UIEventSource, tileLayers: { config: TilesourceConfig, isDisplayed: UIEventSource }[]) { @@ -83,7 +82,7 @@ export default class FilterView extends VariableUiElement { const icon = new Combine([Svg.checkbox_filled]).SetStyle(iconStyle); const layer = filteredLayer.layerDef - + const iconUnselected = new Combine([Svg.checkbox_empty]).SetStyle( iconStyle ); @@ -113,18 +112,8 @@ export default class FilterView extends VariableUiElement { const style = "display:flex;align-items:center;padding:0.5rem 0;"; - const mapRendering = layer.mapRendering.filter(r => r.location.has("point"))[0] - let layerIcon = undefined - let layerIconUnchecked = undefined - try { - if (mapRendering !== undefined) { - const defaultTags = new UIEventSource( TagUtils.changeAsProperties(layer.source.osmTags.asChange({id: "node/-1"}))) - layerIcon = mapRendering.GenerateLeafletStyle(defaultTags, false, {noSize: true}).html.SetClass("w-8 h-8 ml-2") - layerIconUnchecked = mapRendering.GenerateLeafletStyle(defaultTags, false, {noSize: true}).html.SetClass("opacity-50 w-8 h-8 ml-2") - } - } catch (e) { - console.error(e) - } + const layerIcon = layer.defaultIcon()?.SetClass("w-8 h-8 ml-2") + const layerIconUnchecked = layer.defaultIcon()?.SetClass("opacity-50 w-8 h-8 ml-2") const layerChecked = new Combine([icon, layerIcon, styledNameChecked, zoomStatus]) .SetStyle(style) diff --git a/UI/BigComponents/ImportButton.ts b/UI/BigComponents/ImportButton.ts index 362237e00..9c206f249 100644 --- a/UI/BigComponents/ImportButton.ts +++ b/UI/BigComponents/ImportButton.ts @@ -16,29 +16,165 @@ import {OsmConnection} from "../../Logic/Osm/OsmConnection"; import {Changes} from "../../Logic/Osm/Changes"; import {ElementStorage} from "../../Logic/ElementStorage"; import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline"; +import Lazy from "../Base/Lazy"; +import ConfirmLocationOfPoint from "../NewPoint/ConfirmLocationOfPoint"; +import {PresetInfo} from "./SimpleAddUI"; +import Img from "../Base/Img"; +import {Translation} from "../i18n/Translation"; +import FilteredLayer from "../../Models/FilteredLayer"; +import SpecialVisualizations, {SpecialVisualization} from "../SpecialVisualizations"; +import {FixedUiElement} from "../Base/FixedUiElement"; +import Svg from "../../Svg"; +import {Utils} from "../../Utils"; + + +export interface ImportButtonState { + description?: Translation; + image: () => BaseUIElement, + message: string | BaseUIElement, + originalTags: UIEventSource, + newTags: UIEventSource, + targetLayer: FilteredLayer, + feature: any, + minZoom: number, + state: { + featureSwitchUserbadge: UIEventSource; + featurePipeline: FeaturePipeline; + allElements: ElementStorage; + selectedElement: UIEventSource; + layoutToUse: LayoutConfig, + osmConnection: OsmConnection, + changes: Changes, + locationControl: UIEventSource<{ zoom: number }> + }, + guiState: { filterViewIsOpened: UIEventSource }, + snapToLayers?: string[], + snapToLayersMaxDist?: number +} + +export class ImportButtonSpecialViz implements SpecialVisualization { + funcName = "import_button" + docs = `This button will copy the data from an external dataset into OpenStreetMap. It is only functional in official themes but can be tested in unofficial themes. + +#### Importing a dataset into OpenStreetMap: requirements + +If you want to import a dataset, make sure that: + +1. The dataset to import has a suitable license +2. The community has been informed of the import +3. All other requirements of the [import guidelines](https://wiki.openstreetmap.org/wiki/Import/Guidelines) have been followed + +There are also some technicalities in your theme to keep in mind: + +1. The new feature will be added and will flow through the program as any other new point as if it came from OSM. + This means that there should be a layer which will match the new tags and which will display it. +2. The original feature from your geojson layer will gain the tag '_imported=yes'. + This should be used to change the appearance or even to hide it (eg by changing the icon size to zero) +3. There should be a way for the theme to detect previously imported points, even after reloading. + A reference number to the original dataset is an excellent way to do this +4. When importing ways, the theme creator is also responsible of avoiding overlapping ways. + +#### Disabled in unofficial themes + +The import button can be tested in an unofficial theme by adding \`test=true\` or \`backend=osm-test\` as [URL-paramter](URL_Parameters.md). +The import button will show up then. If in testmode, you can read the changeset-XML directly in the web console. +In the case that MapComplete is pointed to the testing grounds, the edit will be made on ${OsmConnection.oauth_configs["osm-test"].url} + + +#### Specifying which tags to copy or add + +The first argument of the import button takes a \`;\`-seperated list of tags to add. + +${Utils.Special_visualizations_tagsToApplyHelpText} + + +` + args = [ + { + name: "targetLayer", + doc: "The id of the layer where this point should end up. This is not very strict, it will simply result in checking that this layer is shown preventing possible duplicate elements" + }, + { + name: "tags", + doc: "The tags to add onto the new object - see specification above" + }, + { + name: "text", + doc: "The text to show on the button", + defaultValue: "Import this data into OpenStreetMap" + }, + { + name: "icon", + doc: "A nice icon to show in the button", + defaultValue: "./assets/svg/addSmall.svg" + }, + { + name: "minzoom", + doc: "How far the contributor must zoom in before being able to import the point", + defaultValue: "18" + }, { + name: "Snap onto layer(s)", + doc: "If a way of the given layer is closeby, will snap the new point onto this way (similar as preset might snap). To show multiple layers to snap onto, use a `;`-seperated list", + }, { + name: "snap max distance", + doc: "The maximum distance that this point will move to snap onto a layer (in meters)", + defaultValue: "5" + }] + + constr(state, tagSource, args, guiState) { + if (!state.layoutToUse.official && !(state.featureSwitchIsTesting.data || state.osmConnection._oauth_config.url === OsmConnection.oauth_configs["osm-test"].url)) { + return new Combine([new FixedUiElement("The import button is disabled for unofficial themes to prevent accidents.").SetClass("alert"), + new FixedUiElement("To test, add test=true or backend=osm-test to the URL. The changeset will be printed in the console. Please open a PR to officialize this theme to actually enable the import button.")]) + } + const newTags = SpecialVisualizations.generateTagsToApply(args[1], tagSource) + const id = tagSource.data.id; + const feature = state.allElements.ContainingFeatures.get(id) + let minZoom = args[4] == "" ? 18 : Number(args[4]) + if(isNaN(minZoom)){ + console.warn("Invalid minzoom:", minZoom) + minZoom = 18 + } + const message = args[2] + const imageUrl = args[3] + let img: () => BaseUIElement + const targetLayer: FilteredLayer = state.filteredLayers.data.filter(fl => fl.layerDef.id === args[0])[0] + + if (imageUrl !== undefined && imageUrl !== "") { + img = () => new Img(imageUrl) + } else { + img = () => Svg.add_ui() + } + + const snapToLayers = args[5]?.split(";").filter(s => s !== "") + const snapToLayersMaxDist = Number(args[6] ?? 6) + + if (targetLayer === undefined) { + const e = "Target layer not defined: error in import button for theme: " + state.layoutToUse.id + ": layer " + args[0] + " not found" + console.error(e) + return new FixedUiElement(e).SetClass("alert") + } + + return new ImportButton( + { + state, guiState, image: img, + feature, newTags, message, minZoom, + originalTags: tagSource, + targetLayer, + snapToLayers, + snapToLayersMaxDist + } + ); + } +} export default class ImportButton extends Toggle { - constructor(imageUrl: string | BaseUIElement, - message: string | BaseUIElement, - originalTags: UIEventSource, - newTags: UIEventSource, - feature: any, - minZoom: number, - state: { - featureSwitchUserbadge: UIEventSource; - featurePipeline: FeaturePipeline; - allElements: ElementStorage; - selectedElement: UIEventSource; - layoutToUse: LayoutConfig, - osmConnection: OsmConnection, - changes: Changes, - locationControl: UIEventSource<{ zoom: number }> - }) { + + constructor(o: ImportButtonState) { const t = Translations.t.general.add; - const isImported = originalTags.map(tags => tags._imported === "yes") + const isImported = o.originalTags.map(tags => tags._imported === "yes") const appliedTags = new Toggle( new VariableUiElement( - newTags.map(tgs => { + o.newTags.map(tgs => { const parts = [] for (const tag of tgs) { parts.push(tag.key + "=" + tag.value) @@ -46,63 +182,106 @@ export default class ImportButton extends Toggle { const txt = parts.join(" & ") return t.presetInfo.Subs({tags: txt}).SetClass("subtle") })), undefined, - state.osmConnection.userDetails.map(ud => ud.csCount >= Constants.userJourney.tagsVisibleAt) + o.state.osmConnection.userDetails.map(ud => ud.csCount >= Constants.userJourney.tagsVisibleAt) ) - const button = new SubtleButton(imageUrl, message) + const button = new SubtleButton(o.image(), o.message) - minZoom = Math.max(16, minZoom ?? 19) + o.minZoom = Math.max(16, o.minZoom ?? 19) - button.onClick(async () => { - if (isImported.data) { - return - } - originalTags.data["_imported"] = "yes" - originalTags.ping() // will set isImported as per its definition - const newElementAction = ImportButton.createAddActionForFeature(newTags.data, feature, state.layoutToUse.id) - await state.changes.applyAction(newElementAction) - state.selectedElement.setData(state.allElements.ContainingFeatures.get( - newElementAction.newElementId - )) - console.log("Did set selected element to", state.allElements.ContainingFeatures.get( - newElementAction.newElementId - )) - - - }) const withLoadingCheck = new Toggle(new Toggle( new Loading(t.stillLoading.Clone()), new Combine([button, appliedTags]).SetClass("flex flex-col"), - state.featurePipeline.runningQuery + o.state.featurePipeline.runningQuery ), t.zoomInFurther.Clone(), - state.locationControl.map(l => l.zoom >= minZoom) + o.state.locationControl.map(l => l.zoom >= o.minZoom) ) const importButton = new Toggle(t.hasBeenImported, withLoadingCheck, isImported) + + const importClicked = new UIEventSource(false); + const importFlow = new Toggle( + new Lazy(() => ImportButton.createConfirmPanel(o, isImported, importClicked)), + importButton, + importClicked + ) + + button.onClick(() => { + importClicked.setData(true); + }) + + const pleaseLoginButton = new Toggle(t.pleaseLogin.Clone() - .onClick(() => state.osmConnection.AttemptLogin()) + .onClick(() => o.state.osmConnection.AttemptLogin()) .SetClass("login-button-friendly"), undefined, - state.featureSwitchUserbadge) + o.state.featureSwitchUserbadge) - super(new Toggle(importButton, + super(new Toggle(importFlow, pleaseLoginButton, - state.osmConnection.isLoggedIn + o.state.osmConnection.isLoggedIn ), t.wrongType, - new UIEventSource(ImportButton.canBeImported(feature)) + new UIEventSource(ImportButton.canBeImported(o.feature)) ) } + public static createConfirmPanel( + o: ImportButtonState, + isImported: UIEventSource, + importClicked: UIEventSource): BaseUIElement { + + async function confirm() { + if (isImported.data) { + return + } + o.originalTags.data["_imported"] = "yes" + o.originalTags.ping() // will set isImported as per its definition + const newElementAction = ImportButton.createAddActionForFeature(o.newTags.data, o.feature, o.state.layoutToUse.id) + await o.state.changes.applyAction(newElementAction) + o.state.selectedElement.setData(o.state.allElements.ContainingFeatures.get( + newElementAction.newElementId + )) + console.log("Did set selected element to", o.state.allElements.ContainingFeatures.get( + newElementAction.newElementId + )) + } + + function cancel() { + importClicked.setData(false) + } + + if (o.feature.geometry.type === "Point") { + const presetInfo = { + tags: o.newTags.data, + icon: o.image, + description: o.description, + layerToAddTo: o.targetLayer, + name: o.message, + title: o.message, + preciseInput: { snapToLayers: o.snapToLayers, + maxSnapDistance: o.snapToLayersMaxDist} + } + + const [lon, lat] = o.feature.geometry.coordinates + console.log("Creating an import dialog at location", lon, lat) + return new ConfirmLocationOfPoint(o.state, o.guiState.filterViewIsOpened, presetInfo, Translations.W(o.message), { + lon, + lat + }, confirm, cancel) + } + } + private static canBeImported(feature: any) { const type = feature.geometry.type return type === "Point" || type === "LineString" || (type === "Polygon" && feature.geometry.coordinates.length === 1) } - private static createAddActionForFeature(newTags: Tag[], feature: any, theme: string): OsmChangeAction & { newElementId: string } { + private static createAddActionForFeature(newTags: Tag[], feature: any, theme: string): + OsmChangeAction & { newElementId: string } { const geometry = feature.geometry const type = geometry.type if (type === "Point") { diff --git a/UI/BigComponents/SimpleAddUI.ts b/UI/BigComponents/SimpleAddUI.ts index 6a5b37cb9..f7931b853 100644 --- a/UI/BigComponents/SimpleAddUI.ts +++ b/UI/BigComponents/SimpleAddUI.ts @@ -12,18 +12,16 @@ import BaseUIElement from "../BaseUIElement"; import {VariableUiElement} from "../Base/VariableUIElement"; import Toggle from "../Input/Toggle"; import UserDetails, {OsmConnection} from "../../Logic/Osm/OsmConnection"; -import LocationInput from "../Input/LocationInput"; -import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"; import CreateNewNodeAction from "../../Logic/Osm/Actions/CreateNewNodeAction"; import {OsmObject, OsmWay} from "../../Logic/Osm/OsmObject"; import PresetConfig from "../../Models/ThemeConfig/PresetConfig"; import FilteredLayer from "../../Models/FilteredLayer"; -import {BBox} from "../../Logic/BBox"; import Loc from "../../Models/Loc"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; import {Changes} from "../../Logic/Osm/Changes"; import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline"; import {ElementStorage} from "../../Logic/ElementStorage"; +import ConfirmLocationOfPoint from "../NewPoint/ConfirmLocationOfPoint"; /* * The SimpleAddUI is a single panel, which can have multiple states: @@ -33,8 +31,7 @@ import {ElementStorage} from "../../Logic/ElementStorage"; * - A 'read your unread messages before adding a point' */ -/*private*/ -interface PresetInfo extends PresetConfig { +export interface PresetInfo extends PresetConfig { name: string | BaseUIElement, icon: () => BaseUIElement, layerToAddTo: FilteredLayer @@ -91,20 +88,29 @@ export default class SimpleAddUI extends Toggle { if (preset === undefined) { return presetsOverview } - return SimpleAddUI.CreateConfirmButton(state, filterViewIsOpened, preset, - (tags, location, snapOntoWayId?: string) => { - if (snapOntoWayId === undefined) { - createNewPoint(tags, location, undefined) - } else { - OsmObject.DownloadObject(snapOntoWayId).addCallbackAndRunD(way => { - createNewPoint(tags, location, way) - return true; - }) - } - }, - () => { - selectedPreset.setData(undefined) - }) + + + function confirm(tags, location, snapOntoWayId?: string) { + if (snapOntoWayId === undefined) { + createNewPoint(tags, location, undefined) + } else { + OsmObject.DownloadObject(snapOntoWayId).addCallbackAndRunD(way => { + createNewPoint(tags, location, way) + return true; + }) + } + } + + function cancel() { + selectedPreset.setData(undefined) + } + + const message =Translations.t.general.add.addNew.Subs({category: preset.name}); + return new ConfirmLocationOfPoint(state, filterViewIsOpened, preset, + message, + state.LastClickLocation.data, + confirm, + cancel) } )) @@ -134,170 +140,7 @@ export default class SimpleAddUI extends Toggle { } - private static CreateConfirmButton( - state: { - LastClickLocation: UIEventSource<{ lat: number, lon: number }>, - osmConnection: OsmConnection, - featurePipeline: FeaturePipeline - }, - filterViewIsOpened: UIEventSource, - preset: PresetInfo, - confirm: (tags: any[], location: { lat: number, lon: number }, snapOntoWayId: string) => void, - cancel: () => void): BaseUIElement { - - let location = state.LastClickLocation; - let preciseInput: LocationInput = undefined - if (preset.preciseInput !== undefined) { - // We uncouple the event source - const locationSrc = new UIEventSource({ - lat: location.data.lat, - lon: location.data.lon, - zoom: 19 - }); - - let backgroundLayer = undefined; - if (preset.preciseInput.preferredBackground) { - backgroundLayer = AvailableBaseLayers.SelectBestLayerAccordingTo(locationSrc, new UIEventSource(preset.preciseInput.preferredBackground)) - } - - let snapToFeatures: UIEventSource<{ feature: any }[]> = undefined - let mapBounds: UIEventSource = undefined - if (preset.preciseInput.snapToLayers) { - snapToFeatures = new UIEventSource<{ feature: any }[]>([]) - mapBounds = new UIEventSource(undefined) - } - - - const tags = TagUtils.KVtoProperties(preset.tags ?? []); - preciseInput = new LocationInput({ - mapBackground: backgroundLayer, - centerLocation: locationSrc, - snapTo: snapToFeatures, - snappedPointTags: tags, - maxSnapDistance: preset.preciseInput.maxSnapDistance, - bounds: mapBounds - }) - preciseInput.installBounds(0.15, true) - preciseInput.SetClass("h-32 rounded-xl overflow-hidden border border-gray").SetStyle("height: 12rem;") - - - if (preset.preciseInput.snapToLayers) { - // We have to snap to certain layers. - // Lets fetch them - - let loadedBbox: BBox = undefined - mapBounds?.addCallbackAndRunD(bbox => { - if (loadedBbox !== undefined && bbox.isContainedIn(loadedBbox)) { - // All is already there - // return; - } - - bbox = bbox.pad(2); - loadedBbox = bbox; - const allFeatures: { feature: any }[] = [] - preset.preciseInput.snapToLayers.forEach(layerId => { - state.featurePipeline.GetFeaturesWithin(layerId, bbox).forEach(feats => allFeatures.push(...feats.map(f => ({feature: f})))) - }) - snapToFeatures.setData(allFeatures) - }) - } - - } - - - let confirmButton: BaseUIElement = new SubtleButton(preset.icon(), - new Combine([ - Translations.t.general.add.addNew.Subs({category: preset.name}), - Translations.t.general.add.warnVisibleForEveryone.Clone().SetClass("alert") - ]).SetClass("flex flex-col") - ).SetClass("font-bold break-words") - .onClick(() => { - confirm(preset.tags, (preciseInput?.GetValue() ?? location).data, preciseInput?.snappedOnto?.data?.properties?.id); - }); - - if (preciseInput !== undefined) { - confirmButton = new Combine([preciseInput, confirmButton]) - } - - const openLayerControl = - new SubtleButton( - Svg.layers_ui(), - new Combine([ - Translations.t.general.add.layerNotEnabled - .Subs({layer: preset.layerToAddTo.layerDef.name}) - .SetClass("alert"), - Translations.t.general.add.openLayerControl - ]) - ) - .onClick(() => filterViewIsOpened.setData(true)) - - - const openLayerOrConfirm = new Toggle( - confirmButton, - openLayerControl, - preset.layerToAddTo.isDisplayed - ) - - const disableFilter = new SubtleButton( - new Combine([ - Svg.filter_ui().SetClass("absolute w-full"), - Svg.cross_bottom_right_svg().SetClass("absolute red-svg") - ]).SetClass("relative"), - new Combine( - [ - Translations.t.general.add.disableFiltersExplanation.Clone(), - Translations.t.general.add.disableFilters.Clone().SetClass("text-xl") - ] - ).SetClass("flex flex-col") - ).onClick(() => { - preset.layerToAddTo.appliedFilters.setData([]) - cancel() - }) - - const disableFiltersOrConfirm = new Toggle( - openLayerOrConfirm, - disableFilter, - preset.layerToAddTo.appliedFilters.map(filters => { - if (filters === undefined || filters.length === 0) { - return true; - } - for (const filter of filters) { - if (filter.selected === 0 && filter.filter.options.length === 1) { - return false; - } - if (filter.selected !== undefined) { - const tags = filter.filter.options[filter.selected].osmTags - if (tags !== undefined && tags["and"]?.length !== 0) { - // This actually doesn't filter anything at all - return false; - } - } - } - return true - - }) - ) - - - const tagInfo = SimpleAddUI.CreateTagInfoFor(preset, state.osmConnection); - - const cancelButton = new SubtleButton(Svg.close_ui(), - Translations.t.general.cancel - ).onClick(cancel) - - return new Combine([ - state.osmConnection.userDetails.data.dryRun ? - Translations.t.general.testing.Clone().SetClass("alert") : undefined, - disableFiltersOrConfirm, - cancelButton, - preset.description, - tagInfo - - ]).SetClass("flex flex-col") - - } - - private static CreateTagInfoFor(preset: PresetInfo, osmConnection: OsmConnection, optionallyLinkToWiki = true) { + public static CreateTagInfoFor(preset: PresetInfo, osmConnection: OsmConnection, optionallyLinkToWiki = true) { const csCount = osmConnection.userDetails.data.csCount; return new Toggle( Translations.t.general.add.presetInfo.Subs({ @@ -329,7 +172,7 @@ export default class SimpleAddUI extends Toggle { private static CreatePresetSelectButton(preset: PresetInfo, osmConnection: OsmConnection) { - const tagInfo = SimpleAddUI.CreateTagInfoFor(preset, osmConnection ,false); + const tagInfo = SimpleAddUI.CreateTagInfoFor(preset, osmConnection, false); return new SubtleButton( preset.icon(), new Combine([ @@ -368,7 +211,7 @@ export default class SimpleAddUI extends Toggle { for (const preset of presets) { const tags = TagUtils.KVtoProperties(preset.tags ?? []); - let icon: () => BaseUIElement = () => layer.layerDef.mapRendering[0]. GenerateLeafletStyle(new UIEventSource(tags), false).html + let icon: () => BaseUIElement = () => layer.layerDef.mapRendering[0].GenerateLeafletStyle(new UIEventSource(tags), false).html .SetClass("w-12 h-12 block relative"); const presetInfo: PresetInfo = { tags: preset.tags, diff --git a/UI/DefaultGUI.ts b/UI/DefaultGUI.ts index a4de2d460..8ee02fdd4 100644 --- a/UI/DefaultGUI.ts +++ b/UI/DefaultGUI.ts @@ -32,6 +32,7 @@ export class DefaultGuiState { public readonly copyrightViewIsOpened: UIEventSource; public readonly welcomeMessageOpenedTab: UIEventSource public readonly allFullScreenStates: UIEventSource[] = [] + static state: DefaultGuiState; constructor() { diff --git a/UI/Input/LocationInput.ts b/UI/Input/LocationInput.ts index 3ff7fe5da..65ecd604c 100644 --- a/UI/Input/LocationInput.ts +++ b/UI/Input/LocationInput.ts @@ -96,6 +96,8 @@ export default class LocationInput extends InputElement implements MinimapO let min = undefined; let matchedWay = undefined; for (const feature of self._snapTo.data ?? []) { + try{ + const nearestPointOnLine = GeoOperations.nearestPoint(feature.feature, [loc.lon, loc.lat]) if (min === undefined) { min = nearestPointOnLine @@ -108,6 +110,9 @@ export default class LocationInput extends InputElement implements MinimapO matchedWay = feature.feature; } + }catch(e){ + console.log("Snapping to a nearest point failed for ", feature.feature,"due to ", e) + } } if (min === undefined || min.properties.dist * 1000 > self._maxSnapDistance) { diff --git a/UI/NewPoint/ConfirmLocationOfPoint.ts b/UI/NewPoint/ConfirmLocationOfPoint.ts new file mode 100644 index 000000000..68e380997 --- /dev/null +++ b/UI/NewPoint/ConfirmLocationOfPoint.ts @@ -0,0 +1,184 @@ +import {UIEventSource} from "../../Logic/UIEventSource"; +import {OsmConnection} from "../../Logic/Osm/OsmConnection"; +import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline"; +import BaseUIElement from "../BaseUIElement"; +import LocationInput from "../Input/LocationInput"; +import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"; +import {BBox} from "../../Logic/BBox"; +import {TagUtils} from "../../Logic/Tags/TagUtils"; +import {SubtleButton} from "../Base/SubtleButton"; +import Combine from "../Base/Combine"; +import Translations from "../i18n/Translations"; +import Svg from "../../Svg"; +import Toggle from "../Input/Toggle"; +import SimpleAddUI, {PresetInfo} from "../BigComponents/SimpleAddUI"; + +export default class ConfirmLocationOfPoint extends Combine { + + + constructor( + state: { + osmConnection: OsmConnection, + featurePipeline: FeaturePipeline + }, + filterViewIsOpened: UIEventSource, + preset: PresetInfo, + confirmText: BaseUIElement, + loc: { lon: number, lat: number }, + confirm: (tags: any[], location: { lat: number, lon: number }, snapOntoWayId: string) => void, + cancel: () => void, + ) { + + let preciseInput: LocationInput = undefined + if (preset.preciseInput !== undefined) { + // We uncouple the event source + const zloc = {...loc, zoom: 19} + const locationSrc = new UIEventSource(zloc); + + let backgroundLayer = undefined; + if (preset.preciseInput.preferredBackground) { + backgroundLayer = AvailableBaseLayers.SelectBestLayerAccordingTo(locationSrc, new UIEventSource(preset.preciseInput.preferredBackground)) + } + + let snapToFeatures: UIEventSource<{ feature: any }[]> = undefined + let mapBounds: UIEventSource = undefined + if (preset.preciseInput.snapToLayers && preset.preciseInput.snapToLayers.length > 0) { + snapToFeatures = new UIEventSource<{ feature: any }[]>([]) + mapBounds = new UIEventSource(undefined) + } + + + const tags = TagUtils.KVtoProperties(preset.tags ?? []); + preciseInput = new LocationInput({ + mapBackground: backgroundLayer, + centerLocation: locationSrc, + snapTo: snapToFeatures, + snappedPointTags: tags, + maxSnapDistance: preset.preciseInput.maxSnapDistance, + bounds: mapBounds + }) + preciseInput.installBounds(0.15, true) + preciseInput.SetClass("h-32 rounded-xl overflow-hidden border border-gray").SetStyle("height: 12rem;") + + + if (preset.preciseInput.snapToLayers && preset.preciseInput.snapToLayers.length > 0) { + // We have to snap to certain layers. + // Lets fetch them + + let loadedBbox: BBox = undefined + mapBounds?.addCallbackAndRunD(bbox => { + if (loadedBbox !== undefined && bbox.isContainedIn(loadedBbox)) { + // All is already there + // return; + } + + bbox = bbox.pad(2); + loadedBbox = bbox; + const allFeatures: { feature: any }[] = [] + preset.preciseInput.snapToLayers.forEach(layerId => { + console.log("Snapping to", layerId) + state.featurePipeline.GetFeaturesWithin(layerId, bbox)?.forEach(feats => allFeatures.push(...feats.map(f => ({feature: f})))) + }) + console.log("Snapping to", allFeatures) + snapToFeatures.setData(allFeatures) + }) + } + + } + + + let confirmButton: BaseUIElement = new SubtleButton(preset.icon(), + new Combine([ + confirmText, + Translations.t.general.add.warnVisibleForEveryone.Clone().SetClass("alert") + ]).SetClass("flex flex-col") + ).SetClass("font-bold break-words") + .onClick(() => { + confirm(preset.tags, (preciseInput?.GetValue()?.data ?? loc), preciseInput?.snappedOnto?.data?.properties?.id); + }); + + if (preciseInput !== undefined) { + confirmButton = new Combine([preciseInput, confirmButton]) + } + + const openLayerControl = + new SubtleButton( + Svg.layers_ui(), + new Combine([ + Translations.t.general.add.layerNotEnabled + .Subs({layer: preset.layerToAddTo.layerDef.name}) + .SetClass("alert"), + Translations.t.general.add.openLayerControl + ]) + ) + .onClick(() => filterViewIsOpened.setData(true)) + + + const openLayerOrConfirm = new Toggle( + confirmButton, + openLayerControl, + preset.layerToAddTo.isDisplayed + ) + + const disableFilter = new SubtleButton( + new Combine([ + Svg.filter_ui().SetClass("absolute w-full"), + Svg.cross_bottom_right_svg().SetClass("absolute red-svg") + ]).SetClass("relative"), + new Combine( + [ + Translations.t.general.add.disableFiltersExplanation.Clone(), + Translations.t.general.add.disableFilters.Clone().SetClass("text-xl") + ] + ).SetClass("flex flex-col") + ).onClick(() => { + preset.layerToAddTo.appliedFilters.setData([]) + cancel() + }) + + const disableFiltersOrConfirm = new Toggle( + openLayerOrConfirm, + disableFilter, + preset.layerToAddTo.appliedFilters.map(filters => { + if (filters === undefined || filters.length === 0) { + return true; + } + for (const filter of filters) { + if (filter.selected === 0 && filter.filter.options.length === 1) { + return false; + } + if (filter.selected !== undefined) { + const tags = filter.filter.options[filter.selected].osmTags + if (tags !== undefined && tags["and"]?.length !== 0) { + // This actually doesn't filter anything at all + return false; + } + } + } + return true + + }) + ) + + + const tagInfo = SimpleAddUI.CreateTagInfoFor(preset, state.osmConnection); + + const cancelButton = new SubtleButton(Svg.close_ui(), + Translations.t.general.cancel + ).onClick(cancel) + + super([ + state.osmConnection.userDetails.data.dryRun ? + Translations.t.general.testing.Clone().SetClass("alert") : undefined, + disableFiltersOrConfirm, + cancelButton, + preset.description, + tagInfo + + ]) + + this.SetClass("flex flex-col") + + } + +} \ No newline at end of file diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index 87af2ab7d..fb9e4b6bc 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -20,7 +20,7 @@ import Histogram from "./BigComponents/Histogram"; import Loc from "../Models/Loc"; import {Utils} from "../Utils"; import LayerConfig from "../Models/ThemeConfig/LayerConfig"; -import ImportButton from "./BigComponents/ImportButton"; +import ImportButton, {ImportButtonSpecialViz} from "./BigComponents/ImportButton"; import {Tag} from "../Logic/Tags/Tag"; import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSource"; import ShowDataMultiLayer from "./ShowDataLayer/ShowDataMultiLayer"; @@ -38,10 +38,13 @@ import {SubtleButton} from "./Base/SubtleButton"; import ChangeTagAction from "../Logic/Osm/Actions/ChangeTagAction"; import {And} from "../Logic/Tags/And"; import Toggle from "./Input/Toggle"; +import {DefaultGuiState} from "./DefaultGUI"; +import Img from "./Base/Img"; +import FilteredLayer from "../Models/FilteredLayer"; export interface SpecialVisualization { funcName: string, - constr: ((state: State, tagSource: UIEventSource, argument: string[]) => BaseUIElement), + constr: ((state: State, tagSource: UIEventSource, argument: string[], guistate: DefaultGuiState) => BaseUIElement), docs: string, example?: string, args: { name: string, defaultValue?: string, doc: string }[] @@ -49,17 +52,7 @@ export interface SpecialVisualization { export default class SpecialVisualizations { - private static tagsToApplyHelpText = `These can either be a tag to add, such as \`amenity=fast_food\` or can use a substitution, e.g. \`addr:housenumber=$number\`. -This new point will then have the tags \`amenity=fast_food\` and \`addr:housenumber\` with the value that was saved in \`number\` in the original feature. - -If a value to substitute is undefined, empty string will be used instead. - -This supports multiple values, e.g. \`ref=$source:geometry:type/$source:geometry:ref\` - -Remark that the syntax is slightly different then expected; it uses '$' to note a value to copy, followed by a name (matched with \`[a-zA-Z0-9_:]*\`). Sadly, delimiting with \`{}\` as these already mark the boundaries of the special rendering... - -Note that these values can be prepare with javascript in the theme by using a [calculatedTag](calculatedTags.md#calculating-tags-with-javascript) - ` + static tagsToApplyHelpText = Utils.Special_visualizations_tagsToApplyHelpText public static specialVisualizations: SpecialVisualization[] = [ { @@ -490,79 +483,7 @@ Note that these values can be prepare with javascript in the theme by using a [c ) } }, - { - funcName: "import_button", - args: [ - { - name: "tags", - doc: "The tags to add onto the new object - see specification above" - }, - { - name: "text", - doc: "The text to show on the button", - defaultValue: "Import this data into OpenStreetMap" - }, - { - name: "icon", - doc: "A nice icon to show in the button", - defaultValue: "./assets/svg/addSmall.svg" - }, - { - name: "minzoom", - doc: "How far the contributor must zoom in before being able to import the point", - defaultValue: "18" - }], - docs: `This button will copy the data from an external dataset into OpenStreetMap. It is only functional in official themes but can be tested in unofficial themes. - -#### Importing a dataset into OpenStreetMap: requirements - -If you want to import a dataset, make sure that: - -1. The dataset to import has a suitable license -2. The community has been informed of the import -3. All other requirements of the [import guidelines](https://wiki.openstreetmap.org/wiki/Import/Guidelines) have been followed - -There are also some technicalities in your theme to keep in mind: - -1. The new feature will be added and will flow through the program as any other new point as if it came from OSM. - This means that there should be a layer which will match the new tags and which will display it. -2. The original feature from your geojson layer will gain the tag '_imported=yes'. - This should be used to change the appearance or even to hide it (eg by changing the icon size to zero) -3. There should be a way for the theme to detect previously imported points, even after reloading. - A reference number to the original dataset is an excellent way to do this -4. When importing ways, the theme creator is also responsible of avoiding overlapping ways. - -#### Disabled in unofficial themes - -The import button can be tested in an unofficial theme by adding \`test=true\` or \`backend=osm-test\` as [URL-paramter](URL_Parameters.md). -The import button will show up then. If in testmode, you can read the changeset-XML directly in the web console. -In the case that MapComplete is pointed to the testing grounds, the edit will be made on ${OsmConnection.oauth_configs["osm-test"].url} - - -#### Specifying which tags to copy or add - -The first argument of the import button takes a \`;\`-seperated list of tags to add. - -${SpecialVisualizations.tagsToApplyHelpText} - -`, - constr: (state, tagSource, args) => { - if (!state.layoutToUse.official && !(state.featureSwitchIsTesting.data || state.osmConnection._oauth_config.url === OsmConnection.oauth_configs["osm-test"].url)) { - return new Combine([new FixedUiElement("The import button is disabled for unofficial themes to prevent accidents.").SetClass("alert"), - new FixedUiElement("To test, add test=true or backend=osm-test to the URL. The changeset will be printed in the console. Please open a PR to officialize this theme to actually enable the import button.")]) - } - const rewrittenTags = SpecialVisualizations.generateTagsToApply(args[0], tagSource) - const id = tagSource.data.id; - const feature = state.allElements.ContainingFeatures.get(id) - const minzoom = Number(args[3]) - const message = args[1] - const image = args[2] - - return new ImportButton( - image, message, tagSource, rewrittenTags, feature, minzoom, state - ) - } - }, + new ImportButtonSpecialViz(), { funcName: "multi_apply", docs: "A button to apply the tagging of this object onto a list of other features. This is an advanced feature for which you'll need calculatedTags", @@ -687,7 +608,7 @@ ${SpecialVisualizations.tagsToApplyHelpText} } ] - private static generateTagsToApply(spec: string, tagSource: UIEventSource): UIEventSource { + static generateTagsToApply(spec: string, tagSource: UIEventSource): UIEventSource { const tgsSpec = spec.split(";").map(spec => { const kv = spec.split("=").map(s => s.trim()); diff --git a/UI/SubstitutedTranslation.ts b/UI/SubstitutedTranslation.ts index ff2d3a447..cf6e4af76 100644 --- a/UI/SubstitutedTranslation.ts +++ b/UI/SubstitutedTranslation.ts @@ -8,6 +8,7 @@ import {Utils} from "../Utils"; import {VariableUiElement} from "./Base/VariableUIElement"; import Combine from "./Base/Combine"; import BaseUIElement from "./BaseUIElement"; +import {DefaultGuiState} from "./DefaultGUI"; export class SubstitutedTranslation extends VariableUiElement { @@ -49,7 +50,7 @@ export class SubstitutedTranslation extends VariableUiElement { } const viz = proto.special; try { - return viz.func.constr(State.state, tagsSource, proto.special.args).SetStyle(proto.special.style); + return viz.func.constr(State.state, tagsSource, proto.special.args, DefaultGuiState.state).SetStyle(proto.special.style); } catch (e) { console.error("SPECIALRENDERING FAILED for", tagsSource.data?.id, e) return new FixedUiElement(`Could not generate special rendering for ${viz.func}(${viz.args.join(", ")}) ${e}`).SetStyle("alert") diff --git a/Utils.ts b/Utils.ts index 40fb518ee..e0dbf2641 100644 --- a/Utils.ts +++ b/Utils.ts @@ -15,6 +15,18 @@ export class Utils { private static injectedDownloads = {} private static _download_cache = new Map, timestamp: number }>() + public static Special_visualizations_tagsToApplyHelpText = `These can either be a tag to add, such as \`amenity=fast_food\` or can use a substitution, e.g. \`addr:housenumber=$number\`. +This new point will then have the tags \`amenity=fast_food\` and \`addr:housenumber\` with the value that was saved in \`number\` in the original feature. + +If a value to substitute is undefined, empty string will be used instead. + +This supports multiple values, e.g. \`ref=$source:geometry:type/$source:geometry:ref\` + +Remark that the syntax is slightly different then expected; it uses '$' to note a value to copy, followed by a name (matched with \`[a-zA-Z0-9_:]*\`). Sadly, delimiting with \`{}\` as these already mark the boundaries of the special rendering... + +Note that these values can be prepare with javascript in the theme by using a [calculatedTag](calculatedTags.md#calculating-tags-with-javascript) + ` + static EncodeXmlValue(str) { if (typeof str !== "string") { str = "" + str diff --git a/assets/layers/type_node/type_node.json b/assets/layers/type_node/type_node.json new file mode 100644 index 000000000..5ead02f1c --- /dev/null +++ b/assets/layers/type_node/type_node.json @@ -0,0 +1,12 @@ +{ + "id": "type_node", + "description": "This is a special meta_layer which exports _every_ point in OSM. This only works if zoomed below the point that the full tile is loaded (and not loaded via Overpass). Note that this point will also contain a property `parent_ways` which contains all the ways this node is part of as a list", + "minzoom": 18, + "source": { + "osmTags": "id~node/.*" + }, + "mapRendering": [], + "name": "All OSM Nodes", + "title": "OSM node {id}", + "tagRendering": [ ] +} \ No newline at end of file diff --git a/assets/themes/grb_import/grb.json b/assets/themes/grb_import/grb.json index 6f3cbd5db..a7ce909e6 100644 --- a/assets/themes/grb_import/grb.json +++ b/assets/themes/grb_import/grb.json @@ -28,7 +28,20 @@ "overrideAll": { "minzoom": 18 }, + "trackAllNodes": true, "layers": [ + { + "builtin": "type_node", + "isShown": { + "render": "no" + }, + "override": { + "calculatedTags": [ + "_is_part_of_building=feat.get('parent_ways')?.some(p => p.building !== undefined && p.building !== '') ?? false", + "_is_part_of_landuse=feat.get('parent_ways')?.some(p => (p.landuse !== undefined && p.landuse !== '') || (p.natural !== undefined && p.natural !== '')) ?? false" + ] + } + }, { "id": "OSM-buildings", "name": "All OSM-buildings", @@ -413,7 +426,7 @@ "all_tags", { "id": "import-button", - "render": "{import_button(addr:street=$STRAATNM; addr:housenumber=$HUISNR)}" + "render": "{import_button(OSM-buildings, addr:street=$STRAATNM; addr:housenumber=$HUISNR,Import this address,,,OSM-buildings,5)}" } ] }, @@ -657,7 +670,7 @@ }, { "id": "Import-button", - "render": "{import_button(building=$building; source:geometry:date=$_grb_date; source:geometry:ref=$_grb_ref, Upload this building to OpenStreetMap)}", + "render": "{import_button(OSM-buildings,building=$building; source:geometry:date=$_grb_date; source:geometry:ref=$_grb_ref, Upload this building to OpenStreetMap)}", "mappings": [ { "if": "_overlaps_with!=null", diff --git a/assets/themes/uk_addresses/uk_addresses.json b/assets/themes/uk_addresses/uk_addresses.json index e48f8cf7a..a9f9d0a48 100644 --- a/assets/themes/uk_addresses/uk_addresses.json +++ b/assets/themes/uk_addresses/uk_addresses.json @@ -93,7 +93,7 @@ }, { "id": "uk_addresses_import_button", - "render": "{import_button(ref:inspireid=$inspireid, Add this address, ./assets/themes/uk_addresses/housenumber_add.svg)}" + "render": "{import_button(addresses, ref:inspireid=$inspireid, Add this address, ./assets/themes/uk_addresses/housenumber_add.svg)}" } ], "calculatedTags": [ diff --git a/index.ts b/index.ts index 3ff564ca9..2ed85051e 100644 --- a/index.ts +++ b/index.ts @@ -64,9 +64,11 @@ class Init { const guiState = new DefaultGuiState() State.state = new State(layoutToUse); + DefaultGuiState.state = guiState; // This 'leaks' the global state via the window object, useful for debugging // @ts-ignore window.mapcomplete_state = State.state; + new DefaultGUI(State.state, guiState) if (encoded !== undefined && encoded.length > 10) { diff --git a/scripts/generateLayerOverview.ts b/scripts/generateLayerOverview.ts index e4c981dec..da1a984b4 100644 --- a/scripts/generateLayerOverview.ts +++ b/scripts/generateLayerOverview.ts @@ -12,8 +12,8 @@ import {Utils} from "../Utils"; // It spits out an overview of those to be used to load them interface LayersAndThemes { - themes: any[], - layers: { parsed: any, path: string }[] + themes: LayoutConfigJson[], + layers: { parsed: LayerConfigJson, path: string }[] } @@ -35,7 +35,6 @@ class LayerOverviewUtils { } } - writeFiles(lt: LayersAndThemes) { writeFileSync("./assets/generated/known_layers_and_themes.json", JSON.stringify({ "layers": lt.layers.map(l => l.parsed), @@ -43,7 +42,6 @@ class LayerOverviewUtils { })) } - validateLayer(layerJson: LayerConfigJson, path: string, knownPaths: Set, context?: string): string[] { let errorCount = []; if (layerJson["overpassTags"] !== undefined) { @@ -109,6 +107,8 @@ class LayerOverviewUtils { } let themeErrorCount = [] + // used only for the reports + let themeConfigs: LayoutConfig[] = [] for (const themeInfo of themeFiles) { const themeFile = themeInfo.parsed const themePath = themeInfo.path @@ -119,7 +119,7 @@ class LayerOverviewUtils { themeErrorCount.push("The theme " + themeFile.id + " has units defined - these should be defined on the layer instead. (Hint: use overrideAll: { '+units': ... }) ") } if (themeFile["roamingRenderings"] !== undefined) { - themeErrorCount.push("Theme " + themeFile.id + " contains an old 'roamingRenderings'. Use an 'overrideAll' instead") + themeErrorCount.push("Theme " + themeFile.id + " contains an old 'roamingRenderings'. Use an 'overrideAll' instead") } for (const layer of themeFile.layers) { if (typeof layer === "string") { @@ -144,17 +144,17 @@ class LayerOverviewUtils { } } } - + const referencedLayers = Utils.NoNull([].concat(...themeFile.layers.map(layer => { - if(typeof layer === "string"){ + if (typeof layer === "string") { return layer } - if(layer["builtin"] !== undefined){ + if (layer["builtin"] !== undefined) { return layer["builtin"] } return undefined }).map(layerName => { - if(typeof layerName === "string"){ + if (typeof layerName === "string") { return [layerName] } return layerName @@ -176,9 +176,9 @@ class LayerOverviewUtils { } const neededLanguages = themeFile["mustHaveLanguage"] if (neededLanguages !== undefined) { - console.log("Checking language requerements for ", theme.id, "as it must have", neededLanguages.join(", ")) - const allTranslations = [].concat(Translation.ExtractAllTranslationsFrom(theme, theme.id), - ...referencedLayers.map(layerId => Translation.ExtractAllTranslationsFrom(knownLayerIds.get(layerId), theme.id+"->"+layerId))) + console.log("Checking language requirements for ", theme.id, "as it must have", neededLanguages.join(", ")) + const allTranslations = [].concat(Translation.ExtractAllTranslationsFrom(theme, theme.id), + ...referencedLayers.map(layerId => Translation.ExtractAllTranslationsFrom(knownLayerIds.get(layerId), theme.id + "->" + layerId))) for (const neededLanguage of neededLanguages) { allTranslations .filter(t => t.tr.translations[neededLanguage] === undefined && t.tr.translations["*"] === undefined) @@ -189,7 +189,7 @@ class LayerOverviewUtils { } - + themeConfigs.push(theme) } catch (e) { themeErrorCount.push("Could not parse theme " + themeFile["id"] + "due to", e) } @@ -210,12 +210,11 @@ class LayerOverviewUtils { console.log(msg) console.log("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") - if (process.argv.indexOf("--report") >= 0) { + if (args.indexOf("--report") >= 0) { console.log("Writing report!") writeFileSync("layer_report.txt", errors) } - - if (process.argv.indexOf("--no-fail") < 0) { + if (args.indexOf("--no-fail") < 0) { throw msg; } } diff --git a/test.ts b/test.ts index a8cc94f92..653048710 100644 --- a/test.ts +++ b/test.ts @@ -1,13 +1,139 @@ -import * as wd from "wikidata-sdk" -import * as wds from "wikibase-sdk" import {Utils} from "./Utils"; +import FullNodeDatabaseSource from "./Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"; -const url = wd.getEntities(["Q42"]) -console.log(url) -Utils.downloadJson(url).then(async (entities) => { - //const parsed = wd.parse.wb.entities(entities)["Q42"] - console.log(entities) - console.log(wds.simplify.entity(entities.entities["Q42"], { - timeConverter: 'simple-day' - })) + +const data = "\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " " + +const url = "https://www.openstreetmap.org/api/0.6/map?bbox=3.217620849609375,51.21548639922819,3.218994140625,51.21634661126673" +Utils.downloadJson(url).then(data =>{ + const osmSource = { + rawDataHandlers : [] + } + new FullNodeDatabaseSource(osmSource) + osmSource.rawDataHandlers[0]( data, 0) }) \ No newline at end of file From 12bc91aea3d986340a2e5fec733da5d72e7dee58 Mon Sep 17 00:00:00 2001 From: Nicole <15011784+nicolelaine@users.noreply.github.com> Date: Sun, 31 Oct 2021 21:18:28 +0100 Subject: [PATCH 59/95] Update postboxes.json --- assets/themes/postboxes/postboxes.json | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/assets/themes/postboxes/postboxes.json b/assets/themes/postboxes/postboxes.json index 520f05d20..c00500859 100644 --- a/assets/themes/postboxes/postboxes.json +++ b/assets/themes/postboxes/postboxes.json @@ -47,10 +47,6 @@ }, "tagRenderings": [ "images", - { - "id": "minimap", - "render": "{minimap(18): height: 5rem; overflow: hidden; border-radius:3rem; }" - } ], "icon": { "render": "./assets/themes/postboxes/postbox.svg" @@ -126,10 +122,6 @@ }, "tagRenderings": [ "images", - { - "id": "minimap", - "render": "{minimap(18): height: 5rem; overflow: hidden; border-radius:3rem; }" - }, { "render": { "en": "Opening Hours: {opening_hours_table()}" @@ -225,4 +217,4 @@ ] } ] -} \ No newline at end of file +} From c4f217b74ebd2517943160483d71b2d8b86bf471 Mon Sep 17 00:00:00 2001 From: Nicole <15011784+nicolelaine@users.noreply.github.com> Date: Sun, 31 Oct 2021 21:20:03 +0100 Subject: [PATCH 60/95] Update postboxes.json --- assets/themes/postboxes/postboxes.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/themes/postboxes/postboxes.json b/assets/themes/postboxes/postboxes.json index c00500859..653a99ed1 100644 --- a/assets/themes/postboxes/postboxes.json +++ b/assets/themes/postboxes/postboxes.json @@ -46,7 +46,7 @@ "en": "The layer showing postboxes." }, "tagRenderings": [ - "images", + "images" ], "icon": { "render": "./assets/themes/postboxes/postbox.svg" @@ -121,7 +121,7 @@ "de": "Eine Ebene mit Postämtern." }, "tagRenderings": [ - "images", + "images" { "render": { "en": "Opening Hours: {opening_hours_table()}" From d6b9e1fd6c456617588b0a08145254cee0e3eb40 Mon Sep 17 00:00:00 2001 From: Nicole <15011784+nicolelaine@users.noreply.github.com> Date: Sun, 31 Oct 2021 21:22:44 +0100 Subject: [PATCH 61/95] Update postboxes.json --- assets/themes/postboxes/postboxes.json | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/assets/themes/postboxes/postboxes.json b/assets/themes/postboxes/postboxes.json index 653a99ed1..1d0ee144d 100644 --- a/assets/themes/postboxes/postboxes.json +++ b/assets/themes/postboxes/postboxes.json @@ -46,7 +46,11 @@ "en": "The layer showing postboxes." }, "tagRenderings": [ - "images" + "images", + { + "id": "minimap", + "render": "{minimap(18): height: 5rem; overflow: hidden; border-radius:3rem; }" + } ], "icon": { "render": "./assets/themes/postboxes/postbox.svg" @@ -121,7 +125,11 @@ "de": "Eine Ebene mit Postämtern." }, "tagRenderings": [ - "images" + "images", + { + "id": "minimap", + "render": "{minimap(18): height: 5rem; overflow: hidden; border-radius:3rem; }" + } { "render": { "en": "Opening Hours: {opening_hours_table()}" From 3c1a64973379efa7185d779ae832b7966a592017 Mon Sep 17 00:00:00 2001 From: Nicole <15011784+nicolelaine@users.noreply.github.com> Date: Sun, 31 Oct 2021 21:23:41 +0100 Subject: [PATCH 62/95] Update postboxes.json --- assets/themes/postboxes/postboxes.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/themes/postboxes/postboxes.json b/assets/themes/postboxes/postboxes.json index 1d0ee144d..1d68e167b 100644 --- a/assets/themes/postboxes/postboxes.json +++ b/assets/themes/postboxes/postboxes.json @@ -126,10 +126,10 @@ }, "tagRenderings": [ "images", - { + { "id": "minimap", "render": "{minimap(18): height: 5rem; overflow: hidden; border-radius:3rem; }" - } + }, { "render": { "en": "Opening Hours: {opening_hours_table()}" From 83d96bd485a5a8b99dd7daf186c58bab8de809d2 Mon Sep 17 00:00:00 2001 From: Nicole <15011784+nicolelaine@users.noreply.github.com> Date: Sun, 31 Oct 2021 21:26:28 +0100 Subject: [PATCH 63/95] Update postboxes.json --- assets/themes/postboxes/postboxes.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/themes/postboxes/postboxes.json b/assets/themes/postboxes/postboxes.json index 1d68e167b..f9834de3d 100644 --- a/assets/themes/postboxes/postboxes.json +++ b/assets/themes/postboxes/postboxes.json @@ -49,7 +49,7 @@ "images", { "id": "minimap", - "render": "{minimap(18): height: 5rem; overflow: hidden; border-radius:3rem; }" + "render": "{minimap(18): height: 10rem; overflow: hidden; border-radius:1rem; }" } ], "icon": { @@ -128,7 +128,7 @@ "images", { "id": "minimap", - "render": "{minimap(18): height: 5rem; overflow: hidden; border-radius:3rem; }" + "render": "{minimap(18): height: 10rem; overflow: hidden; border-radius:1rem; }" }, { "render": { From 7d4c54c1b114c743aeaecd2fd634d2ecbfe7f87c Mon Sep 17 00:00:00 2001 From: Nicole <15011784+nicolelaine@users.noreply.github.com> Date: Sun, 31 Oct 2021 21:31:06 +0100 Subject: [PATCH 64/95] Update postboxes.json --- assets/themes/postboxes/postboxes.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/themes/postboxes/postboxes.json b/assets/themes/postboxes/postboxes.json index f9834de3d..57633ba58 100644 --- a/assets/themes/postboxes/postboxes.json +++ b/assets/themes/postboxes/postboxes.json @@ -48,8 +48,8 @@ "tagRenderings": [ "images", { - "id": "minimap", - "render": "{minimap(18): height: 10rem; overflow: hidden; border-radius:1rem; }" + + "render": "{minimap(17): height: 10rem; overflow: hidden; border: 2px solid black; }" } ], "icon": { From 2fef7617a20dee51bb75ec3d88c68fb8321d3266 Mon Sep 17 00:00:00 2001 From: Nicole <15011784+nicolelaine@users.noreply.github.com> Date: Sun, 31 Oct 2021 21:32:40 +0100 Subject: [PATCH 65/95] Update postboxes.json --- assets/themes/postboxes/postboxes.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/assets/themes/postboxes/postboxes.json b/assets/themes/postboxes/postboxes.json index 57633ba58..301f46a04 100644 --- a/assets/themes/postboxes/postboxes.json +++ b/assets/themes/postboxes/postboxes.json @@ -128,7 +128,9 @@ "images", { "id": "minimap", - "render": "{minimap(18): height: 10rem; overflow: hidden; border-radius:1rem; }" + + + "render": "{minimap(17): height: 10rem; overflow: hidden; border: 2px solid black; }" }, { "render": { From 083a0b66171f55b53911ae00577a003b44613d1c Mon Sep 17 00:00:00 2001 From: Nicole <15011784+nicolelaine@users.noreply.github.com> Date: Sun, 31 Oct 2021 21:41:28 +0100 Subject: [PATCH 66/95] Update postboxes.json --- assets/themes/postboxes/postboxes.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/themes/postboxes/postboxes.json b/assets/themes/postboxes/postboxes.json index 301f46a04..266e82fe8 100644 --- a/assets/themes/postboxes/postboxes.json +++ b/assets/themes/postboxes/postboxes.json @@ -49,7 +49,7 @@ "images", { - "render": "{minimap(17): height: 10rem; overflow: hidden; border: 2px solid black; }" + "render": "{minimap(17): height: 10rem; overflow: hidden; }" } ], "icon": { From 3b1f91c36844d63212e47b985c9c83d07ae2b561 Mon Sep 17 00:00:00 2001 From: Nicole <15011784+nicolelaine@users.noreply.github.com> Date: Sun, 31 Oct 2021 21:43:15 +0100 Subject: [PATCH 67/95] Update postboxes.json --- assets/themes/postboxes/postboxes.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/themes/postboxes/postboxes.json b/assets/themes/postboxes/postboxes.json index 266e82fe8..6f4b07b2e 100644 --- a/assets/themes/postboxes/postboxes.json +++ b/assets/themes/postboxes/postboxes.json @@ -49,7 +49,7 @@ "images", { - "render": "{minimap(17): height: 10rem; overflow: hidden; }" + "render": "{minimap(17): height: 10rem; overflow: hidden; border-radius:3rem }" } ], "icon": { From 9cd379e2517dbf9f171a5d0af7f1945b4b762d9a Mon Sep 17 00:00:00 2001 From: Nicole <15011784+nicolelaine@users.noreply.github.com> Date: Sun, 31 Oct 2021 21:46:03 +0100 Subject: [PATCH 68/95] Update postboxes.json --- assets/themes/postboxes/postboxes.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/themes/postboxes/postboxes.json b/assets/themes/postboxes/postboxes.json index 6f4b07b2e..312f5b9de 100644 --- a/assets/themes/postboxes/postboxes.json +++ b/assets/themes/postboxes/postboxes.json @@ -49,7 +49,7 @@ "images", { - "render": "{minimap(17): height: 10rem; overflow: hidden; border-radius:3rem }" + "render": "{minimap(17): height: 10rem; overflow: hidden; border-radius: 0.5rem; }" } ], "icon": { @@ -130,7 +130,7 @@ "id": "minimap", - "render": "{minimap(17): height: 10rem; overflow: hidden; border: 2px solid black; }" + "render": "{minimap(17): height: 10rem; overflow: hidden; border-radius: 0.5rem; }" }, { "render": { From 760aa6384016e42041d404cc2450c92c582a589d Mon Sep 17 00:00:00 2001 From: Nicole <15011784+nicolelaine@users.noreply.github.com> Date: Sun, 31 Oct 2021 22:06:26 +0100 Subject: [PATCH 69/95] Update postboxes.json --- assets/themes/postboxes/postboxes.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/themes/postboxes/postboxes.json b/assets/themes/postboxes/postboxes.json index 312f5b9de..f734760cd 100644 --- a/assets/themes/postboxes/postboxes.json +++ b/assets/themes/postboxes/postboxes.json @@ -49,7 +49,7 @@ "images", { - "render": "{minimap(17): height: 10rem; overflow: hidden; border-radius: 0.5rem; }" + "render": "{minimap(17): height: 10rem; overflow: hidden; border: 1px solid DADADA; border-radius: 0.5rem; }" } ], "icon": { From f99f3e2672149ed360d374b4e8892bd398a8ae7a Mon Sep 17 00:00:00 2001 From: Nicole <15011784+nicolelaine@users.noreply.github.com> Date: Sun, 31 Oct 2021 22:16:24 +0100 Subject: [PATCH 70/95] Update postboxes.json --- assets/themes/postboxes/postboxes.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/themes/postboxes/postboxes.json b/assets/themes/postboxes/postboxes.json index f734760cd..a23eeab77 100644 --- a/assets/themes/postboxes/postboxes.json +++ b/assets/themes/postboxes/postboxes.json @@ -49,7 +49,7 @@ "images", { - "render": "{minimap(17): height: 10rem; overflow: hidden; border: 1px solid DADADA; border-radius: 0.5rem; }" + "render": "{minimap(17): height: 10rem; overflow: hidden; border: 1px solid black; border-radius: 0.5rem; }" } ], "icon": { From 6c4e432fbfb7db43202d64637fa27498f066623d Mon Sep 17 00:00:00 2001 From: Nicole <15011784+nicolelaine@users.noreply.github.com> Date: Sun, 31 Oct 2021 22:20:36 +0100 Subject: [PATCH 71/95] Update postboxes.json --- assets/themes/postboxes/postboxes.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/themes/postboxes/postboxes.json b/assets/themes/postboxes/postboxes.json index a23eeab77..7741ac2d6 100644 --- a/assets/themes/postboxes/postboxes.json +++ b/assets/themes/postboxes/postboxes.json @@ -49,7 +49,7 @@ "images", { - "render": "{minimap(17): height: 10rem; overflow: hidden; border: 1px solid black; border-radius: 0.5rem; }" + "render": "{minimap(17): height: 10rem; overflow: hidden; border: 1px solid #DADADA; border-radius: 0.5rem; }" } ], "icon": { @@ -130,7 +130,7 @@ "id": "minimap", - "render": "{minimap(17): height: 10rem; overflow: hidden; border-radius: 0.5rem; }" + "render": "{minimap(17): height: 10rem; overflow: hidden; border: 1px solid #DADADA; border-radius: 0.5rem; }" }, { "render": { From 1db54f3c8ede6abb7c8551cce45d9cf07cec5778 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 2 Nov 2021 17:47:21 +0100 Subject: [PATCH 72/95] Workaround for upstream issue: always use 'opening_hours' as using ome keys crashes the OH-parser --- UI/OpeningHours/OpeningHoursVisualization.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/UI/OpeningHours/OpeningHoursVisualization.ts b/UI/OpeningHours/OpeningHoursVisualization.ts index fb8ae05d6..9a2a30800 100644 --- a/UI/OpeningHours/OpeningHoursVisualization.ts +++ b/UI/OpeningHours/OpeningHoursVisualization.ts @@ -27,13 +27,19 @@ export default class OpeningHoursVisualization extends Toggle { const tagsDirect = tags.data; const ohTable = new VariableUiElement(tags .map(tags => { - const value : string = tags[key]; - if(value.startsWith(prefix) && value.endsWith(postfix)){ - return value.substring(prefix.length, value.length - postfix.length) + const value: string = tags[key]; + if (value === undefined) { + return undefined + } + if (value.startsWith(prefix) && value.endsWith(postfix)) { + return value.substring(prefix.length, value.length - postfix.length).trim() } return value; }) // This mapping will absorb all other changes to tags in order to prevent regeneration .map(ohtext => { + if (ohtext === undefined) { + return new FixedUiElement("No opening hours defined with key " + key).SetClass("alert") + } try { // noinspection JSPotentiallyInvalidConstructorUsage const oh = new opening_hours(ohtext, { @@ -41,12 +47,12 @@ export default class OpeningHoursVisualization extends Toggle { lon: tagsDirect._lon, address: { country_code: tagsDirect._country - } - }, {tag_key: key}); + }, + }, {tag_key: "opening_hours"}); return OpeningHoursVisualization.CreateFullVisualisation(oh) } catch (e) { - console.log(e); + console.warn(e, e.stack); return new Combine([Translations.t.general.opening_hours.error_loading, new Toggle( new FixedUiElement(e).SetClass("subtle"), From 2484848cd6c364b328fc8a7eda5146cfd6e55024 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 3 Nov 2021 00:44:53 +0100 Subject: [PATCH 73/95] Refactoring of GPS-location (uses featureSource too now), factoring out state, add ReplaceGeometryAction and conflation example --- Logic/Actors/GeoLocationHandler.ts | 86 +++--- Logic/BBox.ts | 5 + Logic/FeatureSource/FeaturePipeline.ts | 6 +- .../NewGeometryFromChangesFeatureSource.ts | 2 +- .../RenderingMultiPlexerFeatureSource.ts | 4 +- .../FullNodeDatabaseSource.ts | 108 ++++++- .../TiledFeatureSource/OsmFeatureSource.ts | 6 +- Logic/Osm/Actions/ChangeTagAction.ts | 11 +- Logic/Osm/Actions/CreateNewWayAction.ts | 20 +- Logic/Osm/Actions/ReplaceGeometryAction.ts | 232 +++++++++++++++ Logic/Osm/Changes.ts | 12 +- Logic/State/MapState.ts | 9 +- UI/Base/AsyncLazy.ts | 28 ++ UI/Base/Minimap.ts | 1 + UI/Base/MinimapImplementation.ts | 7 + UI/BigComponents/FullWelcomePaneWithTabs.ts | 14 +- UI/BigComponents/ImportButton.ts | 264 +++++++++++++----- UI/BigComponents/LeftControls.ts | 6 +- UI/BigComponents/RightControls.ts | 23 +- UI/BigComponents/ShareScreen.ts | 7 +- UI/DefaultGUI.ts | 75 +---- UI/DefaultGuiState.ts | 74 +++++ UI/ExportPDF.ts | 25 +- UI/Input/LocationInput.ts | 3 + UI/Popup/FeatureInfoBox.ts | 1 - UI/ShowDataLayer/ShowDataLayer.ts | 6 +- UI/SpecialVisualizations.ts | 4 +- UI/SubstitutedTranslation.ts | 2 +- assets/layers/conflation/conflation.json | 32 +++ assets/layers/gps_location/gps_location.json | 15 + .../layers/home_location/home_location.json | 46 +-- .../themes/cycle_highways/cycle_highways.json | 5 - assets/themes/grb_import/grb.json | 6 +- index.ts | 3 +- test.ts | 165 ++--------- test/ReplaceGeometry.spec.ts | 185 ++++++++++++ test/TestAll.ts | 4 +- 37 files changed, 1035 insertions(+), 467 deletions(-) create mode 100644 Logic/Osm/Actions/ReplaceGeometryAction.ts create mode 100644 UI/Base/AsyncLazy.ts create mode 100644 UI/DefaultGuiState.ts create mode 100644 assets/layers/conflation/conflation.json create mode 100644 assets/layers/gps_location/gps_location.json create mode 100644 test/ReplaceGeometry.spec.ts diff --git a/Logic/Actors/GeoLocationHandler.ts b/Logic/Actors/GeoLocationHandler.ts index 86e656b50..5927b73cb 100644 --- a/Logic/Actors/GeoLocationHandler.ts +++ b/Logic/Actors/GeoLocationHandler.ts @@ -1,13 +1,16 @@ -import * as L from "leaflet"; import {UIEventSource} from "../UIEventSource"; import Svg from "../../Svg"; -import Img from "../../UI/Base/Img"; import {LocalStorageSource} from "../Web/LocalStorageSource"; import {VariableUiElement} from "../../UI/Base/VariableUIElement"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; import {QueryParameters} from "../Web/QueryParameters"; +import FeatureSource from "../FeatureSource/FeatureSource"; +import StaticFeatureSource from "../FeatureSource/Sources/StaticFeatureSource"; export default class GeoLocationHandler extends VariableUiElement { + + public readonly currentLocation : FeatureSource + /** * Wether or not the geolocation is active, aka the user requested the current location * @private @@ -25,20 +28,12 @@ export default class GeoLocationHandler extends VariableUiElement { * @private */ private readonly _permission: UIEventSource; - /*** - * The marker on the map, in order to update it - * @private - */ - private _marker: L.Marker; /** * Literally: _currentGPSLocation.data != undefined * @private */ private readonly _hasLocation: UIEventSource; - private readonly _currentGPSLocation: UIEventSource<{ - latlng: any; - accuracy: number; - }>; + private readonly _currentGPSLocation: UIEventSource; /** * Kept in order to update the marker * @private @@ -63,8 +58,8 @@ export default class GeoLocationHandler extends VariableUiElement { private readonly _layoutToUse: LayoutConfig; constructor( - currentGPSLocation: UIEventSource<{ latlng: any; accuracy: number }>, - leafletMap: UIEventSource, + currentGPSLocation: UIEventSource, + leafletMap: UIEventSource, layoutToUse: LayoutConfig ) { const hasLocation = currentGPSLocation.map( @@ -182,10 +177,25 @@ export default class GeoLocationHandler extends VariableUiElement { } }) - + this.currentLocation = new StaticFeatureSource([], false) this._currentGPSLocation.addCallback((location) => { self._previousLocationGrant.setData("granted"); + const feature = { + "type": "Feature", + properties: { + "user:location":"yes", + "accuracy":location.accuracy, + "speed":location.speed, + }, + geometry:{ + type:"Point", + coordinates: [location.longitude, location.latitude], + } + } + + self.currentLocation.features.setData([{feature, freshness: new Date()}]) + const timeSinceRequest = (new Date().getTime() - (self._lastUserRequest?.getTime() ?? 0)) / 1000; if (timeSinceRequest < 30) { @@ -194,33 +204,8 @@ export default class GeoLocationHandler extends VariableUiElement { self.MoveToCurrentLoction(); } - let color = "#1111cc"; - try { - color = getComputedStyle(document.body).getPropertyValue( - "--catch-detail-color" - ); - } catch (e) { - console.error(e); - } - const icon = L.icon({ - iconUrl: Img.AsData(Svg.location.replace(/#000000/g, color).replace(/#000/g, color)), - iconSize: [40, 40], // size of the icon - iconAnchor: [20, 20], // point of the icon which will correspond to marker's location - }); - - const map = self._leafletMap.data; - if(map === undefined){ - return; - } - - const newMarker = L.marker(location.latlng, {icon: icon}); - newMarker.addTo(map); - - if (self._marker !== undefined) { - map.removeLayer(self._marker); - } - self._marker = newMarker; }); + } private init(askPermission: boolean, forceZoom: boolean) { @@ -261,8 +246,8 @@ export default class GeoLocationHandler extends VariableUiElement { this._lastUserRequest = undefined; if ( - this._currentGPSLocation.data.latlng[0] === 0 && - this._currentGPSLocation.data.latlng[1] === 0 + this._currentGPSLocation.data.latitude === 0 && + this._currentGPSLocation.data.longitude === 0 ) { console.debug("Not moving to GPS-location: it is null island"); return; @@ -275,20 +260,20 @@ export default class GeoLocationHandler extends VariableUiElement { if (b !== true) { // B is an array with our locklocation inRange = - b[0][0] <= location.latlng[0] && - location.latlng[0] <= b[1][0] && - b[0][1] <= location.latlng[1] && - location.latlng[1] <= b[1][1]; + b[0][0] <= location.latitude && + location.latitude <= b[1][0] && + b[0][1] <= location.longitude && + location.longitude <= b[1][1]; } } if (!inRange) { console.log( "Not zooming to GPS location: out of bounds", b, - location.latlng + location ); } else { - this._leafletMap.data.setView(location.latlng, targetZoom); + this._leafletMap.data.setView([location.latitude, location.longitude], targetZoom); } } @@ -312,10 +297,7 @@ export default class GeoLocationHandler extends VariableUiElement { navigator.geolocation.watchPosition( function (position) { - self._currentGPSLocation.setData({ - latlng: [position.coords.latitude, position.coords.longitude], - accuracy: position.coords.accuracy, - }); + self._currentGPSLocation.setData(position.coords); }, function () { console.warn("Could not get location with navigator.geolocation"); diff --git a/Logic/BBox.ts b/Logic/BBox.ts index 78634897b..ccd320125 100644 --- a/Logic/BBox.ts +++ b/Logic/BBox.ts @@ -116,6 +116,11 @@ export class BBox { getSouth() { return this.minLat } + + contains(lonLat: [number, number]){ + return this.minLat <= lonLat[1] && lonLat[1] <= this.maxLat + && this.minLon<= lonLat[0] && lonLat[0] <= this.maxLon + } pad(factor: number, maxIncrease = 2): BBox { diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index ecec7cfde..4e90f1616 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -228,11 +228,15 @@ export default class FeaturePipeline { }) if(state.layoutToUse.trackAllNodes){ - new FullNodeDatabaseSource(state, osmFeatureSource, tile => { + const fullNodeDb = new FullNodeDatabaseSource( + state.filteredLayers.data.filter(l => l.layerDef.id === "type_node")[0], + tile => { new RegisteringAllFromFeatureSourceActor(tile) perLayerHierarchy.get(tile.layer.layerDef.id).registerTile(tile) tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile)) }) + + osmFeatureSource.rawDataHandlers.push((osmJson, tileId) => fullNodeDb.handleOsmJson(osmJson, tileId)) } diff --git a/Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource.ts b/Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource.ts index 92138b995..bd73f04c2 100644 --- a/Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource.ts +++ b/Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource.ts @@ -70,7 +70,7 @@ export class NewGeometryFromChangesFeatureSource implements FeatureSource { const w = new OsmWay(change.id) w.tags = tags w.nodes = change.changes["nodes"] - w.coordinates = change.changes["coordinates"].map(coor => coor.reverse()) + w.coordinates = change.changes["coordinates"].map(coor => [coor[1], coor[0]]) add(w.asGeoJson()) break; case "relation": diff --git a/Logic/FeatureSource/Sources/RenderingMultiPlexerFeatureSource.ts b/Logic/FeatureSource/Sources/RenderingMultiPlexerFeatureSource.ts index 5d2161a4e..eb0d4b10d 100644 --- a/Logic/FeatureSource/Sources/RenderingMultiPlexerFeatureSource.ts +++ b/Logic/FeatureSource/Sources/RenderingMultiPlexerFeatureSource.ts @@ -32,7 +32,7 @@ export default class RenderingMultiPlexerFeatureSource { const withIndex: (any & { pointRenderingIndex: number | undefined, lineRenderingIndex: number | undefined })[] = []; - function addAsPoint(feat, rendering, coordinate) { + function addAsPoint(feat, rendering, coordinate) { const patched = { ...feat, pointRenderingIndex: rendering.index @@ -46,8 +46,6 @@ export default class RenderingMultiPlexerFeatureSource { for (const f of features) { const feat = f.feature; - - if (feat.geometry.type === "Point") { for (const rendering of pointRenderings) { diff --git a/Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource.ts b/Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource.ts index 20d7bee07..026e164f7 100644 --- a/Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource.ts +++ b/Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource.ts @@ -2,30 +2,103 @@ import TileHierarchy from "./TileHierarchy"; import FeatureSource, {FeatureSourceForLayer, Tiled} from "../FeatureSource"; import {OsmNode, OsmObject, OsmWay} from "../../Osm/OsmObject"; import SimpleFeatureSource from "../Sources/SimpleFeatureSource"; -import {UIEventSource} from "../../UIEventSource"; import FilteredLayer from "../../../Models/FilteredLayer"; +import {TagsFilter} from "../../Tags/TagsFilter"; +import OsmChangeAction from "../../Osm/Actions/OsmChangeAction"; +import StaticFeatureSource from "../Sources/StaticFeatureSource"; +import {OsmConnection} from "../../Osm/OsmConnection"; +import {GeoOperations} from "../../GeoOperations"; +import {Utils} from "../../../Utils"; +import {UIEventSource} from "../../UIEventSource"; +import {BBox} from "../../BBox"; +import FeaturePipeline from "../FeaturePipeline"; +import {Tag} from "../../Tags/Tag"; +import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig"; +import {ChangeDescription} from "../../Osm/Actions/ChangeDescription"; +import CreateNewNodeAction from "../../Osm/Actions/CreateNewNodeAction"; +import ChangeTagAction from "../../Osm/Actions/ChangeTagAction"; +import {And} from "../../Tags/And"; export default class FullNodeDatabaseSource implements TileHierarchy { public readonly loadedTiles = new Map() private readonly onTileLoaded: (tile: (Tiled & FeatureSourceForLayer)) => void; - private readonly layer : FilteredLayer - + private readonly layer: FilteredLayer + constructor( - state: { - readonly filteredLayers: UIEventSource}, - osmFeatureSource: { rawDataHandlers: ((data: any, tileId: number) => void)[] }, + layer: FilteredLayer, onTileLoaded: ((tile: Tiled & FeatureSourceForLayer) => void)) { this.onTileLoaded = onTileLoaded - this.layer = state.filteredLayers.data.filter(l => l.layerDef.id === "type_node")[0] - if(this.layer === undefined){ - throw "Weird: tracking all nodes, but layer 'type_node' is not defined" + this.layer = layer; + if (this.layer === undefined) { + throw "Layer is undefined" } - const self = this - osmFeatureSource.rawDataHandlers.push((osmJson, tileId) => self.handleOsmXml(osmJson, tileId)) } - private handleOsmXml(osmJson: any, tileId: number) { + /** + * Given a list of coordinates, will search already existing OSM-points to snap onto. + * Either the geometry will be moved OR the existing point will be moved, depending on configuration and tags. + * This requires the 'type_node'-layer to be activated + */ + public static MergePoints( + state: { + filteredLayers: UIEventSource, + featurePipeline: FeaturePipeline, + layoutToUse: LayoutConfig + }, + newGeometryLngLats: [number, number][], + configs: ConflationConfig[], + ) { + const typeNode = state.filteredLayers.data.filter(l => l.layerDef.id === "type_node")[0] + if (typeNode === undefined) { + throw "Type Node layer is not defined. Add 'type_node' as layer to your layerconfig to use this feature" + } + + const bbox = new BBox(newGeometryLngLats) + const bbox_padded = bbox.pad(1.2) + const allNodes: any[] = [].concat(...state.featurePipeline.GetFeaturesWithin("type_node", bbox).map(tile => tile.filter( + feature => bbox_padded.contains(GeoOperations.centerpointCoordinates(feature)) + ))) + // The strategy: for every point of the new geometry, we search a point that is closeby and matches + // If multiple options match, we choose the most optimal (aka closest) + + const maxDistance = Math.max(...configs.map(c => c.withinRangeOfM)) + for (const coordinate of newGeometryLngLats) { + + let closestNode = undefined; + let closestNodeDistance = undefined + for (const node of allNodes) { + const d = GeoOperations.distanceBetween(GeoOperations.centerpointCoordinates(node), coordinate) + if (d > maxDistance) { + continue + } + let matchesSomeConfig = false + for (const config of configs) { + if (d > config.withinRangeOfM) { + continue + } + if (!config.ifMatches.matchesProperties(node.properties)) { + continue + } + matchesSomeConfig = true; + } + if (!matchesSomeConfig) { + continue + } + if (closestNode === undefined || closestNodeDistance > d) { + closestNode = node; + closestNodeDistance = d; + } + } + + + } + + } + + + + public handleOsmJson(osmJson: any, tileId: number) { const allObjects = OsmObject.ParseObjects(osmJson.elements) const nodesById = new Map() @@ -57,7 +130,7 @@ export default class FullNodeDatabaseSource implements TileHierarchy ({ - feature: osmNode.asGeoJson(),freshness: now + feature: osmNode.asGeoJson(), freshness: now })) const featureSource = new SimpleFeatureSource(this.layer, tileId) @@ -66,5 +139,12 @@ export default class FullNodeDatabaseSource implements TileHierarchy { - console.log("Tile ", Tiles.tile_from_index(neededTile).join("/"), "loaded") + console.debug("Tile ", Tiles.tile_from_index(neededTile).join("/"), "loaded") }) } } catch (e) { @@ -98,7 +98,7 @@ export default class OsmFeatureSource { console.log("Attempting to get tile", z, x, y, "from the osm api") const osmJson = await Utils.downloadJson(url) try { - console.log("Got tile", z, x, y, "from the osm api") + console.debug("Got tile", z, x, y, "from the osm api") this.rawDataHandlers.forEach(handler => handler(osmJson, Tiles.tile_index(z, x, y))) const geojson = OsmToGeoJson.default(osmJson, // @ts-ignore @@ -110,10 +110,8 @@ export default class OsmFeatureSource { // We only keep what is needed geojson.features = geojson.features.filter(feature => this.allowedTags.matchesProperties(feature.properties)) - geojson.features.forEach(f => f.properties["_backend"] = this._backend) - console.log("Tile geojson:", z, x, y, "is", geojson) const index = Tiles.tile_index(z, x, y); new PerLayerFeatureSourceSplitter(this.filteredLayers, this.handleTile, diff --git a/Logic/Osm/Actions/ChangeTagAction.ts b/Logic/Osm/Actions/ChangeTagAction.ts index 00e9b001e..862d36629 100644 --- a/Logic/Osm/Actions/ChangeTagAction.ts +++ b/Logic/Osm/Actions/ChangeTagAction.ts @@ -11,7 +11,7 @@ export default class ChangeTagAction extends OsmChangeAction { constructor(elementId: string, tagsFilter: TagsFilter, currentTags: any, meta: { theme: string, - changeType: "answer" | "soft-delete" | "add-image" + changeType: "answer" | "soft-delete" | "add-image" | string }) { super(); this._elementId = elementId; @@ -27,11 +27,16 @@ export default class ChangeTagAction extends OsmChangeAction { const key = kv.k; const value = kv.v; if (key === undefined || key === null) { - console.log("Invalid key"); + console.error("Invalid key:", key); return undefined; } if (value === undefined || value === null) { - console.log("Invalid value for ", key); + console.error("Invalid value for ", key,":", value); + return undefined; + } + + if(typeof value !== "string"){ + console.error("Invalid value for ", key, "as it is not a string:", value) return undefined; } diff --git a/Logic/Osm/Actions/CreateNewWayAction.ts b/Logic/Osm/Actions/CreateNewWayAction.ts index ec5486121..48b7ec7fb 100644 --- a/Logic/Osm/Actions/CreateNewWayAction.ts +++ b/Logic/Osm/Actions/CreateNewWayAction.ts @@ -4,39 +4,25 @@ import {Changes} from "../Changes"; import {Tag} from "../../Tags/Tag"; import CreateNewNodeAction from "./CreateNewNodeAction"; import {And} from "../../Tags/And"; -import {TagsFilter} from "../../Tags/TagsFilter"; export default class CreateNewWayAction extends OsmChangeAction { public newElementId: string = undefined private readonly coordinates: ({ nodeId?: number, lat: number, lon: number })[]; private readonly tags: Tag[]; private readonly _options: { - theme: string, existingPointHandling?: { - withinRangeOfM: number, - ifMatches?: TagsFilter, - mode: "reuse_osm_point" | "move_osm_point" - } [] + theme: string }; /*** * Creates a new way to upload to OSM - * @param tags: the tags to apply to the wya + * @param tags: the tags to apply to the way * @param coordinates: the coordinates. Might have a nodeId, in this case, this node will be used * @param options */ constructor(tags: Tag[], coordinates: ({ nodeId?: number, lat: number, lon: number })[], options: { - theme: string, - /** - * IF specified, an existing OSM-point within this range and satisfying the condition 'ifMatches' will be used instead of a new coordinate. - * If multiple points are possible, only the closest point is considered - */ - existingPointHandling?: { - withinRangeOfM: number, - ifMatches?: TagsFilter, - mode: "reuse_osm_point" | "move_osm_point" - } [] + theme: string }) { super() this.coordinates = coordinates; diff --git a/Logic/Osm/Actions/ReplaceGeometryAction.ts b/Logic/Osm/Actions/ReplaceGeometryAction.ts new file mode 100644 index 000000000..72195ff7f --- /dev/null +++ b/Logic/Osm/Actions/ReplaceGeometryAction.ts @@ -0,0 +1,232 @@ +import OsmChangeAction from "./OsmChangeAction"; +import {Changes} from "../Changes"; +import {ChangeDescription} from "./ChangeDescription"; +import {Tag} from "../../Tags/Tag"; +import FeatureSource from "../../FeatureSource/FeatureSource"; +import {OsmNode, OsmObject, OsmWay} from "../OsmObject"; +import {GeoOperations} from "../../GeoOperations"; +import StaticFeatureSource from "../../FeatureSource/Sources/StaticFeatureSource"; +import CreateNewNodeAction from "./CreateNewNodeAction"; +import ChangeTagAction from "./ChangeTagAction"; +import {And} from "../../Tags/And"; +import {Utils} from "../../../Utils"; +import {OsmConnection} from "../OsmConnection"; + +export default class ReplaceGeometryAction extends OsmChangeAction { + private readonly feature: any; + private readonly state: { + osmConnection: OsmConnection + }; + private readonly wayToReplaceId: string; + private readonly theme: string; + private readonly targetCoordinates: [number, number][]; + private readonly newTags: Tag[] | undefined; + + constructor( + state: { + osmConnection: OsmConnection + }, + feature: any, + wayToReplaceId: string, + options: { + theme: string, + newTags?: Tag[] + } + ) { + super(); + this.state = state; + this.feature = feature; + this.wayToReplaceId = wayToReplaceId; + this.theme = options.theme; + + const geom = this.feature.geometry + let coordinates: [number, number][] + if (geom.type === "LineString") { + coordinates = geom.coordinates + } else if (geom.type === "Polygon") { + coordinates = geom.coordinates[0] + } + this.targetCoordinates = coordinates + this.newTags = options.newTags + } + + public async GetPreview(): Promise { + const {closestIds, allNodesById} = await this.GetClosestIds(); + const preview = closestIds.map((newId, i) => { + if (newId === undefined) { + return { + type: "Feature", + properties: { + "newpoint": "yes", + "id": "replace-geometry-move-" + i + }, + geometry: { + type: "Point", + coordinates: this.targetCoordinates[i] + } + }; + } + const origPoint = allNodesById.get(newId).centerpoint() + return { + type: "Feature", + properties: { + "move": "yes", + "osm-id": newId, + "id": "replace-geometry-move-" + i + }, + geometry: { + type: "LineString", + coordinates: [[origPoint[1], origPoint[0]], this.targetCoordinates[i]] + } + }; + }) + return new StaticFeatureSource(preview, false) + + } + + protected async CreateChangeDescriptions(changes: Changes): Promise { + + const allChanges: ChangeDescription[] = [] + const actualIdsToUse: number[] = [] + + const {closestIds, osmWay} = await this.GetClosestIds() + + for (let i = 0; i < closestIds.length; i++) { + const closestId = closestIds[i]; + const [lon, lat] = this.targetCoordinates[i] + if (closestId === undefined) { + + const newNodeAction = new CreateNewNodeAction( + [], + lat, lon, + { + allowReuseOfPreviouslyCreatedPoints: true, + theme: this.theme, changeType: null + }) + const changeDescr = await newNodeAction.CreateChangeDescriptions(changes) + allChanges.push(...changeDescr) + actualIdsToUse.push(newNodeAction.newElementIdNumber) + + } else { + const change = { + id: closestId, + type: "node", + meta: { + theme: this.theme, + changeType: "move" + }, + changes: {lon, lat} + } + actualIdsToUse.push(closestId) + allChanges.push(change) + } + } + + + if (this.newTags !== undefined && this.newTags.length > 0) { + const addExtraTags = new ChangeTagAction( + this.wayToReplaceId, + new And(this.newTags), + osmWay.tags, { + theme: this.theme, + changeType: "conflation" + } + ) + allChanges.push(...await addExtraTags.CreateChangeDescriptions(changes)) + } + + // AT the very last: actually change the nodes of the way! + allChanges.push({ + type: "way", + id: osmWay.id, + changes: { + nodes: actualIdsToUse, + coordinates: this.targetCoordinates + }, + meta: { + theme: this.theme, + changeType: "conflation" + } + }) + + + return allChanges + } + + /** + * For 'this.feature`, gets a corresponding closest node that alreay exsists + * @constructor + * @private + */ + private async GetClosestIds(): Promise<{ closestIds: number[], allNodesById: Map, osmWay: OsmWay }> { + // TODO FIXME: cap move length on points which are embedded into other ways (ev. disconnect them) + // TODO FIXME: if a new point has to be created, snap to already existing ways + // TODO FIXME: reuse points if they are the same in the target coordinates + const splitted = this.wayToReplaceId.split("/"); + const type = splitted[0]; + const idN = Number(splitted[1]); + if (idN < 0 || type !== "way") { + throw "Invalid ID to conflate: " + this.wayToReplaceId + } + const url = `${this.state.osmConnection._oauth_config.url}/api/0.6/${this.wayToReplaceId}/full`; + const rawData = await Utils.downloadJsonCached(url, 1000) + const parsed = OsmObject.ParseObjects(rawData.elements); + const allNodesById = new Map() + const allNodes = parsed.filter(o => o.type === "node") + for (const node of allNodes) { + allNodesById.set(node.id, node) + } + + + /** + * Allright! We know all the nodes of the original way and all the nodes of the target coordinates. + * For each of the target coordinates, we search the closest, already existing point and reuse this point + */ + + const closestIds = [] + const distances = [] + for (const target of this.targetCoordinates) { + let closestDistance = undefined + let closestId = undefined; + for (const osmNode of allNodes) { + + const cp = osmNode.centerpoint() + const d = GeoOperations.distanceBetween(target, [cp[1], cp[0]]) + if (closestId === undefined || closestDistance > d) { + closestId = osmNode.id + closestDistance = d + } + } + closestIds.push(closestId) + distances.push(closestDistance) + } + + // Next step: every closestId can only occur once in the list + for (let i = 0; i < closestIds.length; i++) { + const closestId = closestIds[i] + for (let j = i + 1; j < closestIds.length; j++) { + const otherClosestId = closestIds[j] + if (closestId !== otherClosestId) { + continue + } + // We have two occurences of 'closestId' - we only keep the closest instance! + const di = distances[i] + const dj = distances[j] + if (di < dj) { + closestIds[j] = undefined + } else { + closestIds[i] = undefined + } + } + } + + + const osmWay = parsed[parsed.length - 1] + if (osmWay.type !== "way") { + throw "WEIRD: expected an OSM-way as last element here!" + } + return {closestIds, allNodesById, osmWay}; + } + + +} \ No newline at end of file diff --git a/Logic/Osm/Changes.ts b/Logic/Osm/Changes.ts index 48fd10cf7..72e5434c5 100644 --- a/Logic/Osm/Changes.ts +++ b/Logic/Osm/Changes.ts @@ -114,7 +114,16 @@ export class Changes { } public async applyAction(action: OsmChangeAction): Promise { - const changes = await action.Perform(this) + this.applyChanges(await action.Perform(this)) + } + + public async applyActions(actions: OsmChangeAction[]) { + for (const action of actions) { + await this.applyAction(action) + } + } + + public applyChanges(changes: ChangeDescription[]) { console.log("Received changes:", changes) this.pendingChanges.data.push(...changes); this.pendingChanges.ping(); @@ -126,6 +135,7 @@ export class Changes { CreateNewNodeAction.registerIdRewrites(mappings) } + /** * UPload the selected changes to OSM. * Returns 'true' if successfull and if they can be removed diff --git a/Logic/State/MapState.ts b/Logic/State/MapState.ts index 00a05dd61..8939149f5 100644 --- a/Logic/State/MapState.ts +++ b/Logic/State/MapState.ts @@ -14,6 +14,7 @@ import {QueryParameters} from "../Web/QueryParameters"; import * as personal from "../../assets/themes/personal/personal.json"; import FilterConfig from "../../Models/ThemeConfig/FilterConfig"; import ShowOverlayLayer from "../../UI/ShowDataLayer/ShowOverlayLayer"; +import {Coord} from "@turf/turf"; /** * Contains all the leaflet-map related state @@ -44,13 +45,7 @@ export default class MapState extends UserRelatedState { /** * The location as delivered by the GPS */ - public currentGPSLocation: UIEventSource<{ - latlng: { lat: number; lng: number }; - accuracy: number; - }> = new UIEventSource<{ - latlng: { lat: number; lng: number }; - accuracy: number; - }>(undefined); + public currentGPSLocation: UIEventSource = new UIEventSource(undefined); public readonly mainMapObject: BaseUIElement & MinimapObj; diff --git a/UI/Base/AsyncLazy.ts b/UI/Base/AsyncLazy.ts new file mode 100644 index 000000000..b8db53d3c --- /dev/null +++ b/UI/Base/AsyncLazy.ts @@ -0,0 +1,28 @@ +import BaseUIElement from "../BaseUIElement"; +import {VariableUiElement} from "./VariableUIElement"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import Loading from "./Loading"; + +export default class AsyncLazy extends BaseUIElement{ + private readonly _f: () => Promise; + + constructor(f: () => Promise) { + super(); + this._f = f; + } + + protected InnerConstructElement(): HTMLElement { + // The caching of the BaseUIElement will guarantee that _f will only be called once + + return new VariableUiElement( + UIEventSource.FromPromise(this._f()).map(el => { + if(el === undefined){ + return new Loading() + } + return el + }) + + ).ConstructElement() + } + +} \ No newline at end of file diff --git a/UI/Base/Minimap.ts b/UI/Base/Minimap.ts index 32cdadbd6..4a878684e 100644 --- a/UI/Base/Minimap.ts +++ b/UI/Base/Minimap.ts @@ -19,6 +19,7 @@ export interface MinimapOptions { export interface MinimapObj { readonly leafletMap: UIEventSource, installBounds(factor: number | BBox, showRange?: boolean) : void + TakeScreenshot(): Promise; } export default class Minimap { diff --git a/UI/Base/MinimapImplementation.ts b/UI/Base/MinimapImplementation.ts index 249d4df6d..271b5d5cf 100644 --- a/UI/Base/MinimapImplementation.ts +++ b/UI/Base/MinimapImplementation.ts @@ -9,6 +9,7 @@ import {Map} from "leaflet"; import Minimap, {MinimapObj, MinimapOptions} from "./Minimap"; import {BBox} from "../../Logic/BBox"; import 'leaflet-polylineoffset' +import {SimpleMapScreenshoter} from "leaflet-simple-map-screenshoter"; export default class MinimapImplementation extends BaseUIElement implements MinimapObj { private static _nextId = 0; @@ -278,4 +279,10 @@ export default class MinimapImplementation extends BaseUIElement implements Mini this.leafletMap.setData(map) } + + public async TakeScreenshot(){ + const screenshotter = new SimpleMapScreenshoter(); + screenshotter.addTo(this.leafletMap.data); + return await screenshotter.takeScreen('image') + } } \ No newline at end of file diff --git a/UI/BigComponents/FullWelcomePaneWithTabs.ts b/UI/BigComponents/FullWelcomePaneWithTabs.ts index 59ca6e0c9..da16326e4 100644 --- a/UI/BigComponents/FullWelcomePaneWithTabs.ts +++ b/UI/BigComponents/FullWelcomePaneWithTabs.ts @@ -14,6 +14,9 @@ import Toggle from "../Input/Toggle"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; import {Utils} from "../../Utils"; import UserRelatedState from "../../Logic/State/UserRelatedState"; +import Loc from "../../Models/Loc"; +import BaseLayer from "../../Models/BaseLayer"; +import FilteredLayer from "../../Models/FilteredLayer"; export default class FullWelcomePaneWithTabs extends ScrollableFullScreen { @@ -24,7 +27,10 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen { layoutToUse: LayoutConfig, osmConnection: OsmConnection, featureSwitchShareScreen: UIEventSource, - featureSwitchMoreQuests: UIEventSource + featureSwitchMoreQuests: UIEventSource, + locationControl: UIEventSource, + backgroundLayer: UIEventSource, + filteredLayers: UIEventSource } & UserRelatedState) { const layoutToUse = state.layoutToUse; super( @@ -39,7 +45,8 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen { layoutToUse: LayoutConfig, osmConnection: OsmConnection, featureSwitchShareScreen: UIEventSource, - featureSwitchMoreQuests: UIEventSource + featureSwitchMoreQuests: UIEventSource, + locationControl: UIEventSource, backgroundLayer: UIEventSource, filteredLayers: UIEventSource } & UserRelatedState, isShown: UIEventSource): { header: string | BaseUIElement; content: BaseUIElement }[] { @@ -77,7 +84,8 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen { layoutToUse: LayoutConfig, osmConnection: OsmConnection, featureSwitchShareScreen: UIEventSource, - featureSwitchMoreQuests: UIEventSource + featureSwitchMoreQuests: UIEventSource, + locationControl: UIEventSource, backgroundLayer: UIEventSource, filteredLayers: UIEventSource } & UserRelatedState, currentTab: UIEventSource, isShown: UIEventSource) { const tabs = FullWelcomePaneWithTabs.ConstructBaseTabs(state, isShown) diff --git a/UI/BigComponents/ImportButton.ts b/UI/BigComponents/ImportButton.ts index 9c206f249..fd35a72db 100644 --- a/UI/BigComponents/ImportButton.ts +++ b/UI/BigComponents/ImportButton.ts @@ -9,7 +9,6 @@ import Toggle from "../Input/Toggle"; import CreateNewNodeAction from "../../Logic/Osm/Actions/CreateNewNodeAction"; import {Tag} from "../../Logic/Tags/Tag"; import Loading from "../Base/Loading"; -import OsmChangeAction from "../../Logic/Osm/Actions/OsmChangeAction"; import CreateNewWayAction from "../../Logic/Osm/Actions/CreateNewWayAction"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; import {OsmConnection} from "../../Logic/Osm/OsmConnection"; @@ -26,6 +25,13 @@ import SpecialVisualizations, {SpecialVisualization} from "../SpecialVisualizati import {FixedUiElement} from "../Base/FixedUiElement"; import Svg from "../../Svg"; import {Utils} from "../../Utils"; +import Minimap from "../Base/Minimap"; +import ShowDataLayer from "../ShowDataLayer/ShowDataLayer"; +import AllKnownLayers from "../../Customizations/AllKnownLayers"; +import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"; +import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer"; +import BaseLayer from "../../Models/BaseLayer"; +import ReplaceGeometryAction from "../../Logic/Osm/Actions/ReplaceGeometryAction"; export interface ImportButtonState { @@ -38,6 +44,8 @@ export interface ImportButtonState { feature: any, minZoom: number, state: { + backgroundLayer: UIEventSource; + filteredLayers: UIEventSource; featureSwitchUserbadge: UIEventSource; featurePipeline: FeaturePipeline; allElements: ElementStorage; @@ -48,8 +56,14 @@ export interface ImportButtonState { locationControl: UIEventSource<{ zoom: number }> }, guiState: { filterViewIsOpened: UIEventSource }, - snapToLayers?: string[], - snapToLayersMaxDist?: number + + snapSettings?: { + snapToLayers: string[], + snapToLayersMaxDist?: number + }, + conflationSettings?: { + conflateWayId: string + } } export class ImportButtonSpecialViz implements SpecialVisualization { @@ -83,7 +97,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be #### Specifying which tags to copy or add -The first argument of the import button takes a \`;\`-seperated list of tags to add. +The argument \`tags\` of the import button takes a \`;\`-seperated list of tags to add. ${Utils.Special_visualizations_tagsToApplyHelpText} @@ -113,8 +127,9 @@ ${Utils.Special_visualizations_tagsToApplyHelpText} doc: "How far the contributor must zoom in before being able to import the point", defaultValue: "18" }, { - name: "Snap onto layer(s)", - doc: "If a way of the given layer is closeby, will snap the new point onto this way (similar as preset might snap). To show multiple layers to snap onto, use a `;`-seperated list", + name: "Snap onto layer(s)/replace geometry with this other way", + doc: " - If the value corresponding with this key starts with 'way/' and the feature is a LineString or Polygon, the original OSM-way geometry will be changed to match the new geometry\n" + + " - If a way of the given layer(s) is closeby, will snap the new point onto this way (similar as preset might snap). To show multiple layers to snap onto, use a `;`-seperated list", }, { name: "snap max distance", doc: "The maximum distance that this point will move to snap onto a layer (in meters)", @@ -130,7 +145,7 @@ ${Utils.Special_visualizations_tagsToApplyHelpText} const id = tagSource.data.id; const feature = state.allElements.ContainingFeatures.get(id) let minZoom = args[4] == "" ? 18 : Number(args[4]) - if(isNaN(minZoom)){ + if (isNaN(minZoom)) { console.warn("Invalid minzoom:", minZoom) minZoom = 18 } @@ -145,13 +160,29 @@ ${Utils.Special_visualizations_tagsToApplyHelpText} img = () => Svg.add_ui() } - const snapToLayers = args[5]?.split(";").filter(s => s !== "") - const snapToLayersMaxDist = Number(args[6] ?? 6) - - if (targetLayer === undefined) { - const e = "Target layer not defined: error in import button for theme: " + state.layoutToUse.id + ": layer " + args[0] + " not found" - console.error(e) - return new FixedUiElement(e).SetClass("alert") + let snapSettings = undefined + let conflationSettings = undefined + const possibleWayId = tagSource.data[args[5]] + if (possibleWayId?.startsWith("way/")) { + // This is a conflation + conflationSettings = { + conflateWayId: possibleWayId + } + } else { + + + const snapToLayers = args[5]?.split(";").filter(s => s !== "") + const snapToLayersMaxDist = Number(args[6] ?? 6) + + if (targetLayer === undefined) { + const e = "Target layer not defined: error in import button for theme: " + state.layoutToUse.id + ": layer " + args[0] + " not found" + console.error(e) + return new FixedUiElement(e).SetClass("alert") + } + snapSettings = { + snapToLayers, + snapToLayersMaxDist + } } return new ImportButton( @@ -160,8 +191,8 @@ ${Utils.Special_visualizations_tagsToApplyHelpText} feature, newTags, message, minZoom, originalTags: tagSource, targetLayer, - snapToLayers, - snapToLayersMaxDist + snapSettings, + conflationSettings } ); } @@ -201,7 +232,7 @@ export default class ImportButton extends Toggle { const importClicked = new UIEventSource(false); const importFlow = new Toggle( - new Lazy(() => ImportButton.createConfirmPanel(o, isImported, importClicked)), + ImportButton.createConfirmPanel(o, isImported, importClicked), importButton, importClicked ) @@ -228,7 +259,121 @@ export default class ImportButton extends Toggle { ) } - public static createConfirmPanel( + public static createConfirmPanel(o: ImportButtonState, + isImported: UIEventSource, + importClicked: UIEventSource) { + const geometry = o.feature.geometry + if (geometry.type === "Point") { + return new Lazy(() => ImportButton.createConfirmPanelForPoint(o, isImported, importClicked)) + } + + + if (geometry.type === "Polygon" || geometry.type == "LineString") { + return new Lazy(() => ImportButton.createConfirmForWay(o, isImported, importClicked)) + } + console.error("Invalid type to import", geometry.type) + return new FixedUiElement("Invalid geometry type:" + geometry.type).SetClass("alert") + + + } + + public static createConfirmForWay(o: ImportButtonState, + isImported: UIEventSource, + importClicked: UIEventSource): BaseUIElement { + + const confirmationMap = Minimap.createMiniMap({ + allowMoving: false, + background: o.state.backgroundLayer + }) + confirmationMap.SetStyle("height: 20rem; overflow: hidden").SetClass("rounded-xl") + + const relevantFeatures = Utils.NoNull([o.feature, o.state.allElements?.ContainingFeatures?.get(o.conflationSettings?.conflateWayId)]) + // SHow all relevant data - including (eventually) the way of which the geometry will be replaced + new ShowDataMultiLayer({ + leafletMap: confirmationMap.leafletMap, + enablePopups: false, + zoomToFeatures: true, + features: new StaticFeatureSource(relevantFeatures, false), + allElements: o.state.allElements, + layers: o.state.filteredLayers + }) + + const theme = o.state.layoutToUse.id + + + const changes = o.state.changes + let confirm: () => Promise + if (o.conflationSettings !== undefined) { + + let replaceGeometryAction = new ReplaceGeometryAction( + o.state, + o.feature, + o.conflationSettings.conflateWayId, + { + theme: o.state.layoutToUse.id, + newTags: o.newTags.data + } + ) + + replaceGeometryAction.GetPreview().then(changePreview => { + new ShowDataLayer({ + leafletMap: confirmationMap.leafletMap, + enablePopups: false, + zoomToFeatures: false, + features: changePreview, + allElements: o.state.allElements, + layerToShow: AllKnownLayers.sharedLayers.get("conflation") + }) + }) + + confirm = async () => { + changes.applyAction (replaceGeometryAction) + return o.feature.properties.id + } + + } else { + confirm = async () => { + const geom = o.feature.geometry + let coordinates: [number, number][] + if (geom.type === "LineString") { + coordinates = geom.coordinates + } else if (geom.type === "Polygon") { + coordinates = geom.coordinates[0] + } + const action = new CreateNewWayAction(o.newTags.data, coordinates.map(lngLat => ({ + lat: lngLat[1], + lon: lngLat[0] + })), {theme}) + return action.newElementId + } + } + + + const confirmButton = new SubtleButton(o.image(), o.message) + confirmButton.onClick(async () => { + { + if (isImported.data) { + return + } + o.originalTags.data["_imported"] = "yes" + o.originalTags.ping() // will set isImported as per its definition + + const idToSelect = await confirm() + + o.state.selectedElement.setData(o.state.allElements.ContainingFeatures.get(idToSelect)) + + } + }) + + const cancel = new SubtleButton(Svg.close_ui(), Translations.t.general.cancel).onClick(() => { + importClicked.setData(false) + }) + + + return new Combine([confirmationMap, confirmButton, cancel]).SetClass("flex flex-col") + } + + public static createConfirmPanelForPoint( o: ImportButtonState, isImported: UIEventSource, importClicked: UIEventSource): BaseUIElement { @@ -239,39 +384,43 @@ export default class ImportButton extends Toggle { } o.originalTags.data["_imported"] = "yes" o.originalTags.ping() // will set isImported as per its definition - const newElementAction = ImportButton.createAddActionForFeature(o.newTags.data, o.feature, o.state.layoutToUse.id) + const geometry = o.feature.geometry + const lat = geometry.coordinates[1] + const lon = geometry.coordinates[0]; + const newElementAction = new CreateNewNodeAction(o.newTags.data, lat, lon, { + theme: o.state.layoutToUse.id, + changeType: "import" + }) + await o.state.changes.applyAction(newElementAction) o.state.selectedElement.setData(o.state.allElements.ContainingFeatures.get( newElementAction.newElementId )) - console.log("Did set selected element to", o.state.allElements.ContainingFeatures.get( - newElementAction.newElementId - )) } function cancel() { importClicked.setData(false) } - if (o.feature.geometry.type === "Point") { - const presetInfo = { - tags: o.newTags.data, - icon: o.image, - description: o.description, - layerToAddTo: o.targetLayer, - name: o.message, - title: o.message, - preciseInput: { snapToLayers: o.snapToLayers, - maxSnapDistance: o.snapToLayersMaxDist} + const presetInfo = { + tags: o.newTags.data, + icon: o.image, + description: o.description, + layerToAddTo: o.targetLayer, + name: o.message, + title: o.message, + preciseInput: { + snapToLayers: o.snapSettings?.snapToLayers, + maxSnapDistance: o.snapSettings?.snapToLayersMaxDist } - - const [lon, lat] = o.feature.geometry.coordinates - console.log("Creating an import dialog at location", lon, lat) - return new ConfirmLocationOfPoint(o.state, o.guiState.filterViewIsOpened, presetInfo, Translations.W(o.message), { - lon, - lat - }, confirm, cancel) } + + const [lon, lat] = o.feature.geometry.coordinates + return new ConfirmLocationOfPoint(o.state, o.guiState.filterViewIsOpened, presetInfo, Translations.W(o.message), { + lon, + lat + }, confirm, cancel) + } @@ -279,41 +428,4 @@ export default class ImportButton extends Toggle { const type = feature.geometry.type return type === "Point" || type === "LineString" || (type === "Polygon" && feature.geometry.coordinates.length === 1) } - - private static createAddActionForFeature(newTags: Tag[], feature: any, theme: string): - OsmChangeAction & { newElementId: string } { - const geometry = feature.geometry - const type = geometry.type - if (type === "Point") { - const lat = geometry.coordinates[1] - const lon = geometry.coordinates[0]; - return new CreateNewNodeAction(newTags, lat, lon, { - theme, - changeType: "import" - }) - } - - if (type === "LineString") { - return new CreateNewWayAction( - newTags, - geometry.coordinates.map(coor => ({lon: coor[0], lat: coor[1]})), - { - theme - } - ) - } - - if (type === "Polygon") { - return new CreateNewWayAction( - newTags, - geometry.coordinates[0].map(coor => ({lon: coor[0], lat: coor[1]})), - { - theme - } - ) - } - - return undefined; - - } } \ No newline at end of file diff --git a/UI/BigComponents/LeftControls.ts b/UI/BigComponents/LeftControls.ts index f2459d002..aff6b01f4 100644 --- a/UI/BigComponents/LeftControls.ts +++ b/UI/BigComponents/LeftControls.ts @@ -14,6 +14,8 @@ import Loc from "../../Models/Loc"; import {BBox} from "../../Logic/BBox"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; import FilteredLayer from "../../Models/FilteredLayer"; +import BaseLayer from "../../Models/BaseLayer"; +import {OsmConnection} from "../../Logic/Osm/OsmConnection"; export default class LeftControls extends Combine { @@ -26,7 +28,9 @@ export default class LeftControls extends Combine { featureSwitchEnableExport: UIEventSource, featureSwitchExportAsPdf: UIEventSource, filteredLayers: UIEventSource, - featureSwitchFilter: UIEventSource + featureSwitchFilter: UIEventSource, + backgroundLayer: UIEventSource, + osmConnection: OsmConnection }, guiState: { downloadControlIsOpened: UIEventSource, diff --git a/UI/BigComponents/RightControls.ts b/UI/BigComponents/RightControls.ts index a8853a54f..352fc11e9 100644 --- a/UI/BigComponents/RightControls.ts +++ b/UI/BigComponents/RightControls.ts @@ -4,17 +4,30 @@ import MapControlButton from "../MapControlButton"; import GeoLocationHandler from "../../Logic/Actors/GeoLocationHandler"; import Svg from "../../Svg"; import MapState from "../../Logic/State/MapState"; +import ShowDataLayer from "../ShowDataLayer/ShowDataLayer"; +import AllKnownLayers from "../../Customizations/AllKnownLayers"; export default class RightControls extends Combine { constructor(state:MapState) { + + const geolocatioHandler = new GeoLocationHandler( + state.currentGPSLocation, + state.leafletMap, + state.layoutToUse + ) + + new ShowDataLayer({ + layerToShow: AllKnownLayers.sharedLayers.get("gps_location"), + leafletMap: state.leafletMap, + enablePopups: true, + features: geolocatioHandler.currentLocation + }) + const geolocationButton = new Toggle( new MapControlButton( - new GeoLocationHandler( - state.currentGPSLocation, - state.leafletMap, - state.layoutToUse - ), { + geolocatioHandler + , { dontStyle: true } ), diff --git a/UI/BigComponents/ShareScreen.ts b/UI/BigComponents/ShareScreen.ts index b112b15b0..9fc7dde91 100644 --- a/UI/BigComponents/ShareScreen.ts +++ b/UI/BigComponents/ShareScreen.ts @@ -8,11 +8,14 @@ import Toggle from "../Input/Toggle"; import Translations from "../i18n/Translations"; import BaseUIElement from "../BaseUIElement"; import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; -import MapState from "../../Logic/State/MapState"; +import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; +import Loc from "../../Models/Loc"; +import BaseLayer from "../../Models/BaseLayer"; +import FilteredLayer from "../../Models/FilteredLayer"; export default class ShareScreen extends Combine { - constructor(state: MapState) { + constructor(state: {layoutToUse: LayoutConfig, locationControl: UIEventSource, backgroundLayer: UIEventSource, filteredLayers: UIEventSource}) { const layout = state?.layoutToUse; const tr = Translations.t.general.sharescreen; diff --git a/UI/DefaultGUI.ts b/UI/DefaultGUI.ts index 8ee02fdd4..0c93e4bf8 100644 --- a/UI/DefaultGUI.ts +++ b/UI/DefaultGUI.ts @@ -6,9 +6,6 @@ import FullWelcomePaneWithTabs from "./BigComponents/FullWelcomePaneWithTabs"; import MapControlButton from "./MapControlButton"; import Svg from "../Svg"; import Toggle from "./Input/Toggle"; -import Hash from "../Logic/Web/Hash"; -import {QueryParameters} from "../Logic/Web/QueryParameters"; -import Constants from "../Models/Constants"; import UserBadge from "./BigComponents/UserBadge"; import SearchAndGo from "./BigComponents/SearchAndGo"; import Link from "./Base/Link"; @@ -24,77 +21,7 @@ import Translations from "./i18n/Translations"; import SimpleAddUI from "./BigComponents/SimpleAddUI"; import StrayClickHandler from "../Logic/Actors/StrayClickHandler"; import Lazy from "./Base/Lazy"; - -export class DefaultGuiState { - public readonly welcomeMessageIsOpened : UIEventSource; - public readonly downloadControlIsOpened: UIEventSource; - public readonly filterViewIsOpened: UIEventSource; - public readonly copyrightViewIsOpened: UIEventSource; - public readonly welcomeMessageOpenedTab: UIEventSource - public readonly allFullScreenStates: UIEventSource[] = [] - static state: DefaultGuiState; - - constructor() { - - - - this.welcomeMessageOpenedTab = UIEventSource.asFloat(QueryParameters.GetQueryParameter( - "tab", - "0", - `The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >${Constants.userJourney.mapCompleteHelpUnlock} changesets)` - )); - this.welcomeMessageIsOpened = QueryParameters.GetBooleanQueryParameter( - "welcome-control-toggle", - "false", - "Whether or not the welcome panel is shown" - ) - this.downloadControlIsOpened = QueryParameters.GetBooleanQueryParameter( - "download-control-toggle", - "false", - "Whether or not the download panel is shown" - ) - this.filterViewIsOpened = QueryParameters.GetBooleanQueryParameter( - "filter-toggle", - "false", - "Whether or not the filter view is shown" - ) - this.copyrightViewIsOpened = QueryParameters.GetBooleanQueryParameter( - "copyright-toggle", - "false", - "Whether or not the copyright view is shown" - ) - if(Hash.hash.data === "download"){ - this.downloadControlIsOpened.setData(true) - } - if(Hash.hash.data === "filters"){ - this.filterViewIsOpened.setData(true) - } - if(Hash.hash.data === "copyright"){ - this.copyrightViewIsOpened.setData(true) - } - if(Hash.hash.data === "" || Hash.hash.data === undefined || Hash.hash.data === "welcome"){ - this.welcomeMessageIsOpened.setData(true) - } - - this.allFullScreenStates.push(this.downloadControlIsOpened, this.filterViewIsOpened, this.copyrightViewIsOpened, this.welcomeMessageIsOpened) - - for (let i = 0; i < this.allFullScreenStates.length; i++){ - const fullScreenState = this.allFullScreenStates[i]; - for (let j = 0; j < this.allFullScreenStates.length; j++){ - if(i == j){ - continue - } - const otherState = this.allFullScreenStates[j]; - fullScreenState.addCallbackAndRunD(isOpened => { - if(isOpened){ - otherState.setData(false) - } - }) - } - } - - } -} +import {DefaultGuiState} from "./DefaultGuiState"; /** diff --git a/UI/DefaultGuiState.ts b/UI/DefaultGuiState.ts new file mode 100644 index 000000000..7c5cae139 --- /dev/null +++ b/UI/DefaultGuiState.ts @@ -0,0 +1,74 @@ +import {UIEventSource} from "../Logic/UIEventSource"; +import {QueryParameters} from "../Logic/Web/QueryParameters"; +import Constants from "../Models/Constants"; +import Hash from "../Logic/Web/Hash"; + +export class DefaultGuiState { + public readonly welcomeMessageIsOpened: UIEventSource; + public readonly downloadControlIsOpened: UIEventSource; + public readonly filterViewIsOpened: UIEventSource; + public readonly copyrightViewIsOpened: UIEventSource; + public readonly welcomeMessageOpenedTab: UIEventSource + public readonly allFullScreenStates: UIEventSource[] = [] + static state: DefaultGuiState; + + constructor() { + + + this.welcomeMessageOpenedTab = UIEventSource.asFloat(QueryParameters.GetQueryParameter( + "tab", + "0", + `The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >${Constants.userJourney.mapCompleteHelpUnlock} changesets)` + )); + this.welcomeMessageIsOpened = QueryParameters.GetBooleanQueryParameter( + "welcome-control-toggle", + "false", + "Whether or not the welcome panel is shown" + ) + this.downloadControlIsOpened = QueryParameters.GetBooleanQueryParameter( + "download-control-toggle", + "false", + "Whether or not the download panel is shown" + ) + this.filterViewIsOpened = QueryParameters.GetBooleanQueryParameter( + "filter-toggle", + "false", + "Whether or not the filter view is shown" + ) + this.copyrightViewIsOpened = QueryParameters.GetBooleanQueryParameter( + "copyright-toggle", + "false", + "Whether or not the copyright view is shown" + ) + if (Hash.hash.data === "download") { + this.downloadControlIsOpened.setData(true) + } + if (Hash.hash.data === "filters") { + this.filterViewIsOpened.setData(true) + } + if (Hash.hash.data === "copyright") { + this.copyrightViewIsOpened.setData(true) + } + if (Hash.hash.data === "" || Hash.hash.data === undefined || Hash.hash.data === "welcome") { + this.welcomeMessageIsOpened.setData(true) + } + + this.allFullScreenStates.push(this.downloadControlIsOpened, this.filterViewIsOpened, this.copyrightViewIsOpened, this.welcomeMessageIsOpened) + + for (let i = 0; i < this.allFullScreenStates.length; i++) { + const fullScreenState = this.allFullScreenStates[i]; + for (let j = 0; j < this.allFullScreenStates.length; j++) { + if (i == j) { + continue + } + const otherState = this.allFullScreenStates[j]; + fullScreenState.addCallbackAndRunD(isOpened => { + if (isOpened) { + otherState.setData(false) + } + }) + } + } + + } +} \ No newline at end of file diff --git a/UI/ExportPDF.ts b/UI/ExportPDF.ts index e030e30b4..e03103322 100644 --- a/UI/ExportPDF.ts +++ b/UI/ExportPDF.ts @@ -1,9 +1,6 @@ - - import jsPDF from "jspdf"; -import {SimpleMapScreenshoter} from "leaflet-simple-map-screenshoter"; import {UIEventSource} from "../Logic/UIEventSource"; -import Minimap from "./Base/Minimap"; +import Minimap, {MinimapObj} from "./Base/Minimap"; import Loc from "../Models/Loc"; import BaseLayer from "../Models/BaseLayer"; import {FixedUiElement} from "./Base/FixedUiElement"; @@ -14,7 +11,6 @@ import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"; import FeaturePipeline from "../Logic/FeatureSource/FeaturePipeline"; import ShowDataLayer from "./ShowDataLayer/ShowDataLayer"; import {BBox} from "../Logic/BBox"; -import ShowOverlayLayer from "./ShowDataLayer/ShowOverlayLayer"; /** * Creates screenshoter to take png screenshot * Creates jspdf and downloads it @@ -63,14 +59,12 @@ export default class ExportPDF { location: new UIEventSource(loc), // We remove the link between the old and the new UI-event source as moving the map while the export is running fucks up the screenshot background: options.background, allowMoving: false, - - - onFullyLoaded: leaflet => window.setTimeout(() => { + onFullyLoaded: _ => window.setTimeout(() => { if (self._screenhotTaken) { return; } try { - self.CreatePdf(leaflet) + self.CreatePdf(minimap) .then(() => self.cleanup()) .catch(() => self.cleanup()) } catch (e) { @@ -112,20 +106,17 @@ export default class ExportPDF { this._screenhotTaken = true; } - private async CreatePdf(leaflet: L.Map) { + private async CreatePdf(minimap: MinimapObj) { + + + console.log("PDF creation started") const t = Translations.t.general.pdf; const layout = this._layout - const screenshotter = new SimpleMapScreenshoter(); - //minimap op index.html -> hidden daar alles op doen en dan weg - //minimap - leaflet map ophalen - boundaries ophalen - State.state.featurePipeline - screenshotter.addTo(leaflet); - let doc = new jsPDF('landscape'); - - const image = (await screenshotter.takeScreen('image')) + const image = await minimap.TakeScreenshot() // @ts-ignore doc.addImage(image, 'PNG', 0, 0, this.mapW, this.mapH); diff --git a/UI/Input/LocationInput.ts b/UI/Input/LocationInput.ts index 65ecd604c..21cfdebe7 100644 --- a/UI/Input/LocationInput.ts +++ b/UI/Input/LocationInput.ts @@ -167,6 +167,9 @@ export default class LocationInput extends InputElement implements MinimapO installBounds(factor: number | BBox, showRange?: boolean): void { this.map.installBounds(factor, showRange) } + TakeScreenshot(): Promise { + return this.map.TakeScreenshot() + } protected InnerConstructElement(): HTMLElement { try { diff --git a/UI/Popup/FeatureInfoBox.ts b/UI/Popup/FeatureInfoBox.ts index 9b95512a9..3ec287856 100644 --- a/UI/Popup/FeatureInfoBox.ts +++ b/UI/Popup/FeatureInfoBox.ts @@ -58,7 +58,6 @@ export default class FeatureInfoBox extends ScrollableFullScreen { for (const groupName of allGroupNames) { const questions = layerConfig.tagRenderings.filter(tr => tr.group === groupName) const questionBox = new QuestionBox(tags, questions, layerConfig.units); - console.log("Groupname:", groupName) questionBoxes.set(groupName, questionBox) } } diff --git a/UI/ShowDataLayer/ShowDataLayer.ts b/UI/ShowDataLayer/ShowDataLayer.ts index 309903348..497c9282b 100644 --- a/UI/ShowDataLayer/ShowDataLayer.ts +++ b/UI/ShowDataLayer/ShowDataLayer.ts @@ -155,7 +155,6 @@ export default class ShowDataLayer { continue } try { - if ((feat.geometry.type === "LineString" || feat.geometry.type === "MultiLineString")) { const self = this; const coords = L.GeoJSON.coordsToLatLngs(feat.geometry.coordinates) @@ -190,9 +189,10 @@ export default class ShowDataLayer { if (options.zoomToFeatures ?? false) { try { - mp.fitBounds(this.geoLayer.getBounds(), {animate: false}) + const bounds = this.geoLayer.getBounds() + mp.fitBounds(bounds, {animate: false}) } catch (e) { - console.error(e) + console.debug("Invalid bounds",e) } } diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index fb9e4b6bc..674b44499 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -20,7 +20,7 @@ import Histogram from "./BigComponents/Histogram"; import Loc from "../Models/Loc"; import {Utils} from "../Utils"; import LayerConfig from "../Models/ThemeConfig/LayerConfig"; -import ImportButton, {ImportButtonSpecialViz} from "./BigComponents/ImportButton"; +import {ImportButtonSpecialViz} from "./BigComponents/ImportButton"; import {Tag} from "../Logic/Tags/Tag"; import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSource"; import ShowDataMultiLayer from "./ShowDataLayer/ShowDataMultiLayer"; @@ -38,9 +38,9 @@ import {SubtleButton} from "./Base/SubtleButton"; import ChangeTagAction from "../Logic/Osm/Actions/ChangeTagAction"; import {And} from "../Logic/Tags/And"; import Toggle from "./Input/Toggle"; -import {DefaultGuiState} from "./DefaultGUI"; import Img from "./Base/Img"; import FilteredLayer from "../Models/FilteredLayer"; +import {DefaultGuiState} from "./DefaultGuiState"; export interface SpecialVisualization { funcName: string, diff --git a/UI/SubstitutedTranslation.ts b/UI/SubstitutedTranslation.ts index cf6e4af76..076073f75 100644 --- a/UI/SubstitutedTranslation.ts +++ b/UI/SubstitutedTranslation.ts @@ -8,7 +8,7 @@ import {Utils} from "../Utils"; import {VariableUiElement} from "./Base/VariableUIElement"; import Combine from "./Base/Combine"; import BaseUIElement from "./BaseUIElement"; -import {DefaultGuiState} from "./DefaultGUI"; +import {DefaultGuiState} from "./DefaultGuiState"; export class SubstitutedTranslation extends VariableUiElement { diff --git a/assets/layers/conflation/conflation.json b/assets/layers/conflation/conflation.json new file mode 100644 index 000000000..2b80d1a36 --- /dev/null +++ b/assets/layers/conflation/conflation.json @@ -0,0 +1,32 @@ +{ + "id": "conflation", + "description": "This is a special meta_layer which render geometry-changes for inspection", + "minzoom": 1, + "source": { + "osmTags": { + "or": ["move=yes","newpoint=yes"] + } + }, + "name": "Conflation", + "title": "Conflation", + "mapRendering": [ + { + "location": "point", + "icon": "addSmall:#000", + "iconSize": "10,10,center" + }, + { + "location": "end", + "icon": "circle:#0f0", + "iconSize": "10,10,center" + },{ + "location": "start", + "icon": "square:#f00", + "iconSize": "10,10,center" + }, + { + "width": "3", + "color": "#00f" + } + ] +} \ No newline at end of file diff --git a/assets/layers/gps_location/gps_location.json b/assets/layers/gps_location/gps_location.json new file mode 100644 index 000000000..59c2c4ef0 --- /dev/null +++ b/assets/layers/gps_location/gps_location.json @@ -0,0 +1,15 @@ +{ + "id": "gps_location", + "description": "Meta layer showing the current location of the user", + "minzoom": 0, + "source": { + "osmTags": "user:location=yes" + }, + "mapRendering": [ + { + "icon": "crosshair:#00f", + "iconSize": "40,40,center", + "location": "point" + } + ] +} \ No newline at end of file diff --git a/assets/layers/home_location/home_location.json b/assets/layers/home_location/home_location.json index 0fb1af576..c2073d3b8 100644 --- a/assets/layers/home_location/home_location.json +++ b/assets/layers/home_location/home_location.json @@ -1,35 +1,19 @@ { - "id": "home_location", - "description": "Meta layer showing the home location of the user", - "minzoom": 0, - "source": { - "osmTags": "user:home=yes" - }, - "icon": { + "id": "home_location", + "description": "Meta layer showing the home location of the user", + "minzoom": 0, + "source": { + "osmTags": "user:home=yes" + }, + "mapRendering": [ + { + "icon": { "render": "circle:white;./assets/svg/home.svg" - }, - "iconSize": { + }, + "iconSize": { "render": "20,20,center" - }, - "color": { - "render": "#00f" - }, - "mapRendering": [ - { - "icon": { - "render": "circle:white;./assets/svg/home.svg" - }, - "iconSize": { - "render": "20,20,center" - }, - "location": [ - "point" - ] - }, - { - "color": { - "render": "#00f" - } - } - ] + }, + "location": "point" + } + ] } \ No newline at end of file diff --git a/assets/themes/cycle_highways/cycle_highways.json b/assets/themes/cycle_highways/cycle_highways.json index 884ddf2ad..0954bc838 100644 --- a/assets/themes/cycle_highways/cycle_highways.json +++ b/assets/themes/cycle_highways/cycle_highways.json @@ -235,11 +235,6 @@ } ], "mapRendering": [ - { - "location": [ - "point" - ] - }, { "color": { "render": "#ff7392", diff --git a/assets/themes/grb_import/grb.json b/assets/themes/grb_import/grb.json index a7ce909e6..4ffa3b8d9 100644 --- a/assets/themes/grb_import/grb.json +++ b/assets/themes/grb_import/grb.json @@ -38,7 +38,9 @@ "override": { "calculatedTags": [ "_is_part_of_building=feat.get('parent_ways')?.some(p => p.building !== undefined && p.building !== '') ?? false", - "_is_part_of_landuse=feat.get('parent_ways')?.some(p => (p.landuse !== undefined && p.landuse !== '') || (p.natural !== undefined && p.natural !== '')) ?? false" + "_is_part_of_building_passage=feat.get('parent_ways')?.some(p => p.tunnel === 'building_passage') ?? false", + "_is_part_of_highway=!feat.get('is_part_of_building_passage') && (feat.get('parent_ways')?.some(p => p.highway !== undefined && p.highway !== '') ?? false)", + "_is_part_of_landuse=feat.get('parent_ways')?.some(p => (p.landuse !== undefined && p.landuse !== '') || (p.natural !== undefined && p.natural !== '')) ?? false" ] } }, @@ -674,7 +676,7 @@ "mappings": [ { "if": "_overlaps_with!=null", - "then": "Cannot be imported directly, there is a nearly identical building geometry in OpenStreetMap" + "then": "{import_button(OSM-buildings,building=$building; source:geometry:date=$_grb_date; source:geometry:ref=$_grb_ref, Replace the geometry in OpenStreetMap,,,_osm_obj:id)}" } ] }, diff --git a/index.ts b/index.ts index 2ed85051e..dcf030c48 100644 --- a/index.ts +++ b/index.ts @@ -9,10 +9,11 @@ import {Utils} from "./Utils"; import AllThemesGui from "./UI/AllThemesGui"; import DetermineLayout from "./Logic/DetermineLayout"; import LayoutConfig from "./Models/ThemeConfig/LayoutConfig"; -import DefaultGUI, {DefaultGuiState} from "./UI/DefaultGUI"; +import DefaultGUI from "./UI/DefaultGUI"; import State from "./State"; import AvailableBaseLayersImplementation from "./Logic/Actors/AvailableBaseLayersImplementation"; import ShowOverlayLayerImplementation from "./UI/ShowDataLayer/ShowOverlayLayerImplementation"; +import {DefaultGuiState} from "./UI/DefaultGuiState"; // Workaround for a stupid crash: inject some functions which would give stupid circular dependencies or crash the other nodejs scripts running from console MinimapImplementation.initialize() diff --git a/test.ts b/test.ts index 653048710..7bc8c81d8 100644 --- a/test.ts +++ b/test.ts @@ -1,139 +1,26 @@ -import {Utils} from "./Utils"; -import FullNodeDatabaseSource from "./Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"; - - -const data = "\n" + - "\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " " - -const url = "https://www.openstreetmap.org/api/0.6/map?bbox=3.217620849609375,51.21548639922819,3.218994140625,51.21634661126673" -Utils.downloadJson(url).then(data =>{ - const osmSource = { - rawDataHandlers : [] - } - new FullNodeDatabaseSource(osmSource) - osmSource.rawDataHandlers[0]( data, 0) -}) \ No newline at end of file +import ShowDataLayer from "./UI/ShowDataLayer/ShowDataLayer"; +import AllKnownLayers from "./Customizations/AllKnownLayers"; +import Minimap from "./UI/Base/Minimap"; +import StaticFeatureSource from "./Logic/FeatureSource/Sources/StaticFeatureSource"; +import MinimapImplementation from "./UI/Base/MinimapImplementation"; +import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers"; +import BaseLayer from "./Models/BaseLayer"; +import {UIEventSource} from "./Logic/UIEventSource"; +import AvailableBaseLayersImplementation from "./Logic/Actors/AvailableBaseLayersImplementation"; +MinimapImplementation.initialize() +AvailableBaseLayers.implement(new AvailableBaseLayersImplementation()) +const confirmationMap = Minimap.createMiniMap({ + background: new UIEventSource(AvailableBaseLayers.osmCarto) +}) +const features = [{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":1728823483},"geometry":{"type":"LineString","coordinates":[[3.216693,51.2147409],[3.2166930000000225,51.214740500000055]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":1728823481},"geometry":{"type":"LineString","coordinates":[[3.2167247,51.2146969],[3.21671060000004,51.2147159000002]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":1728823481},"geometry":{"type":"LineString","coordinates":[[3.2167247,51.2146969],[3.2167241999999976,51.214696799999714]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":1728823549},"geometry":{"type":"LineString","coordinates":[[3.2168871,51.2147399],[3.2168876999999547,51.21474009999989]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":4978289383},"geometry":{"type":"LineString","coordinates":[[3.2169973,51.2147676],[3.2169969000000034,51.21476780000005]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":4978289388},"geometry":{"type":"LineString","coordinates":[[3.2169829,51.2147884],[3.2169673999999895,51.21481170000002]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":4978289388},"geometry":{"type":"LineString","coordinates":[[3.2169829,51.2147884],[3.216949899999979,51.214808000000225]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":4978289388},"geometry":{"type":"LineString","coordinates":[[3.2169829,51.2147884],[3.2169306,51.21480400000028]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":4978289388},"geometry":{"type":"LineString","coordinates":[[3.2169829,51.2147884],[3.2169465999999756,51.214779199999825]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":4978288381},"geometry":{"type":"LineString","coordinates":[[3.2168856,51.2147638],[3.216885599999961,51.214763799999986]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":4978289386},"geometry":{"type":"LineString","coordinates":[[3.2168815,51.2147718],[3.216881100000038,51.21477160000009]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":4978289384},"geometry":{"type":"LineString","coordinates":[[3.2168674,51.2147683],[3.216867399999983,51.214768400000224]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":1728823514},"geometry":{"type":"LineString","coordinates":[[3.2168551,51.2147863],[3.2168551000000436,51.21478629999984]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":1728823483},"geometry":{"type":"LineString","coordinates":[[3.216693,51.2147409],[3.2166930000000225,51.214740500000055]]}},"freshness":"2021-11-02T20:06:53.088Z"}] +const changePreview = new StaticFeatureSource(features.map(f => f.feature), false) +console.log("ChangePreview", changePreview.features.data) +new ShowDataLayer({ + leafletMap: confirmationMap.leafletMap, + enablePopups: false, + zoomToFeatures: true, + features: changePreview, + layerToShow: AllKnownLayers.sharedLayers.get("conflation") +}) + +confirmationMap.SetStyle("height: 20rem").SetClass("w-full").AttachTo("maindiv") \ No newline at end of file diff --git a/test/ReplaceGeometry.spec.ts b/test/ReplaceGeometry.spec.ts new file mode 100644 index 000000000..40c3e53bb --- /dev/null +++ b/test/ReplaceGeometry.spec.ts @@ -0,0 +1,185 @@ +import T from "./TestHelper"; +import FullNodeDatabaseSource from "../Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"; +import {Utils} from "../Utils"; + +export default class ReplaceGeometrySpec extends T { + constructor() { + super("ReplaceGeometry", [ + ["Simple house replacement", async () => { + const coordinates = <[number, number][]>[[ + 3.216690793633461, + 51.21474084112525 + ], + [ + 3.2167256623506546, + 51.214696737309964 + ], + [ + 3.2169999182224274, + 51.214768983537674 + ], + [ + 3.2169650495052338, + 51.21480720678671 + ], + [ + 3.2169368863105774, + 51.21480090625335 + ], + [ + 3.2169489562511444, + 51.21478074454077 + ], + [ + 3.216886594891548, + 51.214765203214625 + ], + [ + 3.2168812304735184, + 51.21477192378873 + ], + [ + 3.2168644666671753, + 51.214768983537674 + ], + [ + 3.2168537378311157, + 51.21478746511261 + ], + [ + 3.216690793633461, + 51.21474084112525 + ] + ] + + Utils.injectJsonDownloadForTests( + "https://www.openstreetmap.org/api/0.6/way/160909312/full", + { + "version": "0.6", + "generator": "CGImap 0.8.5 (920083 spike-06.openstreetmap.org)", + "copyright": "OpenStreetMap and contributors", + "attribution": "http://www.openstreetmap.org/copyright", + "license": "http://opendatacommons.org/licenses/odbl/1-0/", + "elements": [{ + "type": "node", + "id": 1728823481, + "lat": 51.2146969, + "lon": 3.2167247, + "timestamp": "2017-07-18T22:52:45Z", + "version": 2, + "changeset": 50391526, + "user": "catweazle67", + "uid": 1976209 + }, { + "type": "node", + "id": 1728823483, + "lat": 51.2147409, + "lon": 3.216693, + "timestamp": "2017-07-18T22:52:45Z", + "version": 2, + "changeset": 50391526, + "user": "catweazle67", + "uid": 1976209 + }, { + "type": "node", + "id": 1728823514, + "lat": 51.2147863, + "lon": 3.2168551, + "timestamp": "2017-07-18T22:52:45Z", + "version": 2, + "changeset": 50391526, + "user": "catweazle67", + "uid": 1976209 + }, { + "type": "node", + "id": 1728823549, + "lat": 51.2147399, + "lon": 3.2168871, + "timestamp": "2017-07-18T22:52:46Z", + "version": 2, + "changeset": 50391526, + "user": "catweazle67", + "uid": 1976209 + }, { + "type": "node", + "id": 4978288381, + "lat": 51.2147638, + "lon": 3.2168856, + "timestamp": "2017-07-18T22:52:21Z", + "version": 1, + "changeset": 50391526, + "user": "catweazle67", + "uid": 1976209 + }, { + "type": "node", + "id": 4978289383, + "lat": 51.2147676, + "lon": 3.2169973, + "timestamp": "2017-07-18T22:52:21Z", + "version": 1, + "changeset": 50391526, + "user": "catweazle67", + "uid": 1976209 + }, { + "type": "node", + "id": 4978289384, + "lat": 51.2147683, + "lon": 3.2168674, + "timestamp": "2017-07-18T22:52:21Z", + "version": 1, + "changeset": 50391526, + "user": "catweazle67", + "uid": 1976209 + }, { + "type": "node", + "id": 4978289386, + "lat": 51.2147718, + "lon": 3.2168815, + "timestamp": "2017-07-18T22:52:21Z", + "version": 1, + "changeset": 50391526, + "user": "catweazle67", + "uid": 1976209 + }, { + "type": "node", + "id": 4978289388, + "lat": 51.2147884, + "lon": 3.2169829, + "timestamp": "2017-07-18T22:52:21Z", + "version": 1, + "changeset": 50391526, + "user": "catweazle67", + "uid": 1976209 + }, { + "type": "way", + "id": 160909312, + "timestamp": "2017-07-18T22:52:30Z", + "version": 2, + "changeset": 50391526, + "user": "catweazle67", + "uid": 1976209, + "nodes": [1728823483, 1728823514, 4978289384, 4978289386, 4978288381, 4978289388, 4978289383, 1728823549, 1728823481, 1728823483], + "tags": { + "addr:city": "Brugge", + "addr:country": "BE", + "addr:housenumber": "108", + "addr:postcode": "8000", + "addr:street": "Ezelstraat", + "building": "yes" + } + }] + } + ) + + + const wayId = "way/160909312" + const url = `https://www.openstreetmap.org/api/0.6/${wayId}/full`; + const rawData = await Utils.downloadJsonCached(url, 1000) + + + + + }] + ]); + } +} \ No newline at end of file diff --git a/test/TestAll.ts b/test/TestAll.ts index 9b23957d1..c6f64d62e 100644 --- a/test/TestAll.ts +++ b/test/TestAll.ts @@ -13,6 +13,7 @@ import TileFreshnessCalculatorSpec from "./TileFreshnessCalculator.spec"; import WikidataSpecTest from "./Wikidata.spec.test"; import ImageProviderSpec from "./ImageProvider.spec"; import ActorsSpec from "./Actors.spec"; +import ReplaceGeometrySpec from "./ReplaceGeometry.spec"; ScriptUtils.fixUtils() @@ -29,7 +30,8 @@ const allTests = [ new TileFreshnessCalculatorSpec(), new WikidataSpecTest(), new ImageProviderSpec(), - new ActorsSpec() + new ActorsSpec(), + new ReplaceGeometrySpec() ] Utils.externalDownloadFunction = async (url) => { From 6e0f57f368ef82ad36b588ed17f6748d184ade1d Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 3 Nov 2021 00:51:05 +0100 Subject: [PATCH 74/95] Generate translations --- assets/layers/conflation/conflation.json | 62 +- assets/layers/gps_location/gps_location.json | 26 +- .../layers/home_location/home_location.json | 34 +- assets/layers/type_node/type_node.json | 20 +- assets/themes/grb_import/grb.json | 4 +- langs/shared-questions/en.json | 4 +- langs/shared-questions/it.json | 81 +- langs/shared-questions/nb_NO.json | 79 +- langs/shared-questions/pt.json | 2 +- langs/themes/de.json | 58 +- langs/themes/en.json | 76 +- langs/themes/it.json | 853 +----------------- langs/themes/nb_NO.json | 198 +--- langs/themes/nl.json | 4 +- 14 files changed, 159 insertions(+), 1342 deletions(-) diff --git a/assets/layers/conflation/conflation.json b/assets/layers/conflation/conflation.json index 2b80d1a36..2f638cfa5 100644 --- a/assets/layers/conflation/conflation.json +++ b/assets/layers/conflation/conflation.json @@ -1,32 +1,36 @@ { - "id": "conflation", - "description": "This is a special meta_layer which render geometry-changes for inspection", - "minzoom": 1, - "source": { - "osmTags": { - "or": ["move=yes","newpoint=yes"] - } - }, - "name": "Conflation", - "title": "Conflation", - "mapRendering": [ - { - "location": "point", - "icon": "addSmall:#000", - "iconSize": "10,10,center" + "id": "conflation", + "description": "This is a special meta_layer which render geometry-changes for inspection", + "minzoom": 1, + "source": { + "osmTags": { + "or": [ + "move=yes", + "newpoint=yes" + ] + } }, - { - "location": "end", - "icon": "circle:#0f0", - "iconSize": "10,10,center" - },{ - "location": "start", - "icon": "square:#f00", - "iconSize": "10,10,center" - }, - { - "width": "3", - "color": "#00f" - } - ] + "name": "Conflation", + "title": "Conflation", + "mapRendering": [ + { + "location": "point", + "icon": "addSmall:#000", + "iconSize": "10,10,center" + }, + { + "location": "end", + "icon": "circle:#0f0", + "iconSize": "10,10,center" + }, + { + "location": "start", + "icon": "square:#f00", + "iconSize": "10,10,center" + }, + { + "width": "3", + "color": "#00f" + } + ] } \ No newline at end of file diff --git a/assets/layers/gps_location/gps_location.json b/assets/layers/gps_location/gps_location.json index 59c2c4ef0..617349405 100644 --- a/assets/layers/gps_location/gps_location.json +++ b/assets/layers/gps_location/gps_location.json @@ -1,15 +1,15 @@ { - "id": "gps_location", - "description": "Meta layer showing the current location of the user", - "minzoom": 0, - "source": { - "osmTags": "user:location=yes" - }, - "mapRendering": [ - { - "icon": "crosshair:#00f", - "iconSize": "40,40,center", - "location": "point" - } - ] + "id": "gps_location", + "description": "Meta layer showing the current location of the user", + "minzoom": 0, + "source": { + "osmTags": "user:location=yes" + }, + "mapRendering": [ + { + "icon": "crosshair:#00f", + "iconSize": "40,40,center", + "location": "point" + } + ] } \ No newline at end of file diff --git a/assets/layers/home_location/home_location.json b/assets/layers/home_location/home_location.json index c2073d3b8..0955af79b 100644 --- a/assets/layers/home_location/home_location.json +++ b/assets/layers/home_location/home_location.json @@ -1,19 +1,19 @@ { - "id": "home_location", - "description": "Meta layer showing the home location of the user", - "minzoom": 0, - "source": { - "osmTags": "user:home=yes" - }, - "mapRendering": [ - { - "icon": { - "render": "circle:white;./assets/svg/home.svg" - }, - "iconSize": { - "render": "20,20,center" - }, - "location": "point" - } - ] + "id": "home_location", + "description": "Meta layer showing the home location of the user", + "minzoom": 0, + "source": { + "osmTags": "user:home=yes" + }, + "mapRendering": [ + { + "icon": { + "render": "circle:white;./assets/svg/home.svg" + }, + "iconSize": { + "render": "20,20,center" + }, + "location": "point" + } + ] } \ No newline at end of file diff --git a/assets/layers/type_node/type_node.json b/assets/layers/type_node/type_node.json index 5ead02f1c..4a61d8232 100644 --- a/assets/layers/type_node/type_node.json +++ b/assets/layers/type_node/type_node.json @@ -1,12 +1,12 @@ { - "id": "type_node", - "description": "This is a special meta_layer which exports _every_ point in OSM. This only works if zoomed below the point that the full tile is loaded (and not loaded via Overpass). Note that this point will also contain a property `parent_ways` which contains all the ways this node is part of as a list", - "minzoom": 18, - "source": { - "osmTags": "id~node/.*" - }, - "mapRendering": [], - "name": "All OSM Nodes", - "title": "OSM node {id}", - "tagRendering": [ ] + "id": "type_node", + "description": "This is a special meta_layer which exports _every_ point in OSM. This only works if zoomed below the point that the full tile is loaded (and not loaded via Overpass). Note that this point will also contain a property `parent_ways` which contains all the ways this node is part of as a list", + "minzoom": 18, + "source": { + "osmTags": "id~node/.*" + }, + "mapRendering": [], + "name": "All OSM Nodes", + "title": "OSM node {id}", + "tagRendering": [] } \ No newline at end of file diff --git a/assets/themes/grb_import/grb.json b/assets/themes/grb_import/grb.json index 4ffa3b8d9..5a3d5d545 100644 --- a/assets/themes/grb_import/grb.json +++ b/assets/themes/grb_import/grb.json @@ -31,7 +31,7 @@ "trackAllNodes": true, "layers": [ { - "builtin": "type_node", + "builtin": "type_node", "isShown": { "render": "no" }, @@ -40,7 +40,7 @@ "_is_part_of_building=feat.get('parent_ways')?.some(p => p.building !== undefined && p.building !== '') ?? false", "_is_part_of_building_passage=feat.get('parent_ways')?.some(p => p.tunnel === 'building_passage') ?? false", "_is_part_of_highway=!feat.get('is_part_of_building_passage') && (feat.get('parent_ways')?.some(p => p.highway !== undefined && p.highway !== '') ?? false)", - "_is_part_of_landuse=feat.get('parent_ways')?.some(p => (p.landuse !== undefined && p.landuse !== '') || (p.natural !== undefined && p.natural !== '')) ?? false" + "_is_part_of_landuse=feat.get('parent_ways')?.some(p => (p.landuse !== undefined && p.landuse !== '') || (p.natural !== undefined && p.natural !== '')) ?? false" ] } }, diff --git a/langs/shared-questions/en.json b/langs/shared-questions/en.json index 87af0877e..0de90284a 100644 --- a/langs/shared-questions/en.json +++ b/langs/shared-questions/en.json @@ -65,7 +65,7 @@ "wheelchair-access": { "mappings": { "0": { - "then": "This place is specially adapted for wheelchair users" + "then": "This place is specially adapated for wheelchair users" }, "1": { "then": "This place is easily reachable with a wheelchair" @@ -96,4 +96,4 @@ "question": "What is the corresponding item on Wikipedia?" } } -} +} \ No newline at end of file diff --git a/langs/shared-questions/it.json b/langs/shared-questions/it.json index 02999c29e..f287d00f2 100644 --- a/langs/shared-questions/it.json +++ b/langs/shared-questions/it.json @@ -15,85 +15,6 @@ }, "website": { "question": "Qual è il sito web di {name}?" - }, - "level": { - "render": "Si trova al piano numero {level}", - "mappings": { - "1": { - "then": "Si trova al pianoterra" - }, - "2": { - "then": "Si trova al pianoterra" - }, - "3": { - "then": "Si trova al primo piano" - }, - "0": { - "then": "Si trova sotto il livello stradale" - } - }, - "question": "A quale piano si trova questo elemento?" - }, - "payment-options": { - "mappings": { - "0": { - "then": "I contanti sono accettati" - }, - "1": { - "then": "I pagamenti con la carta sono accettati" - } - }, - "question": "Quali metodi di pagamento sono accettati qui?" - }, - "wheelchair-access": { - "mappings": { - "1": { - "then": "Questo luogo è facilmente raggiungibile con una sedia a rotelle" - }, - "2": { - "then": "È possibile raggiungere questo luogo con una sedia a rotella ma non è semplice" - }, - "3": { - "then": "Questo luogo non è accessibile con una sedia a rotelle" - }, - "0": { - "then": "Questo luogo è stato adattato per favorire le persone in sedia a rotelle" - } - }, - "question": "Questo luogo è accessibile con una sedia a rotelle?" - }, - "dog-access": { - "mappings": { - "0": { - "then": "Cani ammessi" - }, - "2": { - "then": "Cani ammessi ma solo se tenuti al guinzaglio" - }, - "1": { - "then": "I cani non sono ammessi" - }, - "3": { - "then": "I cani sono ammessi e possono andare in giro liberamente" - } - }, - "question": "I cani sono ammessi in quest’attività?" - }, - "wikipedialink": { - "question": "Qual è il corrispondente elemento su Wikipedia?", - "mappings": { - "0": { - "then": "Non collegato a Wikipedia" - } - } - }, - "wikipedia": { - "mappings": { - "0": { - "then": "Nessuna pagina Wikipedia è ancora stata collegata" - } - }, - "question": "Qual è l’elemento Wikidata corrispondente?" } } -} +} \ No newline at end of file diff --git a/langs/shared-questions/nb_NO.json b/langs/shared-questions/nb_NO.json index 98ffbd0bf..a8241aecf 100644 --- a/langs/shared-questions/nb_NO.json +++ b/langs/shared-questions/nb_NO.json @@ -15,83 +15,6 @@ }, "website": { "question": "Hva er nettsiden til {name}?" - }, - "wikipedialink": { - "mappings": { - "0": { - "then": "Ikke lenket med Wikipedia" - } - }, - "question": "Hva er respektivt element på Wikipedia?" - }, - "wheelchair-access": { - "question": "Er dette stedet tilgjengelig for rullestoler?", - "mappings": { - "3": { - "then": "Dette stedet er ikke tilgjengelig for besøk med rullestol" - }, - "2": { - "then": "Det er mulig å besøke dette stedet i rullestol, men det er ikke lett" - }, - "1": { - "then": "Dette stedet kan enkelt besøkes med rullestol" - }, - "0": { - "then": "Dette stedet er spesielt tilpasset rullestolsbrukere" - } - } - }, - "dog-access": { - "mappings": { - "0": { - "then": "Hunder tillates" - }, - "3": { - "then": "Hunder tillates og kan gå fritt" - }, - "1": { - "then": "Hunder tillates ikke" - }, - "2": { - "then": "Hunder tillates, men de må være i bånd" - } - }, - "question": "Tillates hunder i denne forretningen?" - }, - "level": { - "mappings": { - "2": { - "then": "På gateplan" - }, - "3": { - "then": "I andre etasje" - }, - "1": { - "then": "På gateplan" - }, - "0": { - "then": "Under bakken" - } - } - }, - "payment-options": { - "mappings": { - "0": { - "then": "Kontanter godtas her" - }, - "1": { - "then": "Betalingskort godtas her" - } - }, - "question": "Hvilke betalingsmetoder godtas her?" - }, - "wikipedia": { - "mappings": { - "0": { - "then": "Ingen Wikipedia-side lenket enda" - } - }, - "question": "Hva er respektivt Wikipedia-element?" } } -} +} \ No newline at end of file diff --git a/langs/shared-questions/pt.json b/langs/shared-questions/pt.json index b72e9fa62..b1f25a435 100644 --- a/langs/shared-questions/pt.json +++ b/langs/shared-questions/pt.json @@ -96,4 +96,4 @@ "question": "Qual é o item correspondente na Wikipédia?" } } -} +} \ No newline at end of file diff --git a/langs/themes/de.json b/langs/themes/de.json index 03fe9ed9b..237a48a78 100644 --- a/langs/themes/de.json +++ b/langs/themes/de.json @@ -733,8 +733,7 @@ } }, "shortDescription": "Diese Karte zeigt Fassadenbegrünungen mit Bildern und nützlichen Informationen über Ausrichtung, Sonneneinstrahlung und Pflanzenarten.", - "title": "Fassadenbegrünung", - "description": "Fassadengärten, grüne Fassaden und Bäume in der Stadt bringen nicht nur Ruhe und Frieden, sondern auch eine schönere Stadt, eine größere Artenvielfalt, einen Kühleffekt und eine bessere Luftqualität.
Klimaan VZW und Mechelen Klimaatneutraal wollen bestehende und neue Fassadengärten als Beispiel für Menschen, die ihren eigenen Garten anlegen wollen, oder für naturverbundene Stadtspaziergänger kartieren.
Mehr Informationen über das Projekt unter klimaan.be." + "title": "Fassadenbegrünung" }, "food": { "title": "Restaurants und Schnellimbisse" @@ -764,6 +763,17 @@ } } }, + "mapRendering": { + "0": { + "icon": { + "mappings": { + "0": { + "then": "./assets/themes/hackerspaces/led.png" + } + } + } + } + }, "name": "Hackerspace", "presets": { "0": { @@ -823,17 +833,6 @@ } }, "render": "Hackerspace" - }, - "mapRendering": { - "0": { - "icon": { - "mappings": { - "0": { - "then": "./assets/themes/hackerspaces/led.png" - } - } - } - } } } }, @@ -948,41 +947,24 @@ "tagRenderings": { "station-name": { "question": "Wie lautet der Name dieser Feuerwache?" - }, - "station-agency": { - "mappings": { - "0": { - "then": "Brandschutzbehörde" - } - } } } }, "3": { "presets": { "0": { - "description": "Eine Rettungsstation der Karte hinzufügen", - "title": "Rettungswache" + "description": "Eine Rettungsstation der Karte hinzufügen" } - }, - "title": { - "render": "Rettungswache" - }, - "name": "Karte der Rettungswachen", - "description": "Eine Rettungswache ist ein Ort, an dem Rettungsfahrzeuge, medizinische Ausrüstung, persönliche Schutzausrüstung und anderes medizinisches Material untergebracht sind." + } } - }, - "shortDescription": "Hydranten, Feuerlöscher, Feuerwachen und Rettungswachen." + } }, "maps": { - "title": "Eine Karte der Karten", - "shortDescription": "Dieses Thema zeigt alle (touristischen) Karten, die OpenStreetMap kennt", - "description": "Auf dieser Karte findest du alle Karten, die OpenStreetMap kennt - typischerweise eine große Karte auf einer Informationstafel, die das Gebiet, die Stadt oder die Region zeigt, z.B. eine touristische Karte auf der Rückseite einer Plakatwand, eine Karte eines Naturschutzgebietes, eine Karte der Radwegenetze in der Region, ...)

Wenn eine Karte fehlt, können Sie diese leicht auf OpenStreetMap kartieren." + "title": "Eine Karte der Karten" }, "natuurpunt": { "shortDescription": "Diese Karte zeigt Naturschutzgebiete des flämischen Naturverbands Natuurpunt", - "title": "Naturschutzgebiete", - "description": "Auf dieser Karte können Sie alle Naturschutzgebiete von Natuurpunt finden " + "title": "Naturschutzgebiete" }, "observation_towers": { "description": "Öffentlich zugänglicher Aussichtsturm", @@ -1004,8 +986,7 @@ }, "playgrounds": { "shortDescription": "Eine Karte mit Spielplätzen", - "title": "Spielpläzte", - "description": "Auf dieser Karte finden Sie Spielplätze und können weitere Informationen hinzufügen" + "title": "Spielpläzte" }, "postboxes": { "layers": { @@ -1027,8 +1008,7 @@ }, "shops": { "shortDescription": "Eine bearbeitbare Karte mit grundlegenden Geschäftsinformationen", - "title": "Freie Geschäftskarte", - "description": "Auf dieser Karte kann man grundlegende Informationen über Geschäfte markieren, Öffnungszeiten und Telefonnummern hinzufügen" + "title": "Freie Geschäftskarte" }, "sport_pitches": { "description": "Ein Sportplatz ist eine Fläche, auf der Sportarten gespielt werden", diff --git a/langs/themes/en.json b/langs/themes/en.json index 0c107d672..7d5a45d34 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -610,7 +610,7 @@ "title": "Bicycle infrastructure" }, "cyclestreets": { - "description": "A cyclestreet is a street where motorized traffic is not allowed to overtake cyclists. They are signposted by a special traffic sign. Cyclestreets can be found in the Netherlands and Belgium, but also in Germany and France. ", + "description": "A cyclestreet is is a street where motorized traffic is not allowed to overtake cyclists. They are signposted by a special traffic sign. Cyclestreets can be found in the Netherlands and Belgium, but also in Germany and France. ", "layers": { "0": { "description": "A cyclestreet is a street where motorized traffic is not allowed to overtake a cyclist", @@ -666,14 +666,14 @@ }, "cyclofix": { "description": "The goal of this map is to present cyclists with an easy-to-use solution to find the appropriate infrastructure for their needs.

You can track your precise location (mobile only) and select layers that are relevant for you in the bottom left corner. You can also use this tool to add or edit pins (points of interest) to the map and provide more data by answering the questions.

All changes you make will automatically be saved in the global database of OpenStreetMap and can be freely re-used by others.

For more information about the cyclofix project, go to cyclofix.osm.be.", - "title": "Cyclofix — an open map for cyclists" + "title": "Cyclofix - an open map for cyclists" }, "drinking_water": { "description": "On this map, publicly accessible drinking water spots are shown and can be easily added", "title": "Drinking Water" }, "etymology": { - "description": "On this map, you can see what an object is named after. The streets, buildings, ... come from OpenStreetMap which got linked with Wikidata. In the popup, you'll see the Wikipedia article (if it exists) or a Wikidata box of what the object is named after. If the object itself has a Wikipedia page, that'll be shown too.

You can help contribute too!Zoom in enough and all streets will show up. You can click one and a Wikidata-search box will popup. With a few clicks, you can add an etymology link. Note that you need a free OpenStreetMap account to do this.", + "description": "On this map, you can see what an object is named after. The streets, buildings, ... come from OpenStreetMap which got linked with Wikidata. In the popup, you'll see the Wikipedia article (if it exists) or a wikidata box of what the object is named after. If the object itself has a wikipedia page, that'll be shown too.

You can help contribute too!Zoom in enough and all streets will show up. You can click one and a Wikidata-search box will popup. With a few clicks, you can add an etymology link. Note that you need a free OpenStreetMap account to do this.", "layers": { "1": { "override": { @@ -708,7 +708,7 @@ }, "facadegardens-direction": { "question": "What is the orientation of the garden?", - "render": "Orientation: {direction} (where 0=N and 90=E)" + "render": "Orientation: {direction} (where 0=N and 90=O)" }, "facadegardens-edible": { "mappings": { @@ -792,6 +792,9 @@ "description": "A ghost bike is a memorial for a cyclist who died in a traffic accident, in the form of a white bicycle placed permanently near the accident location.

On this map, one can see all the ghost bikes which are known by OpenStreetMap. Is a ghost bike missing? Everyone can add or update information here - you only need to have a (free) OpenStreetMap account.", "title": "Ghost bikes" }, + "grb": { + "description": "This theme is an attempt to help automating the GRB import.
Note that this is very hacky and 'steals' the GRB data from an external site; in order to do this, you need to install and activate this firefox extension for it to work." + }, "hackerspaces": { "description": "On this map you can see hackerspaces, add a new hackerspace or update data directly", "layers": { @@ -804,6 +807,17 @@ } } }, + "mapRendering": { + "0": { + "icon": { + "mappings": { + "0": { + "then": "./assets/themes/hackerspaces/led.png" + } + } + } + } + }, "name": "Hackerspace", "presets": { "0": { @@ -863,17 +877,6 @@ } }, "render": "Hackerspace" - }, - "mapRendering": { - "0": { - "icon": { - "mappings": { - "0": { - "then": "./assets/themes/hackerspaces/led.png" - } - } - } - } } } }, @@ -881,7 +884,7 @@ "title": "Hackerspaces" }, "hailhydrant": { - "description": "On this map you can find and update hydrants, fire stations, ambulance stations, and extinguishers in your favorite neighborhoods.\n\nYou can track your precise location (mobile only) and select layers that are relevant for you in the bottom left corner. You can also use this tool to add or edit pins (points of interest) to the map and provide additional details by answering available questions.\n\nAll changes you make will automatically be saved in the global database of OpenStreetMap and can be freely re-used by others.", + "description": "On this map you can find and update hydrants, fire stations, ambulance stations, and extinguishers in your favorite neighborhoods. \n\nYou can track your precise location (mobile only) and select layers that are relevant for you in the bottom left corner. You can also use this tool to add or edit pins (points of interest) to the map and provide additional details by answering available questions. \n\nAll changes you make will automatically be saved in the global database of OpenStreetMap and can be freely re-used by others.", "layers": { "0": { "description": "Map layer to show fire hydrants.", @@ -1237,6 +1240,25 @@ "shortDescription": "An editable map with basic shop information", "title": "Open Shop Map" }, + "sidewalks": { + "description": "Experimental theme", + "layers": { + "0": { + "description": "Layer showing sidewalks of highways", + "name": "Sidewalks", + "tagRenderings": { + "streetname": { + "render": "This street is named {name}" + } + }, + "title": { + "render": "{name}" + } + } + }, + "shortDescription": "Sidewalk mapping", + "title": "Sidewalks" + }, "sport_pitches": { "description": "A sport pitch is an area where sports are played", "shortDescription": "A map showing sport pitches", @@ -1297,27 +1319,5 @@ "description": "On this map, you'll find waste baskets near you. If a waste basket is missing on this map, you can add it yourself", "shortDescription": "A map with waste baskets", "title": "Waste Basket" - }, - "sidewalks": { - "description": "Experimental theme", - "layers": { - "0": { - "description": "Layer showing sidewalks of highways", - "name": "Sidewalks", - "tagRenderings": { - "streetname": { - "render": "This street is named {name}" - } - }, - "title": { - "render": "{name}" - } - } - }, - "shortDescription": "Sidewalk mapping", - "title": "Sidewalks" - }, - "grb": { - "description": "This theme is an attempt to help automating the GRB import.
Note that this is very hacky and 'steals' the GRB data from an external site; in order to do this, you need to install and activate this firefox extension for it to work." } } \ No newline at end of file diff --git a/langs/themes/it.json b/langs/themes/it.json index d71f656d4..2b71cfcff 100644 --- a/langs/themes/it.json +++ b/langs/themes/it.json @@ -272,21 +272,6 @@ }, "question": "Come si chiama questa via di arrampicata?", "render": "{name}" - }, - "Bolts": { - "render": "Questo percorso ha {climbing:bolts} bulloni", - "mappings": { - "1": { - "then": "In questo percorso non sono presenti bulloni" - }, - "0": { - "then": "In questo percorso non sono presenti bulloni" - } - }, - "question": "Quanti bulloni sono presenti in questo percorso prima di arrivare alla moulinette?" - }, - "Rock type": { - "render": "Il tipo di roccia è {_embedding_features_with_rock:rock} come dichiarato sul muro circostante" } }, "title": { @@ -296,293 +281,10 @@ } }, "render": "Via di arrampicata" - }, - "name": "Vie di arrampicata", - "presets": { - "0": { - "title": "Via di arrampicata" - } - } - }, - "0": { - "description": "Un club o associazione di arrampacata", - "name": "Club di arrampicata", - "tagRenderings": { - "climbing_club-name": { - "render": "{name}", - "question": "Qual è il nome di questo club o associazione di arrampicata?" - } - }, - "title": { - "mappings": { - "0": { - "then": "Associazione di arrampicata" - } - }, - "render": "Club di arrampicata" - }, - "presets": { - "1": { - "title": "Associazione di arrampicata", - "description": "Un’associazione che ha a che fare con l’arrampicata" - }, - "0": { - "title": "Club di arrampicata", - "description": "Un club di arrampicata" - } - } - }, - "4": { - "title": { - "render": "Opportunità di arrampicata?" - }, - "name": "Opportunità di arrampicata?", - "tagRenderings": { - "climbing-possible": { - "mappings": { - "0": { - "then": "Non è possibile arrampicarsi qua" - }, - "2": { - "then": "Non è possibile arrampicarsi qua" - }, - "1": { - "then": "È possibile arrampicarsi qua" - } - }, - "question": "È possibile arrampicarsi qua?" - }, - "climbing-opportunity-name": { - "render": "{name}" - } - }, - "description": "Un’opportunità di arrampicata?" - }, - "3": { - "title": { - "mappings": { - "2": { - "then": "Sito di arrampicata" - }, - "3": { - "then": "Opportunità di arrampicata {name}" - }, - "0": { - "then": "Muro da arrampicata {name}" - }, - "1": { - "then": "Area di arrampicata {name}" - } - }, - "render": "Opportunità di arrampicata" - }, - "tagRenderings": { - "Rock type (crag/rock/cliff only)": { - "render": "Il tipo di roccia è {rock}", - "question": "Qual è il tipo di roccia qua?", - "mappings": { - "0": { - "then": "Calcare" - } - } - }, - "Type": { - "mappings": { - "1": { - "then": "Un muro da arrampicata (un singolo masso o falesia con almeno qualche via per arrampicata)" - }, - "0": { - "then": "Un masso per arrampicata (una singola roccia o falesia con una o poche vie di arrampicata che possono essere scalate in sicurezza senza una corda)" - } - } - }, - "Containe {_contained_climbing_routes_count} routes": { - "render": "

Contiene {_contained_climbing_routes_count} vie

    {_contained_climbing_routes}
" - }, - "name": { - "question": "Qual è il nome di questa opportunità di arrampicata?", - "mappings": { - "0": { - "then": "Questa opportunità di arrampicata non ha un nome" - } - }, - "render": "{name}" - }, - "Contained routes length hist": { - "render": "

Riassunto della lunghezza

{histogram(_length_hist)}" - }, - "Contained routes hist": { - "render": "

Riassunto delle difficoltà

{histogram(_difficulty_hist)}" - } - }, - "description": "Un’opportunità di arrampicata", - "presets": { - "0": { - "description": "Un’opportunità di arrampicata", - "title": "Opportunità di arrampicata" - } - }, - "name": "Opportunità di arrampicata" - }, - "1": { - "description": "Una palestra di arrampicata", - "name": "Palestre di arrampicata", - "title": { - "render": "Palestra di arrampicata", - "mappings": { - "0": { - "then": "Palestra di arrampicata {name}" - } - } - }, - "tagRenderings": { - "name": { - "render": "{name}", - "question": "Qual è il nome di questa palestra di arrampicata?" - } } } }, - "title": "Mappa aperta per le arrampicate", - "description": "In questa cartina puoi trovare vari luoghi per arrampicata come ad esempio palestre di arrampicata, sale di pratica e rocce naturali.", - "overrideAll": { - "tagRenderings+": { - "2": { - "mappings": { - "2": { - "then": "Riservato ai clienti" - }, - "3": { - "then": "Riservato ai membri del club" - }, - "0": { - "then": "Pubblicamente accessibile a chiunque" - }, - "1": { - "then": "È necessario avere un’autorizzazione per entrare" - } - }, - "question": "Chi può accedervi?" - }, - "9": { - "question": "È possibile arrampicarsi qua con ancoraggi fissi?", - "mappings": { - "1": { - "then": "L’arrampicata sportiva non è possibile qua" - }, - "0": { - "then": "L’arrampicata sportiva è possibile qua" - }, - "2": { - "then": "Sono presenti {climbing:sport} vie di arrampicata sportiva" - } - } - }, - "10": { - "mappings": { - "0": { - "then": "L’arrampicata tradizionale è possibile qua" - }, - "1": { - "then": "L’arrampicata tradizionale non è possibile qua" - }, - "2": { - "then": "Sono presenti {climbing:traditional} vie di arrampicata tradizionale" - } - }, - "question": "È possibile arrampicarsi in maniera tradizionale qua (usando attrezzi propri, ad es. dadi)?" - }, - "11": { - "mappings": { - "0": { - "then": "È presente una parete per l’arrampicata di velocità" - }, - "1": { - "then": "Non è presente una parete per l’arrampicata di velocità" - }, - "2": { - "then": "Sono presenti {climbing:speed} pareti per l’arrampicata di velocità" - } - }, - "question": "È presente una prete per l’arrampicata di velocità?" - }, - "7": { - "mappings": { - "1": { - "then": "L’arrampicata su massi non è possibile qua" - }, - "0": { - "then": "L’arrampicata su massi è possibile qua" - }, - "2": { - "then": "L’arrampicata su massi è possibile anche se su poche vie" - }, - "3": { - "then": "Sono presenti {climbing:boulder} vie di arrampicata su massi" - } - }, - "question": "È possibile praticare ‘bouldering’ qua?" - }, - "8": { - "mappings": { - "0": { - "then": "È possibile arrampicarsi con moulinette qua" - }, - "1": { - "then": "Non è possibile arrampicarsi con moulinette qua" - }, - "2": { - "then": "Sono presenti {climbing:toprope} vie con moulinette" - } - }, - "question": "È possibile arrampicarsi con la corda dall’alto qua?" - }, - "4": { - "render": "Le vie sono lunghe mediamente {canonical(climbing:length)}", - "question": "Quale è la lunghezza (media) delle vie in metri?" - }, - "6": { - "question": "Qual è il livello della via più difficile qua, secondo il sistema di classificazione francese?", - "render": "Il massimo livello di difficoltà è {climbing:grade:french:max} secondo il sistema francese/belga" - }, - "5": { - "question": "Qual è il livello della via più facile qua, secondo il sistema di classificazione francese?", - "render": "Il minimo livello di difficoltà è {climbing:grade:french:min} secondo il sistema francese/belga" - }, - "0": { - "question": "C’è un sito web (anche non ufficiale) con qualche informazione in più (ad es. topografie)?" - }, - "1": { - "mappings": { - "0": { - "then": "L’ elemento in cui è contenuto indica che è pubblicamente accessibile
{_embedding_feature:access:description}" - }, - "1": { - "then": "L’elemento che lo contiene indica che è richiesto un’autorizzazione per accedervi
{_embedding_feature:access:description}" - }, - "2": { - "then": "L’ elemento che lo contiene indica che è accessibile solo ai clienti
{_embedding_feature:access:description}" - }, - "3": { - "then": "L’ elemento che lo contiene indica che è accessibile solamente ai membri del club
{_embedding_feature:access:description}" - } - } - } - }, - "units+": { - "0": { - "applicableUnits": { - "0": { - "human": " metri" - }, - "1": { - "human": " piedi" - } - } - } - } - }, - "descriptionTail": "La cartina di arrampicata è stata originariamente creata da Christian Neumann. Si prega di scrivere qua se si hanno commenti o domande da fare.

Il progetto usa i dati del progetto OpenStreetMap.

" + "title": "Mappa aperta per le arrampicate" }, "cyclestreets": { "layers": { @@ -616,32 +318,19 @@ "mappings": { "3": { "then": "Questa strada non è una strada ciclabile" - }, - "0": { - "then": "Questa è una strada ciclabile (e ha un limite di velocità massima di 30 km/h)" - }, - "1": { - "then": "Questa è una strada ciclabile" - }, - "2": { - "then": "Diverrà tra poco una strada ciclabile" } - }, - "question": "È una strada ciclabile?" + } }, "1": { "question": "Questa strada diventerà una strada ciclabile quando?", "render": "Questa strada diventerà una strada ciclabile dal {cyclestreet:start_date}" } } - }, - "description": "Una strada ciclabile è una strada dove il traffico motorizzato non può superare i velocipedi. La sua presenza è segnalata da un cartello stradale specifico. Le strade ciclabili sono diffuse in Olanda e Belgio, ma si possono trovare anche in Germania e in Francia. ", - "shortDescription": "Una cartina per le strade ciclabili", - "title": "Strade ciclabili" + } }, "cyclofix": { "description": "Questa mappa offre a chi va in bici una soluzione semplice per trovare tutte le infrastrutture di cui ha bisogno.

Puoi tracciare la tua posizione esatta (solo su mobile) e selezionare i livelli che ti interessano nell'angolo in basso a sinistra. Puoi anche usare questo strumento per aggiungere o modificare punti di interesse alla mappa e aggiungere nuove informazioni rispendendo alle domande.

Tutte le modifiche che apporterai saranno automaticamente salvate nel database mondiale di OpenStreetMap e potranno essere liberamente riutilizzate da tutti e tutte.

Per maggiori informazioni sul progetto ciclofix, visita cyclofix.osm.be.", - "title": "Cyclofix — una mappa libera per chi va in bici" + "title": "Cyclofix - una mappa libera per chi va in bici" }, "drinking_water": { "description": "Questa mappa mostra tutti i luoghi in cui è disponibile acqua potabile ed è possibile aggiungerne di nuovi", @@ -697,8 +386,7 @@ "1": { "then": "Non c'è un contenitore per raccogliere la pioggia" } - }, - "question": "È stata installata una riserva d’acqua per il giardino?" + } }, "facadegardens-start_date": { "question": "Quando è stato realizzato il giardino? (è sufficiente l'anno)", @@ -717,26 +405,15 @@ } }, "question": "Il giardino è al sole o in ombra?" - }, - "facadegardens-direction": { - "question": "Com’è orientato questo giardino?", - "render": "Orientamento: {direction} (0 per il Nord e 90 per l’Est)" } - }, - "name": "Giardini verticali", - "title": { - "render": "Giardino verticale" - }, - "description": "Giardini verticali" + } } }, "shortDescription": "Questa mappa mostra i giardini verticali, con foto e informazioni utili sulla loro orientazione, sull'illuminazione solare e sui tipi di piante.", - "title": "Giardini verticali", - "description": "I giardini veritcali e gli alberi in città non solo portano pace e tranquillità ma creano anche un ambiente più bello, aumentano la biodiversità, rendono il clima più fresco e migliorano la qualità dell’aria.
Klimaan VZW e Mechelen Klimaatneutraal vogliono mappare sia i giardini verticali esistenti che quelli nuovi per mostrarli a quanti vogliono costruire un loro proprio giardino o per quelli che amano la natura e vogliono camminare per la città.
Per ulteriori informazioni visita klimaan.be." + "title": "Giardini verticali" }, "ghostbikes": { - "title": "Bici fantasma", - "description": "Una bici fantasma è un monumento in ricordo di un ciclista che è morto in un incidente stradale, che ha la forma di un una bicicletta bianca installata in maniera permanente ne luogo dell’incidente.

In questa cartina, è possibile vedere tutte le bici fantasma che sono state aggiunte su OpenStreetMap. Ne manca una? Chiunque può aggiungere o migliorare le informazioni qui presenti (è solo richiesto un account gratuito su OpenStreetMap)." + "title": "Bici fantasma" }, "hailhydrant": { "layers": { @@ -746,65 +423,19 @@ "mappings": { "2": { "then": "L'idrante è rosso." - }, - "0": { - "then": "Il colore dell’idrante è sconosciuto." - }, - "1": { - "then": "Il colore dell’idrante è giallo." } - }, - "question": "Qual è il colore dell’idrante?", - "render": "Il colore dell’idrante è {colour}" + } }, "hydrant-type": { "mappings": { "0": { "then": "Il tipo di idrante è sconosciuto." - }, - "1": { - "then": " Soprasuolo." - }, - "4": { - "then": " Sottosuolo." - }, - "2": { - "then": " Tubo." - }, - "3": { - "then": " A muro." } }, "question": "Di che tipo è questo idrante?", "render": " Tipo di idrante: {fire_hydrant:type}" - }, - "hydrant-state": { - "render": "Stato di funzionamento", - "mappings": { - "0": { - "then": "L’idrante è (parzialmente o completamente) funzionante." - }, - "1": { - "then": "L’idrante è fuori servizio." - }, - "2": { - "then": "L’idrante è stato rimosso." - } - }, - "question": "Aggiorna lo stato di funzionamento dell’idrante." } - }, - "description": "Livello della mappa che mostra gli idranti antincendio.", - "presets": { - "0": { - "description": "Un idrante è un punto di collegamento dove i pompieri possono estrarre acqua. Potrebbe trovarsi sottoterra.", - "title": "Idrante antincendio" - } - }, - "title": { - "render": "Idrante" - }, - "name": "Mappa degli idranti" + } }, "2": { "description": "Livello che mostra le caserme dei vigili del fuoco.", @@ -815,476 +446,18 @@ "render": "Questa caserma si chiama {name}." }, "station-street": { - "question": " Qual è il nome della via in cui si trova la caserma?", - "render": "La stazione si trova in una strada chiamata {addr:street}." - }, - "station-agency": { - "render": "Questa stazione è gestita da {operator}.", - "mappings": { - "0": { - "then": "Servizio antincendio governativo" - } - }, - "question": "Quale agenzia gestisce questa stazione?" - }, - "station-operator": { - "mappings": { - "0": { - "then": "Questa stazione è gestita dal governo." - }, - "2": { - "then": "Questa stazione è gestita da un gruppo di volontari ufficiale." - }, - "3": { - "then": "Questa stazione è gestita da privati." - }, - "1": { - "then": "Questa stazione è gestita dalla comunità oppure un’associazione informale." - } - }, - "question": "Com’è classificato il gestore di questa stazione?", - "render": "Il gestore è un ente {operator:type}." - }, - "station-place": { - "question": "In che località si trova la stazione? (ad es. quartiere, paese o città)", - "render": "La stazione si trova a {addr:place}." + "question": " Qual è il nome della via in cui si trova la caserma?" } }, "title": { "render": "Caserma dei vigili del fuoco" - }, - "presets": { - "0": { - "description": "Una caserma dei pompieri è un luogo dove si trovano i mezzi antincendio e i pompieri tra una missione e l’altra.", - "title": "Caserma dei vigili del fuoco" - } - } - }, - "3": { - "tagRenderings": { - "ambulance-place": { - "question": "Dove si trova la stazione? (ad es. quartiere, paese o città)", - "render": "La stazione si trova a {addr:place}." - }, - "ambulance-street": { - "question": " Come si chiama la strada in cui si trova questa stazione?", - "render": "Questa stazione si trova in {addr:street}." - }, - "ambulance-name": { - "render": "Questa stazione è chiamata {name}.", - "question": "Qual è il nome di questa stazione delle ambulanze?" - }, - "ambulance-agency": { - "question": "Quale agenzia gestisce questa stazione?", - "render": "Questa stazione è gestita da {operator}." - }, - "ambulance-operator-type": { - "mappings": { - "2": { - "then": "La stazione è gestita da un gruppo ufficiale di volontari." - }, - "0": { - "then": "La stazione è gestita dal governo." - }, - "1": { - "then": "La stazione è gestita dalla comunità o un’organizzazione non ufficiale." - }, - "3": { - "then": "La stazione è gestita da un privato." - } - }, - "question": "Com’è classificato il gestore della stazione?", - "render": "L’operatore è un ente {operator:type}." - } - }, - "title": { - "render": "Stazione delle ambulanze" - }, - "name": "Carta delle stazioni delle ambulanze", - "presets": { - "0": { - "title": "Stazione delle ambulanze", - "description": "Aggiungi una stazione delle ambulanza alla mappa" - } - }, - "description": "La stazione delle ambulanze è un’area per lo stoccaggio delle ambulanze, dell’equipaggiamento medico, dei dispositivi di protezione individuale e di altre forniture medicali." - }, - "1": { - "presets": { - "0": { - "description": "Un estintore è un dispositivo portatile di piccole dimensioni usato per spegnere un incendio", - "title": "Estintore" - } - }, - "name": "Cartina degli estintori.", - "tagRenderings": { - "extinguisher-location": { - "mappings": { - "0": { - "then": "Si trova all’interno." - }, - "1": { - "then": "Si trova all’esterno." - } - }, - "question": "Dove è posizionato?", - "render": "Posizione: {location}" - } - }, - "description": "Livello della mappa che mostra gli idranti antincendio.", - "title": { - "render": "Estintori" } } - }, - "description": "In questa cartina puoi vedere e aggiornare idranti, stazioni dei pompieri, stazioni delle ambulanze ed estintori del tuo quartiere preferito.\n\nPuoi seguire la tua posizione precisa (solo su cellulare) e selezionare i livelli che ti interessano nell’angolo in basso a sinistra. Puoi anche usare questo strumento per aggiungere o modificare i PDI sulla mappa e fornire ulteriori dettagli rispondendo alle domande.\n\nTutte le modifiche che farai verranno automaticamente salvate nel database globale di OpenStreetMap e potranno essere riutilizzate liberamente da tutti.", - "title": "Idranti, estintori, caserme dei vigili del fuoco e stazioni delle ambulanze.", - "shortDescription": "Carta che mostra gli idranti, gli estintori, le caserme dei vigili del fuoco e le stazioni delle ambulanze." + } }, "trees": { "description": "Mappa tutti gli alberi!", "shortDescription": "Mappa tutti gli alberi", "title": "Alberi" - }, - "natuurpunt": { - "shortDescription": "Questa cartina mostra le riserve naturali di Natuurpunt", - "description": "In questa cartina è possibile trovare tutte le riserve naturali offerte da Natuupunt. ", - "title": "Riserve naturali" - }, - "maps": { - "title": "Una cartina delle cartine", - "shortDescription": "Questo tema mostra tutte le mappe (turistiche) conosciute da OpenStreetMap", - "description": "In questa carta puoi trovare tutte le mappe conosciute da OpenStreetMap (tipicamente una grossa mappa su di un pannello informativo che mostra l’area, la città o la regione, ad es. una mappa turistica dietro a un manifesto, la mappa di una riserva naturale, la mappa della rete ciclistica regionale, etc.)

Se manca una mappa, puoi aggiungerla facilmente a questa su OpenStreetMap." - }, - "shops": { - "title": "Mappa dei negozi", - "description": "In questa cartina è possibile aggiungere informazioni di base di un negozio, orari di apertura e numeri di telefono", - "shortDescription": "Una cartina modificabile con informazioni di base dei negozi" - }, - "parkings": { - "title": "Parcheggio", - "description": "Questa cartina mostra diversi posti dove parcheggiare", - "shortDescription": "Questa cartina mostra diversi posti dove parcheggiare" - }, - "playgrounds": { - "shortDescription": "Una cartina dei parchi giochi", - "title": "Parchi giochi", - "description": "In questa cartina vengono mostrati i parchi giochi a cui è possibile aggiungere dettagli" - }, - "personal": { - "title": "Tema personalizzato", - "description": "Crea un tema personale basato sui livelli disponibili per tutti i temi. Per mostrare dei dati, apri selezione livello" - }, - "postboxes": { - "description": "In questa cartina puoi veder e modificare gli uffici postali e le buche delle lettere. Puoi usare questa cartina per trovare dove imbucare la tua prossima cartolina! :)
Hai trovato un errore o una buca delle lettere mancante? Puoi modificare questa cartina con un account gratuito su OpenStreetMap. ", - "layers": { - "0": { - "description": "Il livello che mostra le buche delle lettere.", - "name": "Buche delle lettere", - "title": { - "render": "Buca delle lettere" - }, - "presets": { - "0": { - "title": "Buca delle lettere" - } - } - }, - "1": { - "description": "Un livello che mostra gli uffici postali.", - "filter": { - "0": { - "options": { - "0": { - "question": "Aperto adesso" - } - } - } - }, - "presets": { - "0": { - "title": "Ufficio postale" - } - }, - "name": "Uffici postali", - "tagRenderings": { - "OH": { - "mappings": { - "0": { - "then": "aperto 24/24h (feste incluse)" - } - }, - "question": "Quali sono gli orari di apertura di questo ufficio postale?", - "render": "Orari di apertura: {opening_hours_table()}" - } - }, - "title": { - "render": "Ufficio postale" - } - } - }, - "shortDescription": "Una cartina che mostra le buche delle lettere e gli uffici postali", - "title": "Buche delle lettere e uffici postali" - }, - "surveillance": { - "shortDescription": "Telecamere e altri di mezzi di sorveglianza", - "title": "Sorveglianza sotto controllo", - "description": "In questa cartina puoi trovare le telecamera di sorveglianza." - }, - "uk_addresses": { - "layers": { - "1": { - "tagRenderings": { - "uk_addresses_explanation_osm": { - "render": "Questo indirizzo è salvato su OpenStreetMap" - }, - "uk_addresses_housenumber": { - "mappings": { - "0": { - "then": "Questo edificio non ha indirizzo" - } - }, - "question": "Qual è il numero civico di questa casa?", - "render": "Il numero civico è {addr:housenumber}" - }, - "uk_addresses_street": { - "question": "Qual è la via in cui si trova?", - "render": "L’indirizzo è in via {addr:street}" - } - }, - "name": "Indirizzo presente su OSM", - "description": "Indirizzi", - "title": { - "render": "Indirizzo conosciuto" - } - } - }, - "description": "Contribuisci a OpenStreetMap inserendo le informazioni sull’indirizzo", - "tileLayerSources": { - "0": { - "name": "Confini delle proprietà di osmuk.org" - } - }, - "shortDescription": "Aiuta a costruire un dataset libero per gli indirizzi nel Regno Unito", - "title": "Indirizzi UK" - }, - "observation_towers": { - "shortDescription": "Torri pubblicamente accessibili per godere della vista", - "title": "Torri di osservazione", - "description": "Torri pubblicamente accessibili per godere della vista" - }, - "openwindpowermap": { - "description": "Una cartina per la visione e la modifica delle turbine eoliche.", - "layers": { - "0": { - "presets": { - "0": { - "title": "pala eolica" - } - }, - "tagRenderings": { - "turbine-diameter": { - "render": "Il diametro del rotore di questa pala eolica è di {rotor:diameter} metri.", - "question": "Qual è il diametro (in metri) del rotore di questa pala eolica?" - }, - "turbine-height": { - "render": "L’altezza totale (raggio del rotore incluso) di questa pala eolica è di {height} metri.", - "question": "Qual è l’altezza (in metri e raggio del rotore incluso) di questa pala eolica?" - }, - "turbine-operator": { - "render": "Questa pala eolica è gestita da {operator}.", - "question": "Chi gestisce questa pala eolica?" - }, - "turbine-start-date": { - "render": "Questa pala eolica è entrata in funzione in data {start_date}.", - "question": "Quando è entrata in funzione questa pala eolica?" - }, - "turbine-output": { - "question": "Quant’è la potenza generata da questa pala eolica? (ad es. 2.3 MW)", - "render": "La potenza generata da questa pala eolica è {generator:output:electricity}." - } - }, - "title": { - "mappings": { - "0": { - "then": "{name}" - } - }, - "render": "pala eolica" - }, - "name": "pala eolica", - "units": { - "0": { - "applicableUnits": { - "0": { - "human": " megawatt" - }, - "1": { - "human": " kilowatt" - }, - "2": { - "human": " watt" - }, - "3": { - "human": " gigawatt" - } - } - }, - "1": { - "applicableUnits": { - "0": { - "human": " metri" - } - } - } - } - } - }, - "title": "OpenWindPowerMap" - }, - "cycle_highways": { - "layers": { - "0": { - "name": "strade per velocipedi", - "title": { - "render": "strada per velocipedi" - } - } - }, - "description": "Questa cartina mostra le strade per velocipedi", - "title": "Strade per velocipedi" - }, - "cycle_infra": { - "description": "Una cartina dove vedere e modificare gli elementi riguardanti l’infrastruttura dei velocipedi. Realizzata durante #osoc21.", - "shortDescription": "Una cartina dove vedere e modificare gli elementi riguardanti l’infrastruttura dei velocipedi.", - "title": "Infrastruttura dei velocipedi" - }, - "etymology": { - "layers": { - "1": { - "override": { - "name": "Strade senza informazioni etimologiche" - } - }, - "2": { - "override": { - "name": "Parchi e foreste senza informazioni etimologiche" - } - } - }, - "shortDescription": "Qual è l’origine di un toponimo?", - "title": "Apri Carta Etimologica", - "description": "Su questa cartina sono visibili i nomi a cui sono riferiti gli oggetti. Le strade, gli edifici, etc. provengono da OpenStreetMap che è a sua volta collegata a Wikidata. Nel popup, se esiste, verrà mostrato l’articolo Wikipedia o l'elemento Wikidata a cui si riferisce il nome di quell’oggetto. Se l’oggetto stesso ha una pagina Wikpedia, anch’essa verrà mostrata.

Anche tu puoi contribuire!Ingrandisci abbastanza e tutte le strade appariranno. Puoi cliccare su una e apparirà un popup con la ricerca Wikidata. Con pochi clic puoi aggiungere un collegamento etimologico. Tieni presente che per farlo, hai bisogno di un account gratuito su OpenStreetMap." - }, - "food": { - "title": "Ristoranti e fast food" - }, - "hackerspaces": { - "layers": { - "0": { - "name": "Hackerspace", - "presets": { - "0": { - "description": "Un hackerspace è un’area dove si ritrovano le persone interessate al software", - "title": "Hackerspace" - }, - "1": { - "description": "Un makerspace è un luogo dove gli amanti del fai-da-te si ritrovano per sperimentare con dispositivi di elettronica come arduino, strisce LED, etc.", - "title": "Makerspace" - } - }, - "title": { - "mappings": { - "0": { - "then": " {name}" - } - }, - "render": "Hackerspace" - }, - "tagRenderings": { - "hs-club-mate": { - "mappings": { - "0": { - "then": "In questo hackerspace viene servito Club-Mate" - }, - "1": { - "then": "In questo hackerspace non viene servito Club-Mate" - } - }, - "question": "In questo hackerspace si serve Club-Mate?" - }, - "is_makerspace": { - "question": "È un hackerspace o un makerspace?", - "mappings": { - "0": { - "then": "Si tratta di un makerspace" - }, - "1": { - "then": "Si tratta di un hackerspace tradizionale (orientato al software)" - } - } - }, - "hackerspaces-start_date": { - "render": "Questo hackerspace è stato creato il {start_date}", - "question": "Quando è stato creato questo hackerspace?" - }, - "hackerspaces-opening_hours": { - "mappings": { - "0": { - "then": "Aperto sempre" - } - }, - "render": "{opening_hours_table()}", - "question": "Quando è aperto questo hackerspace?" - }, - "hackerspaces-name": { - "render": "Questo hackerspace si chiama {name}", - "question": "Qual è il nome di questo hackerspace?" - } - }, - "icon": { - "mappings": { - "0": { - "then": "./assets/themes/hackerspaces/led.png" - } - } - }, - "description": "Hackerspace" - } - }, - "title": "Hackerspace", - "description": "Su questa cartina è possibile vedere gli hackerspace, aggiungerne di nuovi o aggiornare le informazioni tutto in maniera pratica", - "shortDescription": "Una cartina degli hackerspace" - }, - "sport_pitches": { - "description": "Una campo sportivo è un’area dove vengono praticati gli sport", - "shortDescription": "Una cartina che mostra i campi sportivi", - "title": "Campi sportivi" - }, - "toilets": { - "description": "Una cartina dei servizi igienici pubblici", - "title": "Mappa libera delle toilet" - }, - "waste_basket": { - "description": "In questa cartina troverai i cestini dei rifiuti nei tuoi paraggi. Se manca un cestino, puoi inserirlo tu stesso", - "shortDescription": "Una cartina dei cestini dei rifiuti", - "title": "Cestino dei rifiuti" - }, - "binoculars": { - "shortDescription": "Una cartina dei binocoli pubblici fissi", - "description": "Una cartina dei binocoli su un palo fissi in un luogo. Si trovano tipicamente nei luoghi turistici, nei belvedere, in cima a torri panoramiche oppure occasionalmente nelle riserve naturali.", - "title": "Binocoli" - }, - "cafes_and_pubs": { - "title": "Caffè e pub" - }, - "fritures": { - "layers": { - "0": { - "override": { - "name": "Friggitoria" - } - } - } } -} +} \ No newline at end of file diff --git a/langs/themes/nb_NO.json b/langs/themes/nb_NO.json index 42c0b0896..d00366f03 100644 --- a/langs/themes/nb_NO.json +++ b/langs/themes/nb_NO.json @@ -1,7 +1,6 @@ { "aed": { - "title": "Åpent AED-kart", - "description": "Defibrillatorer i nærheten" + "title": "Åpne AED-kart" }, "benches": { "shortDescription": "Et benkekart", @@ -25,9 +24,6 @@ "mappings": { "1": { "then": "Kan brukes gratis" - }, - "0": { - "then": "Man må betale for bruk" } } }, @@ -45,31 +41,6 @@ "caravansites-website": { "question": "Har dette stedet en nettside?", "render": "Offisiell nettside: {website}" - }, - "caravansites-internet": { - "mappings": { - "2": { - "then": "Det er tilgang til Internett" - }, - "0": { - "then": "Det finnes tilgang til Internett" - }, - "1": { - "then": "Det er tilgang til Internett" - } - }, - "question": "Tilbyr dette stedet tilgang til Internett?" - }, - "caravansites-internet-fee": { - "question": "Må man betale for tilgang til Internett?", - "mappings": { - "1": { - "then": "Man må ikke betale ekstra for tilgang til Internett" - }, - "0": { - "then": "Man må betale ekstra for tilgang til Internett" - } - } } } } @@ -94,17 +65,7 @@ "name": "Klatreruter", "tagRenderings": { "Length": { - "render": "Denne ruten er {canonical(climbing:length)} lang", - "question": "Hvor mange meter er klatreruten?" - }, - "Name": { - "mappings": { - "0": { - "then": "Denne klatreruten har ikke noe navn" - } - }, - "question": "Hva er navnet på denne klatreruten?", - "render": "{name}" + "render": "Denne ruten er {canonical(climbing:length)} lang" } }, "title": { @@ -169,24 +130,12 @@ "1": { "name": "Fremtidig sykkelvei", "title": { - "render": "Fremtidig sykkelvei", - "mappings": { - "0": { - "then": "{name} vil bli sykkelgate snart" - } - } - }, - "description": "Denne gaten vil bli sykkelgate snart" + "render": "Fremtidig sykkelvei" + } }, "2": { "description": "Lag for å markere hvilken som helst gate som sykkelvei", - "name": "Alle gater", - "title": { - "render": "Gate" - } - }, - "0": { - "name": "Sykkelgater" + "name": "Alle gater" } }, "overrideAll": { @@ -210,8 +159,7 @@ } } }, - "shortDescription": "Et kart over sykkelveier", - "title": "Sykkelgater" + "shortDescription": "Et kart over sykkelveier" }, "facadegardens": { "layers": { @@ -267,140 +215,8 @@ "name": "Kart over brannstasjoner", "title": { "render": "Brannstasjon" - }, - "presets": { - "0": { - "title": "Brannstasjon" - } - }, - "tagRenderings": { - "station-name": { - "render": "Denne stasjonen heter {name}." - }, - "station-operator": { - "mappings": { - "0": { - "then": "Stasjonen drives av myndighetene." - } - } - } - } - } - }, - "title": "Hydranter, brannslukkere, brannstasjoner, og ambulansestasjoner." - }, - "cafes_and_pubs": { - "title": "Kafeer og kneiper" - }, - "charging_stations": { - "title": "Ladestasjoner", - "shortDescription": "Et verdensomspennende kart over ladestasjoner" - }, - "drinking_water": { - "title": "Drikkevann", - "description": "Offentlig tilgjengelig drikkevannssteder" - }, - "food": { - "title": "Restauranter og søppelmat" - }, - "parkings": { - "shortDescription": "Dette kartet viser forskjellige parkeringsplasser", - "title": "Parkering" - }, - "postboxes": { - "layers": { - "0": { - "title": { - "render": "Postboks" - }, - "description": "Laget viser postbokser.", - "name": "Postbokser", - "presets": { - "0": { - "title": "postboks" - } - } - }, - "1": { - "description": "Et lag som viser postkontor.", - "name": "Postkontor", - "presets": { - "0": { - "title": "Postkontor" - } - }, - "title": { - "render": "Postkontor" - } - } - }, - "title": "Postboks og postkontor-kart" - }, - "artwork": { - "description": "Velkommen til det åpne kunstverkskartet, et kart over statuer, byster, grafitti, og andre kunstverk i verden" - }, - "binoculars": { - "title": "Kikkerter", - "shortDescription": "Et kart over fastmonterte kikkerter" - }, - "cyclofix": { - "title": "Cyclofix — et åpent kart for syklister" - }, - "trees": { - "title": "Trær", - "description": "Kartlegg trærne.", - "shortDescription": "Kartlegg alle trærne" - }, - "shops": { - "title": "Kart over åpne butikker" - }, - "bookcases": { - "title": "Kart over åpne bokhyller" - }, - "maps": { - "title": "Et kart over kart" - }, - "natuurpunt": { - "title": "Naturreservater" - }, - "openwindpowermap": { - "layers": { - "0": { - "units": { - "0": { - "applicableUnits": { - "1": { - "human": " kilowatt" - } - } - } } } } - }, - "personal": { - "title": "Personlig tema" - }, - "playgrounds": { - "shortDescription": "Et kart med lekeplasser", - "title": "Lekeplasser" - }, - "toilets": { - "title": "Åpent toalettkart" - }, - "uk_addresses": { - "layers": { - "1": { - "description": "Adresser", - "name": "Kjente adresser i OSM", - "title": { - "render": "Kjent adresse" - } - } - } - }, - "cycle_infra": { - "title": "Sykkelinfrastruktur", - "shortDescription": "Alt relatert til sykkelinfrastruktur." } -} +} \ No newline at end of file diff --git a/langs/themes/nl.json b/langs/themes/nl.json index 06fb6325a..574c05ed6 100644 --- a/langs/themes/nl.json +++ b/langs/themes/nl.json @@ -729,7 +729,7 @@ "grb": { "description": "GRB Fixup", "layers": { - "2": { + "3": { "description": "Dit gebouw heeft een foutmelding", "name": "Fixmes op gebouwen", "tagRenderings": { @@ -772,7 +772,7 @@ "render": "{addr:street} {addr:housenumber}" } }, - "4": { + "5": { "description": "Dit gebouw heeft een foutmelding", "name": "Fixmes op gebouwen", "tagRenderings": { From 4e3f408d5393bf6cb04109aa98279aa6e42479a0 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 3 Nov 2021 00:55:38 +0100 Subject: [PATCH 75/95] Change geolocation behaviour: will not zoom out anymore --- Logic/Actors/GeoLocationHandler.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Logic/Actors/GeoLocationHandler.ts b/Logic/Actors/GeoLocationHandler.ts index 5927b73cb..6e3c7bd75 100644 --- a/Logic/Actors/GeoLocationHandler.ts +++ b/Logic/Actors/GeoLocationHandler.ts @@ -273,7 +273,9 @@ export default class GeoLocationHandler extends VariableUiElement { location ); } else { - this._leafletMap.data.setView([location.latitude, location.longitude], targetZoom); + const currentZoom = this._leafletMap.data.getZoom() + + this._leafletMap.data.setView([location.latitude, location.longitude], Math.max(targetZoom ?? 0, currentZoom)); } } From 0ee6a1a47b6cf05385d17e2328dbe6ee87395b9d Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Wed, 3 Nov 2021 11:58:14 +0100 Subject: [PATCH 76/95] Update mapRendering --- assets/layers/street_lamps/street_lamps.json | 48 ++++--------------- assets/themes/postboxes/postboxes.json | 6 +-- .../street_lighting/street_lighting.json | 12 ++++- .../street_lighting_assen.json | 25 ++++++---- 4 files changed, 37 insertions(+), 54 deletions(-) diff --git a/assets/layers/street_lamps/street_lamps.json b/assets/layers/street_lamps/street_lamps.json index 32383d616..96a64b76f 100644 --- a/assets/layers/street_lamps/street_lamps.json +++ b/assets/layers/street_lamps/street_lamps.json @@ -23,47 +23,19 @@ } ] }, - "icon": { - "render": "./assets/layers/street_lamps/straight_pole.svg", - "mappings": [ - { - "if": { - "and": [ - "lamp_mount=bent_mast", - "light:count=1" - ] - }, - "then": "./assets/layers/street_lamps/bent_pole_1.svg" - }, - { - "if": { - "and": [ - "lamp_mount=bent_mast", - "light:count=2" - ] - }, - "then": "./assets/layers/street_lamps/bent_pole_2.svg" - } - ] - }, - "iconOverlays": [ + "mapRendering": [ { - "if": "light:colour=white", - "then": "circle:white", - "badge": true - }, - { - "if": "light:colour=orange", - "then": "circle:#FFB000", - "badge": true - }, - { - "if": "light:colour=green", - "then": "circle:#55FF55", - "badge": true + "location": "point", + "icon": "./assets/layers/street_lamps/street_lamp.svg", + "iconBadges": [ + { + "if": "light:colour~*", + "then": "circle:{light:colour}" + } + ], + "iconSize": "40,40,bottom" } ], - "iconSize": "40,40,bottom", "presets": [ { "title": { diff --git a/assets/themes/postboxes/postboxes.json b/assets/themes/postboxes/postboxes.json index 7741ac2d6..a81d180e7 100644 --- a/assets/themes/postboxes/postboxes.json +++ b/assets/themes/postboxes/postboxes.json @@ -48,7 +48,7 @@ "tagRenderings": [ "images", { - + "id": "minimap", "render": "{minimap(17): height: 10rem; overflow: hidden; border: 1px solid #DADADA; border-radius: 0.5rem; }" } ], @@ -128,8 +128,6 @@ "images", { "id": "minimap", - - "render": "{minimap(17): height: 10rem; overflow: hidden; border: 1px solid #DADADA; border-radius: 0.5rem; }" }, { @@ -227,4 +225,4 @@ ] } ] -} +} \ No newline at end of file diff --git a/assets/themes/street_lighting/street_lighting.json b/assets/themes/street_lighting/street_lighting.json index f1bd77be0..c7bb25c5a 100644 --- a/assets/themes/street_lighting/street_lighting.json +++ b/assets/themes/street_lighting/street_lighting.json @@ -48,7 +48,11 @@ } ] }, - "color": "#ff0", + "mapRendering": [ + { + "color": "#ff0" + } + ], "tagRenderings": [ { "id": "lit", @@ -113,7 +117,11 @@ } ] }, - "color": "#a9a9a9", + "mapRendering": [ + { + "color": "#a9a9a9" + } + ], "tagRenderings": [ { "id": "lit", diff --git a/assets/themes/street_lighting/street_lighting_assen.json b/assets/themes/street_lighting/street_lighting_assen.json index 799950826..695347f42 100644 --- a/assets/themes/street_lighting/street_lighting_assen.json +++ b/assets/themes/street_lighting/street_lighting_assen.json @@ -34,16 +34,21 @@ "_has_closeby_feature=Number(feat.properties._closest_osm_street_lamp_distance) < 5 ? 'yes' : 'no'" ], "title": "Straatlantaarn in dataset", - "icon": { - "render": "circle:red", - "mappings": [ - { - "if": "_has_closeby_feature=yes", - "then": "circle:#008000aa" - } - ] - }, - "iconSize": "20,20,center", + "mapRendering": [ + { + "location": "point", + "icon": { + "render": "circle:red", + "mappings": [ + { + "if": "_has_closeby_feature=yes", + "then": "circle:#008000aa" + } + ] + }, + "iconSize": "20,20,center" + } + ], "tagRenderings": [ "all_tags" ] From c5d4846def0000720212fadc8415d04e719b951e Mon Sep 17 00:00:00 2001 From: Joost Date: Wed, 3 Nov 2021 15:00:02 +0100 Subject: [PATCH 77/95] removed "deze zaak" This implies opening hours are for a business. But the opening hours syntax is shown for other things as well --- langs/nl.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/langs/nl.json b/langs/nl.json index d0b5573e0..e98ec679e 100644 --- a/langs/nl.json +++ b/langs/nl.json @@ -171,7 +171,7 @@ }, "opening_hours": { "error_loading": "Sorry, deze openingsuren kunnen niet getoond worden", - "open_during_ph": "Op een feestdag is deze zaak", + "open_during_ph": "Op een feestdag is dit", "opensAt": "vanaf", "openTill": "tot", "closed_until": "Gesloten - open op {date}", @@ -181,7 +181,7 @@ "ph_closed": "gesloten", "ph_open": "open", "ph_open_as_usual": "geopend zoals gewoonlijk", - "not_all_rules_parsed": "De openingsuren van deze zaak zijn ingewikkeld. De volgende regels worden niet getoond bij het ingeven:", + "not_all_rules_parsed": "De openingsuren zijn ingewikkeld. De volgende regels worden niet getoond bij het ingeven:", "loadingCountry": "Het land wordt nog bepaald…" }, "skippedQuestions": "Enkele vragen werden overgeslaan", From f76bf2f0148ada21265940c15c3ae8510bd0e50f Mon Sep 17 00:00:00 2001 From: Joost Date: Wed, 3 Nov 2021 15:02:04 +0100 Subject: [PATCH 78/95] removed amenity/shop reference Opening hours not just used on shops and amenities --- langs/en.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/langs/en.json b/langs/en.json index ef1c347af..e93a28ade 100644 --- a/langs/en.json +++ b/langs/en.json @@ -218,10 +218,10 @@ }, "opening_hours": { "error_loading": "Error: could not visualize these opening hours.", - "open_during_ph": "During a public holiday, this amenity is", + "open_during_ph": "During a public holiday, this is", "opensAt": "from", "openTill": "till", - "not_all_rules_parsed": "The opening hours of this shop are complicated. The following rules are ignored in the input element:", + "not_all_rules_parsed": "These opening hours are complicated. The following rules are ignored in the input element:", "closed_until": "Closed until {date}", "closed_permanently": "Closed for an unkown duration", "open_24_7": "Opened around the clock", @@ -298,4 +298,4 @@ "partOfRelation": "This feature is part of a relation. Use another editor to move it.", "cancel": "Cancel move" } -} \ No newline at end of file +} From 63acca1638b9a127d121aecd310bef640d282385 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 4 Nov 2021 02:16:07 +0100 Subject: [PATCH 79/95] Add 'CreateNewWayWithNodeReuse'-action, use it in the GRB-theme --- Logic/Actors/GeoLocationHandler.ts | 23 +- .../FullNodeDatabaseSource.ts | 85 +---- Logic/GeoOperations.ts | 27 +- .../Actions/CreateWayWithPointReuseAction.ts | 313 ++++++++++++++++++ Logic/Osm/Actions/ReplaceGeometryAction.ts | 54 ++- Models/Constants.ts | 2 +- UI/BigComponents/ImportButton.ts | 73 ++-- UI/BigComponents/RightControls.ts | 4 +- assets/layers/conflation/conflation.json | 12 +- assets/themes/grb_import/grb.json | 13 +- 10 files changed, 473 insertions(+), 133 deletions(-) create mode 100644 Logic/Osm/Actions/CreateWayWithPointReuseAction.ts diff --git a/Logic/Actors/GeoLocationHandler.ts b/Logic/Actors/GeoLocationHandler.ts index 6e3c7bd75..c508f8902 100644 --- a/Logic/Actors/GeoLocationHandler.ts +++ b/Logic/Actors/GeoLocationHandler.ts @@ -58,10 +58,15 @@ export default class GeoLocationHandler extends VariableUiElement { private readonly _layoutToUse: LayoutConfig; constructor( - currentGPSLocation: UIEventSource, - leafletMap: UIEventSource, - layoutToUse: LayoutConfig + state: { + currentGPSLocation: UIEventSource, + leafletMap: UIEventSource, + layoutToUse: LayoutConfig, + featureSwitchGeolocation: UIEventSource + } ) { + const currentGPSLocation = state.currentGPSLocation + const leafletMap = state.leafletMap const hasLocation = currentGPSLocation.map( (location) => location !== undefined ); @@ -122,7 +127,7 @@ export default class GeoLocationHandler extends VariableUiElement { this._previousLocationGrant = previousLocationGrant; this._currentGPSLocation = currentGPSLocation; this._leafletMap = leafletMap; - this._layoutToUse = layoutToUse; + this._layoutToUse = state.layoutToUse; this._hasLocation = hasLocation; const self = this; @@ -167,7 +172,7 @@ export default class GeoLocationHandler extends VariableUiElement { const latLonGiven = QueryParameters.wasInitialized("lat") && QueryParameters.wasInitialized("lon") - this.init(false, !latLonGiven); + this.init(false, !latLonGiven && state.featureSwitchGeolocation.data); isLocked.addCallbackAndRunD(isLocked => { if (isLocked) { @@ -208,7 +213,7 @@ export default class GeoLocationHandler extends VariableUiElement { } - private init(askPermission: boolean, forceZoom: boolean) { + private init(askPermission: boolean, zoomToLocation: boolean) { const self = this; if (self._isActive.data) { @@ -222,7 +227,7 @@ export default class GeoLocationHandler extends VariableUiElement { ?.then(function (status) { console.log("Geolocation permission is ", status.state); if (status.state === "granted") { - self.StartGeolocating(forceZoom); + self.StartGeolocating(zoomToLocation); } self._permission.setData(status.state); status.onchange = function () { @@ -234,10 +239,10 @@ export default class GeoLocationHandler extends VariableUiElement { } if (askPermission) { - self.StartGeolocating(forceZoom); + self.StartGeolocating(zoomToLocation); } else if (this._previousLocationGrant.data === "granted") { this._previousLocationGrant.setData(""); - self.StartGeolocating(forceZoom); + self.StartGeolocating(zoomToLocation); } } diff --git a/Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource.ts b/Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource.ts index 026e164f7..750f6d037 100644 --- a/Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource.ts +++ b/Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource.ts @@ -3,21 +3,6 @@ import FeatureSource, {FeatureSourceForLayer, Tiled} from "../FeatureSource"; import {OsmNode, OsmObject, OsmWay} from "../../Osm/OsmObject"; import SimpleFeatureSource from "../Sources/SimpleFeatureSource"; import FilteredLayer from "../../../Models/FilteredLayer"; -import {TagsFilter} from "../../Tags/TagsFilter"; -import OsmChangeAction from "../../Osm/Actions/OsmChangeAction"; -import StaticFeatureSource from "../Sources/StaticFeatureSource"; -import {OsmConnection} from "../../Osm/OsmConnection"; -import {GeoOperations} from "../../GeoOperations"; -import {Utils} from "../../../Utils"; -import {UIEventSource} from "../../UIEventSource"; -import {BBox} from "../../BBox"; -import FeaturePipeline from "../FeaturePipeline"; -import {Tag} from "../../Tags/Tag"; -import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig"; -import {ChangeDescription} from "../../Osm/Actions/ChangeDescription"; -import CreateNewNodeAction from "../../Osm/Actions/CreateNewNodeAction"; -import ChangeTagAction from "../../Osm/Actions/ChangeTagAction"; -import {And} from "../../Tags/And"; export default class FullNodeDatabaseSource implements TileHierarchy { @@ -34,70 +19,7 @@ export default class FullNodeDatabaseSource implements TileHierarchy, - featurePipeline: FeaturePipeline, - layoutToUse: LayoutConfig - }, - newGeometryLngLats: [number, number][], - configs: ConflationConfig[], - ) { - const typeNode = state.filteredLayers.data.filter(l => l.layerDef.id === "type_node")[0] - if (typeNode === undefined) { - throw "Type Node layer is not defined. Add 'type_node' as layer to your layerconfig to use this feature" - } - - const bbox = new BBox(newGeometryLngLats) - const bbox_padded = bbox.pad(1.2) - const allNodes: any[] = [].concat(...state.featurePipeline.GetFeaturesWithin("type_node", bbox).map(tile => tile.filter( - feature => bbox_padded.contains(GeoOperations.centerpointCoordinates(feature)) - ))) - // The strategy: for every point of the new geometry, we search a point that is closeby and matches - // If multiple options match, we choose the most optimal (aka closest) - - const maxDistance = Math.max(...configs.map(c => c.withinRangeOfM)) - for (const coordinate of newGeometryLngLats) { - - let closestNode = undefined; - let closestNodeDistance = undefined - for (const node of allNodes) { - const d = GeoOperations.distanceBetween(GeoOperations.centerpointCoordinates(node), coordinate) - if (d > maxDistance) { - continue - } - let matchesSomeConfig = false - for (const config of configs) { - if (d > config.withinRangeOfM) { - continue - } - if (!config.ifMatches.matchesProperties(node.properties)) { - continue - } - matchesSomeConfig = true; - } - if (!matchesSomeConfig) { - continue - } - if (closestNode === undefined || closestNodeDistance > d) { - closestNode = node; - closestNodeDistance = d; - } - } - - - } - - } - - - + public handleOsmJson(osmJson: any, tileId: number) { const allObjects = OsmObject.ParseObjects(osmJson.elements) @@ -143,8 +65,3 @@ export default class FullNodeDatabaseSource implements TileHierarchy { + + const features = [] + let geometryMoved = false; + for (let i = 0; i < this._coordinateInfo.length; i++) { + const coordinateInfo = this._coordinateInfo[i]; + if (coordinateInfo.identicalTo !== undefined) { + continue + } + if (coordinateInfo.closebyNodes === undefined || coordinateInfo.closebyNodes.length === 0) { + + const newPoint = { + type: "Feature", + properties: { + "newpoint": "yes", + id: "new-geometry-with-reuse-" + i + }, + geometry: { + type: "Point", + coordinates: coordinateInfo.lngLat + } + }; + features.push(newPoint) + continue + } + + const reusedPoint = coordinateInfo.closebyNodes[0] + if (reusedPoint.config.mode === "move_osm_point") { + const moveDescription = { + type: "Feature", + properties: { + "move": "yes", + "osm-id": reusedPoint.node.properties.id, + "id": "new-geometry-move-existing" + i + }, + geometry: { + type: "LineString", + coordinates: [reusedPoint.node.geometry.coordinates, coordinateInfo.lngLat] + } + } + features.push(moveDescription) + + } else { + // The geometry is moved + geometryMoved = true + } + } + + if (geometryMoved) { + + const coords: [number, number][] = [] + for (const info of this._coordinateInfo) { + if (info.identicalTo !== undefined) { + coords.push(coords[info.identicalTo]) + continue + } + + if (info.closebyNodes === undefined || info.closebyNodes.length === 0) { + coords.push(coords[info.identicalTo]) + continue + } + + const closest = info.closebyNodes[0] + if (closest.config.mode === "reuse_osm_point") { + coords.push(closest.node.geometry.coordinates) + } else { + coords.push(info.lngLat) + } + + } + const newGeometry = { + type: "Feature", + properties: { + "resulting-geometry": "yes", + "id": "new-geometry" + }, + geometry: { + type: "LineString", + coordinates: coords + } + } + features.push(newGeometry) + + } + console.log("Preview:", features) + return new StaticFeatureSource(features, false) + } + + protected async CreateChangeDescriptions(changes: Changes): Promise { + const theme = this._state.layoutToUse.id + const allChanges: ChangeDescription[] = [] + const nodeIdsToUse: { lat: number, lon: number, nodeId?: number }[] = [] + for (let i = 0; i < this._coordinateInfo.length; i++) { + const info = this._coordinateInfo[i] + const lat = info.lngLat[1] + const lon = info.lngLat[0] + + if (info.identicalTo !== undefined) { + nodeIdsToUse.push(nodeIdsToUse[info.identicalTo]) + continue + } + if (info.closebyNodes === undefined || info.closebyNodes[0] === undefined) { + const newNodeAction = new CreateNewNodeAction([], lat, lon, { + allowReuseOfPreviouslyCreatedPoints: true, + changeType: null, + theme + }) + + allChanges.push(...(await newNodeAction.CreateChangeDescriptions(changes))) + + nodeIdsToUse.push({ + lat, lon, + nodeId : newNodeAction.newElementIdNumber}) + continue + + } + + const closestPoint = info.closebyNodes[0] + const id = Number(closestPoint.node.properties.id.split("/")[1]) + if(closestPoint.config.mode === "move_osm_point"){ + allChanges.push({ + type: "node", + id, + changes: { + lat, lon + }, + meta: { + theme, + changeType: null + } + }) + } + nodeIdsToUse.push({lat, lon, nodeId: id}) + } + + + const newWay = new CreateNewWayAction(this._tags, nodeIdsToUse, { + theme + }) + + allChanges.push(...(await newWay.Perform(changes))) + + return allChanges + } + + private CalculateClosebyNodes(coordinates: [number, number][]): CoordinateInfo[] { + + const bbox = new BBox(coordinates) + const state = this._state + const allNodes = [].concat(...state.featurePipeline.GetFeaturesWithin("type_node", bbox.pad(1.2))) + const maxDistance = Math.max(...this._config.map(c => c.withinRangeOfM)) + + const coordinateInfo: { + lngLat: [number, number], + identicalTo?: number, + closebyNodes?: { + d: number, + node: any, + config: MergePointConfig + }[] + }[] = coordinates.map(_ => undefined) + + for (let i = 0; i < coordinates.length; i++) { + + if (coordinateInfo[i] !== undefined) { + // Already seen, probably a duplicate coordinate + continue + } + const coor = coordinates[i] + // Check closeby (and probably identical) point further in the coordinate list, mark them as duplicate + for (let j = i + 1; j < coordinates.length; j++) { + if (1000 * GeoOperations.distanceBetween(coor, coordinates[j]) < 0.1) { + coordinateInfo[j] = { + lngLat: coor, + identicalTo: i + } + break; + } + } + + // Gather the actual info for this point + + // Lets search applicable points and determine the merge mode + const closebyNodes: { + d: number, + node: any, + config: MergePointConfig + }[] = [] + for (const node of allNodes) { + const center = node.geometry.coordinates + const d = 1000 * GeoOperations.distanceBetween(coor, center) + if (d > maxDistance) { + continue + } + + for (const config of this._config) { + if (d > config.withinRangeOfM) { + continue + } + if (!config.ifMatches.matchesProperties(node.properties)) { + continue + } + closebyNodes.push({node, d, config}) + } + } + + closebyNodes.sort((n0, n1) => { + return n0.d - n1.d + }) + + coordinateInfo[i] = { + identicalTo: undefined, + lngLat: coor, + closebyNodes + } + + } + + let conflictFree = true; + + do { + conflictFree = true; + for (let i = 0; i < coordinateInfo.length; i++) { + + const coorInfo = coordinateInfo[i] + if (coorInfo.identicalTo !== undefined) { + continue + } + if (coorInfo.closebyNodes === undefined || coorInfo.closebyNodes[0] === undefined) { + continue + } + + for (let j = i + 1; j < coordinates.length; j++) { + const other = coordinateInfo[j] + if (other.closebyNodes === undefined || other.closebyNodes[0] === undefined) { + continue + } + + if (other.closebyNodes[0].node === coorInfo.closebyNodes[0].node) { + conflictFree = false + // We have found a conflict! + // We only keep the closest point + if (other.closebyNodes[0].d > coorInfo.closebyNodes[0].d) { + other.closebyNodes.shift() + } else { + coorInfo.closebyNodes.shift() + } + } + } + } + } while (!conflictFree) + + + return coordinateInfo + } + +} \ No newline at end of file diff --git a/Logic/Osm/Actions/ReplaceGeometryAction.ts b/Logic/Osm/Actions/ReplaceGeometryAction.ts index 72195ff7f..cc56ff03e 100644 --- a/Logic/Osm/Actions/ReplaceGeometryAction.ts +++ b/Logic/Osm/Actions/ReplaceGeometryAction.ts @@ -19,7 +19,15 @@ export default class ReplaceGeometryAction extends OsmChangeAction { }; private readonly wayToReplaceId: string; private readonly theme: string; + /** + * The target coordinates that should end up in OpenStreetMap + */ private readonly targetCoordinates: [number, number][]; + /** + * If a target coordinate is close to another target coordinate, 'identicalTo' will point to the first index. + * @private + */ + private readonly identicalTo: number[] private readonly newTags: Tag[] | undefined; constructor( @@ -46,13 +54,36 @@ export default class ReplaceGeometryAction extends OsmChangeAction { } else if (geom.type === "Polygon") { coordinates = geom.coordinates[0] } + + this.identicalTo = coordinates.map(_ => undefined) + + for (let i = 0; i < coordinates.length; i++) { + if (this.identicalTo[i] !== undefined) { + continue + } + for (let j = i + 1; j < coordinates.length; j++) { + const d = 1000 * GeoOperations.distanceBetween(coordinates[i], coordinates[j]) + if (d < 0.1) { + console.log("Identical coordinates detected: ", i, " and ", j, ": ", coordinates[i], coordinates[j], "distance is", d) + this.identicalTo[j] = i + } + } + } + + this.targetCoordinates = coordinates this.newTags = options.newTags } - public async GetPreview(): Promise { + public async getPreview(): Promise { const {closestIds, allNodesById} = await this.GetClosestIds(); + console.log("Generating preview, identicals are ", ) const preview = closestIds.map((newId, i) => { + if(this.identicalTo[i] !== undefined){ + return undefined + } + + if (newId === undefined) { return { type: "Feature", @@ -80,7 +111,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction { } }; }) - return new StaticFeatureSource(preview, false) + return new StaticFeatureSource(Utils.NoNull(preview), false) } @@ -92,6 +123,11 @@ export default class ReplaceGeometryAction extends OsmChangeAction { const {closestIds, osmWay} = await this.GetClosestIds() for (let i = 0; i < closestIds.length; i++) { + if(this.identicalTo[i] !== undefined){ + const j = this.identicalTo[i] + actualIdsToUse.push(actualIdsToUse[j]) + continue + } const closestId = closestIds[i]; const [lon, lat] = this.targetCoordinates[i] if (closestId === undefined) { @@ -161,7 +197,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction { private async GetClosestIds(): Promise<{ closestIds: number[], allNodesById: Map, osmWay: OsmWay }> { // TODO FIXME: cap move length on points which are embedded into other ways (ev. disconnect them) // TODO FIXME: if a new point has to be created, snap to already existing ways - // TODO FIXME: reuse points if they are the same in the target coordinates + // TODO FIXME: detect intersections with other ways if moved const splitted = this.wayToReplaceId.split("/"); const type = splitted[0]; const idN = Number(splitted[1]); @@ -185,7 +221,8 @@ export default class ReplaceGeometryAction extends OsmChangeAction { const closestIds = [] const distances = [] - for (const target of this.targetCoordinates) { + for (let i = 0; i < this.targetCoordinates.length; i++){ + const target = this.targetCoordinates[i]; let closestDistance = undefined let closestId = undefined; for (const osmNode of allNodes) { @@ -202,9 +239,18 @@ export default class ReplaceGeometryAction extends OsmChangeAction { } // Next step: every closestId can only occur once in the list + // We skip the ones which are identical + console.log("Erasing double ids") for (let i = 0; i < closestIds.length; i++) { + if(this.identicalTo[i] !== undefined){ + closestIds[i] = closestIds[this.identicalTo[i]] + continue + } const closestId = closestIds[i] for (let j = i + 1; j < closestIds.length; j++) { + if(this.identicalTo[j] !== undefined){ + continue + } const otherClosestId = closestIds[j] if (closestId !== otherClosestId) { continue diff --git a/Models/Constants.ts b/Models/Constants.ts index 8d7dab35e..03e6aede0 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -2,7 +2,7 @@ import {Utils} from "../Utils"; export default class Constants { - public static vNumber = "0.12.1-beta"; + public static vNumber = "0.12.2-beta"; public static ImgurApiKey = '7070e7167f0a25a' public static readonly mapillary_client_token_v3 = 'TXhLaWthQ1d4RUg0czVxaTVoRjFJZzowNDczNjUzNmIyNTQyYzI2' public static readonly mapillary_client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85" diff --git a/UI/BigComponents/ImportButton.ts b/UI/BigComponents/ImportButton.ts index fd35a72db..5175132d9 100644 --- a/UI/BigComponents/ImportButton.ts +++ b/UI/BigComponents/ImportButton.ts @@ -32,6 +32,10 @@ import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeature import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer"; import BaseLayer from "../../Models/BaseLayer"; import ReplaceGeometryAction from "../../Logic/Osm/Actions/ReplaceGeometryAction"; +import FullNodeDatabaseSource from "../../Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"; +import CreateWayWithPointReuseAction from "../../Logic/Osm/Actions/CreateWayWithPointReuseAction"; +import OsmChangeAction from "../../Logic/Osm/Actions/OsmChangeAction"; +import FeatureSource from "../../Logic/FeatureSource/FeatureSource"; export interface ImportButtonState { @@ -282,7 +286,7 @@ export default class ImportButton extends Toggle { importClicked: UIEventSource): BaseUIElement { const confirmationMap = Minimap.createMiniMap({ - allowMoving: false, + allowMoving: true, background: o.state.backgroundLayer }) confirmationMap.SetStyle("height: 20rem; overflow: hidden").SetClass("rounded-xl") @@ -297,15 +301,15 @@ export default class ImportButton extends Toggle { allElements: o.state.allElements, layers: o.state.filteredLayers }) + + let action : OsmChangeAction & {getPreview() : Promise} const theme = o.state.layoutToUse.id - - const changes = o.state.changes let confirm: () => Promise if (o.conflationSettings !== undefined) { - let replaceGeometryAction = new ReplaceGeometryAction( + action = new ReplaceGeometryAction( o.state, o.feature, o.conflationSettings.conflateWayId, @@ -314,41 +318,54 @@ export default class ImportButton extends Toggle { newTags: o.newTags.data } ) - - replaceGeometryAction.GetPreview().then(changePreview => { - new ShowDataLayer({ - leafletMap: confirmationMap.leafletMap, - enablePopups: false, - zoomToFeatures: false, - features: changePreview, - allElements: o.state.allElements, - layerToShow: AllKnownLayers.sharedLayers.get("conflation") - }) - }) confirm = async () => { - changes.applyAction (replaceGeometryAction) + changes.applyAction (action) return o.feature.properties.id } } else { + const geom = o.feature.geometry + let coordinates: [number, number][] + if (geom.type === "LineString") { + coordinates = geom.coordinates + } else if (geom.type === "Polygon") { + coordinates = geom.coordinates[0] + } + + + action = new CreateWayWithPointReuseAction( + o.newTags.data, + coordinates, + // @ts-ignore + o.state, + [{ + withinRangeOfM: 1, + ifMatches: new Tag("_is_part_of_building","true"), + mode:"move_osm_point" + + }] + ) + + confirm = async () => { - const geom = o.feature.geometry - let coordinates: [number, number][] - if (geom.type === "LineString") { - coordinates = geom.coordinates - } else if (geom.type === "Polygon") { - coordinates = geom.coordinates[0] - } - const action = new CreateNewWayAction(o.newTags.data, coordinates.map(lngLat => ({ - lat: lngLat[1], - lon: lngLat[0] - })), {theme}) - return action.newElementId + changes.applyAction(action) + return undefined } } + action.getPreview().then(changePreview => { + new ShowDataLayer({ + leafletMap: confirmationMap.leafletMap, + enablePopups: false, + zoomToFeatures: false, + features: changePreview, + allElements: o.state.allElements, + layerToShow: AllKnownLayers.sharedLayers.get("conflation") + }) + }) + const confirmButton = new SubtleButton(o.image(), o.message) confirmButton.onClick(async () => { { diff --git a/UI/BigComponents/RightControls.ts b/UI/BigComponents/RightControls.ts index 352fc11e9..35f11754c 100644 --- a/UI/BigComponents/RightControls.ts +++ b/UI/BigComponents/RightControls.ts @@ -12,9 +12,7 @@ export default class RightControls extends Combine { constructor(state:MapState) { const geolocatioHandler = new GeoLocationHandler( - state.currentGPSLocation, - state.leafletMap, - state.layoutToUse + state ) new ShowDataLayer({ diff --git a/assets/layers/conflation/conflation.json b/assets/layers/conflation/conflation.json index 2f638cfa5..66ea6655a 100644 --- a/assets/layers/conflation/conflation.json +++ b/assets/layers/conflation/conflation.json @@ -30,7 +30,17 @@ }, { "width": "3", - "color": "#00f" + "color": "#00f", + + "dasharray": { + "render": "", + "mappings": [ + { + "if": "resulting-geometry=yes", + "then": "6 6" + } + ] + } } ] } \ No newline at end of file diff --git a/assets/themes/grb_import/grb.json b/assets/themes/grb_import/grb.json index 5a3d5d545..a2fcbcf32 100644 --- a/assets/themes/grb_import/grb.json +++ b/assets/themes/grb_import/grb.json @@ -29,6 +29,7 @@ "minzoom": 18 }, "trackAllNodes": true, + "enableGeolocation": false, "layers": [ { "builtin": "type_node", @@ -41,6 +42,13 @@ "_is_part_of_building_passage=feat.get('parent_ways')?.some(p => p.tunnel === 'building_passage') ?? false", "_is_part_of_highway=!feat.get('is_part_of_building_passage') && (feat.get('parent_ways')?.some(p => p.highway !== undefined && p.highway !== '') ?? false)", "_is_part_of_landuse=feat.get('parent_ways')?.some(p => (p.landuse !== undefined && p.landuse !== '') || (p.natural !== undefined && p.natural !== '')) ?? false" + ], + "mapRendering": [ + { + "icon": "square:#00f", + "iconSize": "5,5,center", + "location": "point" + } ] } }, @@ -638,7 +646,8 @@ "_grb_ref=feat.properties['source:geometry:entity'] + '/' + feat.properties['source:geometry:oidn']", "_imported_osm_object_found= feat.properties['_osm_obj:source:ref'] == feat.properties._grb_ref", "_grb_date=feat.properties['source:geometry:date'].replace(/\\//g,'-')", - "_imported_osm_still_fresh= feat.properties['_osm_obj:source:date'] == feat.properties._grb_date" + "_imported_osm_still_fresh= feat.properties['_osm_obj:source:date'] == feat.properties._grb_date", + "_target_building_type=feat.properties['_osm_obj:building'] === 'yes' ? feat.properties.building : (feat.properties['_osm_obj:building'] ?? feat.properties.building)" ], "tagRenderings": [ { @@ -676,7 +685,7 @@ "mappings": [ { "if": "_overlaps_with!=null", - "then": "{import_button(OSM-buildings,building=$building; source:geometry:date=$_grb_date; source:geometry:ref=$_grb_ref, Replace the geometry in OpenStreetMap,,,_osm_obj:id)}" + "then": "{import_button(OSM-buildings,building=$_target_building_type; source:geometry:date=$_grb_date; source:geometry:ref=$_grb_ref, Replace the geometry in OpenStreetMap,,,_osm_obj:id)}" } ] }, From 6fc4e96a87d859c09df941a00c069c4d1ca7dfb3 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 4 Nov 2021 17:02:43 +0100 Subject: [PATCH 80/95] More work on GRB --- assets/themes/grb_import/grb.json | 15 ++++++++++++--- assets/themes/postboxes/postboxes.json | 4 +--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/assets/themes/grb_import/grb.json b/assets/themes/grb_import/grb.json index a2fcbcf32..2d487ab3d 100644 --- a/assets/themes/grb_import/grb.json +++ b/assets/themes/grb_import/grb.json @@ -100,6 +100,9 @@ "key": "building" }, "render": "The building type is {building}", + "question": { + "en": "What kind of building is this?" + }, "mappings": [ { "if": "building=house", @@ -400,9 +403,10 @@ } ], "calculatedTags": [ - "_embedded_in=feat.overlapWith('OSM-buildings').filter(f => f.feat.properties['addr:housenumber'] !== undefined)[0]?.feat?.properties ", + "_embedded_in=feat.overlapWith('OSM-buildings')[0]?.feat?.properties ", "_embedding_nr=feat.get('_embedded_in')['addr:housenumber']", - "_embedding_street=feat.get('_embedded_in')['addr:street']" + "_embedding_street=feat.get('_embedded_in')['addr:street']", + "_embedding_id=feat.get('_embedded_in').id" ], "isShown": { "render": "yes", @@ -437,6 +441,11 @@ { "id": "import-button", "render": "{import_button(OSM-buildings, addr:street=$STRAATNM; addr:housenumber=$HUISNR,Import this address,,,OSM-buildings,5)}" + }, + { + "id": "apply-button", + "render": "{tag_apply(addr:street=$STRAATNM; addr:housenumber=$HUISNR,Apply this address on the OSM-building,,_embedding_id)}", + "condition": "_embedded_in!=" } ] }, @@ -636,7 +645,7 @@ "name": "GRB geometries", "title": "GRB outline", "calculatedTags": [ - "_overlaps_with=feat.overlapWith('OSM-buildings').filter(f => f.overlap > 1 && (feat.get('_surface') < 20 || f.overlap / feat.get('_surface')) > 0.9)[0] ?? null", + "_overlaps_with=feat.overlapWith('OSM-buildings').filter(f => f.overlap > 1 && (feat.get('_surface') < 20 || f.overlap / feat.get('_surface')) > 0.5)[0] ?? null", "_overlap_absolute=feat.get('_overlaps_with')?.overlap", "_overlap_percentage=Math.round(100 * feat.get('_overlap_absolute') / feat.get('_surface')) ", "_osm_obj:source:ref=feat.get('_overlaps_with')?.feat?.properties['source:geometry:ref']", diff --git a/assets/themes/postboxes/postboxes.json b/assets/themes/postboxes/postboxes.json index 7741ac2d6..1237d6594 100644 --- a/assets/themes/postboxes/postboxes.json +++ b/assets/themes/postboxes/postboxes.json @@ -48,7 +48,7 @@ "tagRenderings": [ "images", { - + "id": "Minimap", "render": "{minimap(17): height: 10rem; overflow: hidden; border: 1px solid #DADADA; border-radius: 0.5rem; }" } ], @@ -128,8 +128,6 @@ "images", { "id": "minimap", - - "render": "{minimap(17): height: 10rem; overflow: hidden; border: 1px solid #DADADA; border-radius: 0.5rem; }" }, { From 1a2636f19eabd3415d5cc4b8461133b32904ab42 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 4 Nov 2021 17:03:12 +0100 Subject: [PATCH 81/95] Formatting, translations --- assets/layers/conflation/conflation.json | 3 +-- assets/themes/postboxes/postboxes.json | 4 ++-- langs/themes/en.json | 11 ++++++++++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/assets/layers/conflation/conflation.json b/assets/layers/conflation/conflation.json index 66ea6655a..743e47cd6 100644 --- a/assets/layers/conflation/conflation.json +++ b/assets/layers/conflation/conflation.json @@ -31,12 +31,11 @@ { "width": "3", "color": "#00f", - "dasharray": { "render": "", "mappings": [ { - "if": "resulting-geometry=yes", + "if": "resulting-geometry=yes", "then": "6 6" } ] diff --git a/assets/themes/postboxes/postboxes.json b/assets/themes/postboxes/postboxes.json index 1237d6594..047c4c322 100644 --- a/assets/themes/postboxes/postboxes.json +++ b/assets/themes/postboxes/postboxes.json @@ -48,7 +48,7 @@ "tagRenderings": [ "images", { - "id": "Minimap", + "id": "Minimap", "render": "{minimap(17): height: 10rem; overflow: hidden; border: 1px solid #DADADA; border-radius: 0.5rem; }" } ], @@ -225,4 +225,4 @@ ] } ] -} +} \ No newline at end of file diff --git a/langs/themes/en.json b/langs/themes/en.json index 7d5a45d34..0764b2039 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -793,7 +793,16 @@ "title": "Ghost bikes" }, "grb": { - "description": "This theme is an attempt to help automating the GRB import.
Note that this is very hacky and 'steals' the GRB data from an external site; in order to do this, you need to install and activate this firefox extension for it to work." + "description": "This theme is an attempt to help automating the GRB import.
Note that this is very hacky and 'steals' the GRB data from an external site; in order to do this, you need to install and activate this firefox extension for it to work.", + "layers": { + "1": { + "tagRenderings": { + "building type": { + "question": "What kind of building is this?" + } + } + } + } }, "hackerspaces": { "description": "On this map you can see hackerspaces, add a new hackerspace or update data directly", From bb47575982d577da3b4c4137e9f19630b1ff3df8 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Thu, 4 Nov 2021 17:27:45 +0100 Subject: [PATCH 82/95] Fix typo --- assets/themes/street_lighting/street_lighting.json | 4 ++-- langs/themes/en.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/themes/street_lighting/street_lighting.json b/assets/themes/street_lighting/street_lighting.json index c7bb25c5a..305eb18ef 100644 --- a/assets/themes/street_lighting/street_lighting.json +++ b/assets/themes/street_lighting/street_lighting.json @@ -71,7 +71,7 @@ { "if": "lit=no", "then": { - "en": "This road is not lit", + "en": "This street is not lit", "nl": "Deze straat is niet verlicht" } }, @@ -140,7 +140,7 @@ { "if": "lit=no", "then": { - "en": "This road is not lit", + "en": "This street is not lit", "nl": "Deze straat is niet verlicht" } }, diff --git a/langs/themes/en.json b/langs/themes/en.json index 4bdbe5ff8..ea49506bc 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -1341,7 +1341,7 @@ "then": "This street is lit" }, "1": { - "then": "This road is not lit" + "then": "This street is not lit" }, "2": { "then": "This street is lit at night" From ab159b1880c604c9185064fb8f359d9a478025ba Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 4 Nov 2021 22:02:42 +0100 Subject: [PATCH 83/95] Translation sync --- assets/layers/artwork/artwork.json | 22 +- assets/layers/barrier/barrier.json | 3 +- assets/layers/bench/bench.json | 48 ++- assets/layers/bench_at_pt/bench_at_pt.json | 3 +- .../bicycle_library/bicycle_library.json | 3 +- assets/layers/bike_parking/bike_parking.json | 3 +- .../bike_repair_station.json | 3 +- .../charging_station/charging_station.json | 299 ++++++++++----- .../cycleways_and_roads.json | 2 +- assets/layers/shops/shops.json | 65 +++- assets/layers/slow_roads/slow_roads.json | 12 +- assets/layers/tree_node/tree_node.json | 6 +- .../visitor_information_centre.json | 3 +- assets/layers/waste_basket/waste_basket.json | 10 +- assets/themes/aed/aed.json | 2 +- assets/themes/artwork/artwork.json | 2 +- assets/themes/bookcases/bookcases.json | 2 +- assets/themes/campersite/campersite.json | 2 +- assets/themes/climbing/climbing.json | 8 +- assets/themes/cyclestreets/cyclestreets.json | 8 +- assets/themes/cyclofix/cyclofix.json | 8 +- assets/themes/etymology.json | 2 +- .../themes/facadegardens/facadegardens.json | 15 +- assets/themes/ghostbikes/ghostbikes.json | 2 +- langs/layers/de.json | 341 +++++++++++++++--- langs/layers/en.json | 296 +++++++-------- langs/layers/fr.json | 14 +- langs/layers/it.json | 36 +- langs/layers/nb_NO.json | 4 +- langs/layers/nl.json | 296 +++++++-------- langs/layers/ru.json | 32 +- langs/shared-questions/en.json | 2 +- langs/shared-questions/it.json | 79 ++++ langs/shared-questions/nb_NO.json | 77 ++++ langs/themes/de.json | 111 +++++- langs/themes/en.json | 125 +++---- langs/themes/eo.json | 74 ---- langs/themes/it.json | 132 ++++++- langs/themes/nb_NO.json | 69 +++- langs/themes/nl.json | 160 ++++---- 40 files changed, 1579 insertions(+), 802 deletions(-) diff --git a/assets/layers/artwork/artwork.json b/assets/layers/artwork/artwork.json index 8d5a821bc..857dbe9d9 100644 --- a/assets/layers/artwork/artwork.json +++ b/assets/layers/artwork/artwork.json @@ -45,12 +45,20 @@ "nl": "Kunstwerk {name}", "fr": "Œuvre d'art {name}", "de": "Kunstwerk {name}", - "id": "Karya seni {name}", + "id": "Karya Seni {name}", "it": "Opera {name}", "ru": "Художественная работа {name}", "es": "Obra de arte {nombre}", "ja": "アートワーク {name}", - "zh_Hant": "藝術品{name}" + "zh_Hant": "藝術品{name}", + "fi": "Taideteos {name}", + "gl": "Obra de arte {name}", + "hu": "Műalkotás {name}", + "nb_NO": "Kunstverk {name}", + "pl": "Dzieło sztuki {name}", + "pt": "Obra de arte {name}", + "pt_BR": "Obra de arte {name}", + "sv": "Konstverk {name}" } } ] @@ -92,7 +100,15 @@ "es": "Obra de arte", "ja": "アートワーク", "zh_Hant": "藝術品", - "nb_NO": "Kunstverk" + "nb_NO": "Kunstverk", + "fi": "Taideteos", + "gl": "Obra de arte", + "hu": "Műalkotás", + "id": "Karya Seni", + "pl": "Dzieło sztuki", + "pt": "Obra de arte", + "pt_BR": "Obra de arte", + "sv": "Konstverk" } } ], diff --git a/assets/layers/barrier/barrier.json b/assets/layers/barrier/barrier.json index 7df7101d4..01f2b580c 100644 --- a/assets/layers/barrier/barrier.json +++ b/assets/layers/barrier/barrier.json @@ -210,7 +210,8 @@ "if": "cycle_barrier:type=squeeze", "then": { "en": "Squeeze gate, gap is smaller at top, than at the bottom ", - "nl": "Knijppoort, ruimte is smaller aan de top, dan aan de bodem " + "nl": "Knijppoort, ruimte is smaller aan de top, dan aan de bodem ", + "de": "Eine Durchfahrtsbeschränkung, Durchfahrtsbreite ist oben kleiner als unten " } } ], diff --git a/assets/layers/bench/bench.json b/assets/layers/bench/bench.json index 11587d5aa..89d567abb 100644 --- a/assets/layers/bench/bench.json +++ b/assets/layers/bench/bench.json @@ -186,7 +186,8 @@ "zh_Hans": "材质: {material}", "pl": "Materiał: {material}", "pt_BR": "Material: {material}", - "pt": "Material: {material}" + "pt": "Material: {material}", + "eo": "Materialo: {material}" }, "freeform": { "key": "material", @@ -210,7 +211,8 @@ "pt_BR": "Material: madeira", "fi": "Materiaali: puu", "pl": "Materiał: drewno", - "pt": "Material: madeira" + "pt": "Material: madeira", + "eo": "Materialo: ligna" } }, { @@ -229,7 +231,8 @@ "zh_Hant": "材質:金屬", "pl": "Materiał: metal", "pt_BR": "Material: metal", - "pt": "Material: metal" + "pt": "Material: metal", + "eo": "Materialo: metala" } }, { @@ -249,7 +252,8 @@ "pt_BR": "Material: pedra", "fi": "Materiaali: kivi", "pl": "Materiał: kamień", - "pt": "Material: pedra" + "pt": "Material: pedra", + "eo": "Materialo: ŝtona" } }, { @@ -269,7 +273,8 @@ "pt_BR": "Material: concreto", "fi": "Materiaali: betoni", "pl": "Materiał: beton", - "pt": "Material: concreto" + "pt": "Material: concreto", + "eo": "Materialo: betona" } }, { @@ -289,7 +294,8 @@ "pt_BR": "Material: plástico", "fi": "Materiaali: muovi", "pl": "Materiał: plastik", - "pt": "Material: plástico" + "pt": "Material: plástico", + "eo": "Materialo: plasta" } }, { @@ -309,7 +315,8 @@ "pt_BR": "Material: aço", "fi": "Materiaali: teräs", "pl": "Materiał: stal", - "pt": "Material: aço" + "pt": "Material: aço", + "eo": "Materialo: ŝtala" } } ], @@ -380,7 +387,8 @@ "pt_BR": "Cor: {colour}", "fi": "Väri: {colour}", "pl": "Kolor: {colour}", - "pt": "Cor: {colour}" + "pt": "Cor: {colour}", + "eo": "Koloro: {colour}" }, "question": { "en": "Which colour does this bench have?", @@ -417,7 +425,8 @@ "pt_BR": "Cor: marrom", "fi": "Väri: ruskea", "pl": "Kolor: brązowy", - "pt": "Cor: castanho" + "pt": "Cor: castanho", + "eo": "Koloro: bruna" } }, { @@ -436,7 +445,8 @@ "pt_BR": "Cor: verde", "fi": "Väri: vihreä", "pl": "Kolor: zielony", - "pt": "Cor: verde" + "pt": "Cor: verde", + "eo": "Koloro: verda" } }, { @@ -455,7 +465,8 @@ "pt_BR": "Cor: cinza", "fi": "Väri: harmaa", "pl": "Kolor: szary", - "pt": "Cor: cinzento" + "pt": "Cor: cinzento", + "eo": "Koloro: griza" } }, { @@ -474,7 +485,8 @@ "pt_BR": "Cor: branco", "fi": "Väri: valkoinen", "pl": "Kolor: biały", - "pt": "Cor: branco" + "pt": "Cor: branco", + "eo": "Koloro: blanka" } }, { @@ -493,7 +505,8 @@ "pt_BR": "Cor: vermelho", "fi": "Väri: punainen", "pl": "Kolor: czerwony", - "pt": "Cor: vermelho" + "pt": "Cor: vermelho", + "eo": "Koloro: ruĝa" } }, { @@ -512,7 +525,8 @@ "pt_BR": "Cor: preto", "fi": "Väri: musta", "pl": "Kolor: czarny", - "pt": "Cor: preto" + "pt": "Cor: preto", + "eo": "Koloro: nigra" } }, { @@ -531,7 +545,8 @@ "pt_BR": "Cor: azul", "fi": "Väri: sininen", "pl": "Kolor: niebieski", - "pt": "Cor: azul" + "pt": "Cor: azul", + "eo": "Koloro: blua" } }, { @@ -550,7 +565,8 @@ "pt_BR": "Cor: amarelo", "fi": "Väri: keltainen", "pl": "Kolor: żółty", - "pt": "Cor: amarelo" + "pt": "Cor: amarelo", + "eo": "Koloro: flava" } } ], diff --git a/assets/layers/bench_at_pt/bench_at_pt.json b/assets/layers/bench_at_pt/bench_at_pt.json index bc15ac3ba..692ee8a3c 100644 --- a/assets/layers/bench_at_pt/bench_at_pt.json +++ b/assets/layers/bench_at_pt/bench_at_pt.json @@ -107,7 +107,8 @@ "pt_BR": "{name}", "fi": "{name}", "pl": "{name}", - "pt": "{name}" + "pt": "{name}", + "eo": "{name}" }, "freeform": { "key": "name" diff --git a/assets/layers/bicycle_library/bicycle_library.json b/assets/layers/bicycle_library/bicycle_library.json index 53be0d372..a1465e55c 100644 --- a/assets/layers/bicycle_library/bicycle_library.json +++ b/assets/layers/bicycle_library/bicycle_library.json @@ -247,7 +247,8 @@ "fr": "Vélothèque", "pt_BR": "Biblioteca de bicicletas", "de": "Fahrradbibliothek", - "pt": "Biblioteca de bicicletas" + "pt": "Biblioteca de bicicletas", + "eo": "Fietsbibliotheek" }, "tags": [ "amenity=bicycle_library" diff --git a/assets/layers/bike_parking/bike_parking.json b/assets/layers/bike_parking/bike_parking.json index 574a06910..17130076b 100644 --- a/assets/layers/bike_parking/bike_parking.json +++ b/assets/layers/bike_parking/bike_parking.json @@ -402,7 +402,8 @@ "zh_Hant": "{access}", "fi": "{access}", "pt_BR": "{access}", - "pt": "{access}" + "pt": "{access}", + "eo": "{access}" }, "freeform": { "key": "access", diff --git a/assets/layers/bike_repair_station/bike_repair_station.json b/assets/layers/bike_repair_station/bike_repair_station.json index 4ece6bf4c..d9c34b759 100644 --- a/assets/layers/bike_repair_station/bike_repair_station.json +++ b/assets/layers/bike_repair_station/bike_repair_station.json @@ -436,7 +436,8 @@ }, "render": { "en": "Report this bicycle pump as broken", - "nl": "Rapporteer deze fietspomp als kapot" + "nl": "Rapporteer deze fietspomp als kapot", + "de": "Melde diese Fahrradpumpe als kaputt" }, "id": "Email maintainer" }, diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index 9c17a3825..53b3f8c48 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -32,7 +32,8 @@ "#": "Allowed vehicle types", "question": { "en": "Which vehicles are allowed to charge here?", - "nl": "Welke voertuigen kunnen hier opgeladen worden?" + "nl": "Welke voertuigen kunnen hier opgeladen worden?", + "de": "Welche Fahrzeuge dürfen hier geladen werden?" }, "multiAnswer": true, "mappings": [ @@ -41,7 +42,8 @@ "ifnot": "bicycle=no", "then": { "en": "Bcycles can be charged here", - "nl": "Fietsen kunnen hier opgeladen worden" + "nl": "Fietsen kunnen hier opgeladen worden", + "de": "Fahrräder können hier geladen werden" } }, { @@ -49,7 +51,8 @@ "ifnot": "motorcar=no", "then": { "en": "Cars can be charged here", - "nl": "Elektrische auto's kunnen hier opgeladen worden" + "nl": "Elektrische auto's kunnen hier opgeladen worden", + "de": "Autos können hier geladen werden" } }, { @@ -57,7 +60,8 @@ "ifnot": "scooter=no", "then": { "en": "Scooters can be charged here", - "nl": "Electrische scooters (snorfiets of bromfiets) kunnen hier opgeladen worden" + "nl": "Electrische scooters (snorfiets of bromfiets) kunnen hier opgeladen worden", + "de": " Roller können hier geladen werden" } }, { @@ -65,7 +69,8 @@ "ifnot": "hgv=no", "then": { "en": "Heavy good vehicles (such as trucks) can be charged here", - "nl": "Vrachtwagens kunnen hier opgeladen worden" + "nl": "Vrachtwagens kunnen hier opgeladen worden", + "de": "Lastkraftwagen (LKW) können hier geladen werden" } }, { @@ -73,7 +78,8 @@ "ifnot": "bus=no", "then": { "en": "Buses can be charged here", - "nl": "Bussen kunnen hier opgeladen worden" + "nl": "Bussen kunnen hier opgeladen worden", + "de": "Busse können hier geladen werden" } } ] @@ -82,11 +88,13 @@ "id": "access", "question": { "en": "Who is allowed to use this charging station?", - "nl": "Wie mag er dit oplaadpunt gebruiken?" + "nl": "Wie mag er dit oplaadpunt gebruiken?", + "de": "Wer darf diese Ladestation benutzen?" }, "render": { "en": "Access is {access}", - "nl": "Toegang voor {access}" + "nl": "Toegang voor {access}", + "de": "Zugang ist {access}" }, "freeform": { "key": "access", @@ -135,11 +143,13 @@ "id": "capacity", "render": { "en": "{capacity} vehicles can be charged here at the same time", - "nl": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden" + "nl": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden", + "de": "{capacity} Fahrzeuge können hier gleichzeitig geladen werden" }, "question": { "en": "How much vehicles can be charged here at the same time?", - "nl": "Hoeveel voertuigen kunnen hier opgeladen worden?" + "nl": "Hoeveel voertuigen kunnen hier opgeladen worden?", + "de": "Wie viele Fahrzeuge können hier gleichzeitig geladen werden?" }, "freeform": { "key": "capacity", @@ -150,7 +160,8 @@ "id": "Available_charging_stations (generated)", "question": { "en": "Which charging connections are available here?", - "nl": "Welke aansluitingen zijn hier beschikbaar?" + "nl": "Welke aansluitingen zijn hier beschikbaar?", + "de": "Welche Ladestationen gibt es hier?" }, "multiAnswer": true, "mappings": [ @@ -251,7 +262,8 @@ }, "then": { "en": "
Chademo
", - "nl": "
Chademo
" + "nl": "
Chademo
", + "de": "
Chademo
" }, "hideInAnswer": true }, @@ -260,7 +272,8 @@ "ifnot": "socket:type1_cable=", "then": { "en": "
Type 1 with cable (J1772)
", - "nl": "
Type 1 met kabel (J1772)
" + "nl": "
Type 1 met kabel (J1772)
", + "de": "
Typ 1 mit Kabel (J1772)
" }, "hideInAnswer": { "or": [ @@ -298,7 +311,8 @@ }, "then": { "en": "
Type 1 with cable (J1772)
", - "nl": "
Type 1 met kabel (J1772)
" + "nl": "
Type 1 met kabel (J1772)
", + "de": "
Typ 1 mit Kabel (J1772)
" }, "hideInAnswer": true }, @@ -307,7 +321,8 @@ "ifnot": "socket:type1=", "then": { "en": "
Type 1 without cable (J1772)
", - "nl": "
Type 1 zonder kabel (J1772)
" + "nl": "
Type 1 zonder kabel (J1772)
", + "de": "
Typ 1 ohne Kabel (J1772)
" }, "hideInAnswer": { "or": [ @@ -345,7 +360,8 @@ }, "then": { "en": "
Type 1 without cable (J1772)
", - "nl": "
Type 1 zonder kabel (J1772)
" + "nl": "
Type 1 zonder kabel (J1772)
", + "de": "
Typ 1 ohne Kabel (J1772)
" }, "hideInAnswer": true }, @@ -354,7 +370,8 @@ "ifnot": "socket:type1_combo=", "then": { "en": "
Type 1 CCS (aka Type 1 Combo)
", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
", + "de": "
Typ 1 CCS (auch bekannt als Typ 1 Combo)
" }, "hideInAnswer": { "or": [ @@ -392,7 +409,8 @@ }, "then": { "en": "
Type 1 CCS (aka Type 1 Combo)
", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
", + "de": "
Typ 1 CCS (auch bekannt als Typ 1 Combo)
" }, "hideInAnswer": true }, @@ -401,7 +419,8 @@ "ifnot": "socket:tesla_supercharger=", "then": { "en": "
Tesla Supercharger
", - "nl": "
Tesla Supercharger
" + "nl": "
Tesla Supercharger
", + "de": "
Tesla Supercharger
" }, "hideInAnswer": { "or": [ @@ -439,7 +458,8 @@ }, "then": { "en": "
Tesla Supercharger
", - "nl": "
Tesla Supercharger
" + "nl": "
Tesla Supercharger
", + "de": "
Tesla Supercharger
" }, "hideInAnswer": true }, @@ -448,7 +468,8 @@ "ifnot": "socket:type2=", "then": { "en": "
Type 2 (mennekes)
", - "nl": "
Type 2 (mennekes)
" + "nl": "
Type 2 (mennekes)
", + "de": "
Typ 2 (Mennekes)
" }, "hideInAnswer": { "or": [ @@ -486,7 +507,8 @@ }, "then": { "en": "
Type 2 (mennekes)
", - "nl": "
Type 2 (mennekes)
" + "nl": "
Type 2 (mennekes)
", + "de": "
Typ 2 (Mennekes)
" }, "hideInAnswer": true }, @@ -495,7 +517,8 @@ "ifnot": "socket:type2_combo=", "then": { "en": "
Type 2 CCS (mennekes)
", - "nl": "
Type 2 CCS (mennekes)
" + "nl": "
Type 2 CCS (mennekes)
", + "de": "
Typ 2 CCS (Mennekes)
" }, "hideInAnswer": { "or": [ @@ -533,7 +556,8 @@ }, "then": { "en": "
Type 2 CCS (mennekes)
", - "nl": "
Type 2 CCS (mennekes)
" + "nl": "
Type 2 CCS (mennekes)
", + "de": "
Typ 2 CCS (Mennekes)
" }, "hideInAnswer": true }, @@ -542,7 +566,8 @@ "ifnot": "socket:type2_cable=", "then": { "en": "
Type 2 with cable (mennekes)
", - "nl": "
Type 2 met kabel (J1772)
" + "nl": "
Type 2 met kabel (J1772)
", + "de": "
Typ 2 mit Kabel (Mennekes)
" }, "hideInAnswer": { "or": [ @@ -580,7 +605,8 @@ }, "then": { "en": "
Type 2 with cable (mennekes)
", - "nl": "
Type 2 met kabel (J1772)
" + "nl": "
Type 2 met kabel (J1772)
", + "de": "
Typ 2 mit Kabel (Mennekes)
" }, "hideInAnswer": true }, @@ -589,7 +615,8 @@ "ifnot": "socket:tesla_supercharger_ccs=", "then": { "en": "
Tesla Supercharger CCS (a branded type2_css)
", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
", + "de": "
Tesla Supercharger CCS (Typ 2 CSS)
" }, "hideInAnswer": { "or": [ @@ -627,7 +654,8 @@ }, "then": { "en": "
Tesla Supercharger CCS (a branded type2_css)
", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
", + "de": "
Tesla Supercharger CCS (Typ 2 CSS)
" }, "hideInAnswer": true }, @@ -740,7 +768,8 @@ "ifnot": "socket:USB-A=", "then": { "en": "
USB to charge phones and small electronics
", - "nl": "
USB om GSMs en kleine electronica op te laden
" + "nl": "
USB om GSMs en kleine electronica op te laden
", + "de": "
USB zum Laden von Smartphones oder Elektrokleingeräten
" } }, { @@ -752,7 +781,8 @@ }, "then": { "en": "
USB to charge phones and small electronics
", - "nl": "
USB om GSMs en kleine electronica op te laden
" + "nl": "
USB om GSMs en kleine electronica op te laden
", + "de": "
USB zum Laden von Smartphones und Elektrokleingeräten
" }, "hideInAnswer": true }, @@ -804,7 +834,8 @@ "ifnot": "socket:bosch_5pin=", "then": { "en": "
Bosch Active Connect with 5 pins and cable
", - "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
" + "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
", + "de": "
Bosch Active Connect mit 5 Pins und Kabel
" }, "hideInAnswer": { "or": [ @@ -838,7 +869,8 @@ }, "then": { "en": "
Bosch Active Connect with 5 pins and cable
", - "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
" + "nl": "
Bosch Active Connect met 5 pinnen aan een kabel
", + "de": "
Bosch Active Connect mit 5 Pins und Kabel
" }, "hideInAnswer": true } @@ -1189,14 +1221,21 @@ }, "question": { "en": "When is this charging station opened?", - "nl": "Wanneer is dit oplaadpunt beschikbaar??" + "nl": "Wanneer is dit oplaadpunt beschikbaar??", + "de": "Wann ist diese Ladestation geöffnet?", + "it": "Quali sono gli orari di apertura di questa stazione di ricarica?", + "ja": "この充電ステーションはいつオープンしますか?", + "nb_NO": "Når åpnet denne ladestasjonen?", + "ru": "В какое время работает эта зарядная станция?", + "zh_Hant": "何時是充電站開放使用的時間?" }, "mappings": [ { "if": "opening_hours=24/7", "then": { "en": "24/7 opened (including holidays)", - "nl": "24/7 open - ook tijdens vakanties" + "nl": "24/7 open - ook tijdens vakanties", + "de": "durchgehend geöffnet (auch an Feiertagen)" } } ] @@ -1305,7 +1344,8 @@ "ifnot": "payment:app=no", "then": { "en": "Payment is done using a dedicated app", - "nl": "Betalen via een app van het netwerk" + "nl": "Betalen via een app van het netwerk", + "de": "Bezahlung mit einer speziellen App" } }, { @@ -1313,7 +1353,8 @@ "ifnot": "payment:membership_card=no", "then": { "en": "Payment is done using a membership card", - "nl": "Betalen via een lidkaart van het netwerk" + "nl": "Betalen via een lidkaart van het netwerk", + "de": "Bezahlung mit einer Mitgliedskarte" } } ] @@ -1324,7 +1365,8 @@ "#": "In some cases, charging is free but one has to be authenticated. We only ask for authentication if fee is no (or unset). By default one sees the questions for either the payment options or the authentication options, but normally not both", "question": { "en": "What kind of authentication is available at the charging station?", - "nl": "Hoe kan men zich aanmelden aan dit oplaadstation?" + "nl": "Hoe kan men zich aanmelden aan dit oplaadstation?", + "de": "Welche Authentifizierung ist an der Ladestation möglich?" }, "multiAnswer": true, "mappings": [ @@ -1333,7 +1375,8 @@ "ifnot": "authentication:membership_card=no", "then": { "en": "Authentication by a membership card", - "nl": "Aanmelden met een lidkaart is mogelijk" + "nl": "Aanmelden met een lidkaart is mogelijk", + "de": "Authentifizierung durch eine Mitgliedskarte" } }, { @@ -1341,7 +1384,8 @@ "ifnot": "authentication:app=no", "then": { "en": "Authentication by an app", - "nl": "Aanmelden via een applicatie is mogelijk" + "nl": "Aanmelden via een applicatie is mogelijk", + "de": "Authentifizierung durch eine App" } }, { @@ -1349,7 +1393,8 @@ "ifnot": "authentication:phone_call=no", "then": { "en": "Authentication via phone call is available", - "nl": "Aanmelden door te bellen naar een telefoonnummer is mogelijk" + "nl": "Aanmelden door te bellen naar een telefoonnummer is mogelijk", + "de": "Authentifizierung per Anruf ist möglich" } }, { @@ -1357,7 +1402,8 @@ "ifnot": "authentication:short_message=no", "then": { "en": "Authentication via SMS is available", - "nl": "Aanmelden via SMS is mogelijk" + "nl": "Aanmelden via SMS is mogelijk", + "de": "Authentifizierung per Anruf ist möglich" } }, { @@ -1365,7 +1411,8 @@ "ifnot": "authentication:nfc=no", "then": { "en": "Authentication via NFC is available", - "nl": "Aanmelden via NFC is mogelijk" + "nl": "Aanmelden via NFC is mogelijk", + "de": "Authentifizierung über NFC ist möglich" } }, { @@ -1373,7 +1420,8 @@ "ifnot": "authentication:money_card=no", "then": { "en": "Authentication via Money Card is available", - "nl": "Aanmelden met Money Card is mogelijk" + "nl": "Aanmelden met Money Card is mogelijk", + "de": "Authentifizierung über Geldkarte ist möglich" } }, { @@ -1381,7 +1429,8 @@ "ifnot": "authentication:debit_card=no", "then": { "en": "Authentication via debit card is available", - "nl": "Aanmelden met een betaalkaart is mogelijk" + "nl": "Aanmelden met een betaalkaart is mogelijk", + "de": "Authentifizierung per Debitkarte ist möglich" } }, { @@ -1389,7 +1438,8 @@ "ifnot": "authentication:none=no", "then": { "en": "Charging here is (also) possible without authentication", - "nl": "Hier opladen is (ook) mogelijk zonder aan te melden" + "nl": "Hier opladen is (ook) mogelijk zonder aan te melden", + "de": "Das Aufladen ist hier (auch) ohne Authentifizierung möglich" } } ], @@ -1404,11 +1454,13 @@ "id": "Auth phone", "render": { "en": "Authenticate by calling or SMS'ing to {authentication:phone_call:number}", - "nl": "Aanmelden door te bellen of te SMS'en naar {authentication:phone_call:number}" + "nl": "Aanmelden door te bellen of te SMS'en naar {authentication:phone_call:number}", + "de": "Authentifizierung durch Anruf oder SMS an {authentication:phone_call:number}" }, "question": { "en": "What's the phone number for authentication call or SMS?", - "nl": "Wat is het telefoonnummer dat men moet bellen of SMS'en om zich aan te melden?" + "nl": "Wat is het telefoonnummer dat men moet bellen of SMS'en om zich aan te melden?", + "de": "Wie lautet die Telefonnummer für den Authentifizierungsanruf oder die SMS?" }, "freeform": { "key": "authentication:phone_call:number", @@ -1425,21 +1477,24 @@ "id": "maxstay", "question": { "en": "What is the maximum amount of time one is allowed to stay here?", - "nl": "Hoelang mag een voertuig hier blijven staan?" + "nl": "Hoelang mag een voertuig hier blijven staan?", + "de": "Was ist die Höchstdauer des Aufenthalts hier?" }, "freeform": { "key": "maxstay" }, "render": { "en": "One can stay at most {canonical(maxstay)}", - "nl": "De maximale parkeertijd hier is {canonical(maxstay)}" + "nl": "De maximale parkeertijd hier is {canonical(maxstay)}", + "de": "Die maximale Parkzeit beträgt {canonical(maxstay)}" }, "mappings": [ { "if": "maxstay=unlimited", "then": { "en": "No timelimit on leaving your vehicle here", - "nl": "Geen maximum parkeertijd" + "nl": "Geen maximum parkeertijd", + "de": "Keine Höchstparkdauer" } } ], @@ -1456,11 +1511,22 @@ "id": "Network", "render": { "en": "Part of the network {network}", - "nl": "Maakt deel uit van het {network}-netwerk" + "nl": "Maakt deel uit van het {network}-netwerk", + "de": "Teil des Netzwerks {network}", + "it": "{network}", + "ja": "{network}", + "nb_NO": "{network}", + "ru": "{network}", + "zh_Hant": "{network}" }, "question": { "en": "Is this charging station part of a network?", - "nl": "Is dit oplaadpunt deel van een groter netwerk?" + "nl": "Is dit oplaadpunt deel van een groter netwerk?", + "de": "Ist diese Ladestation Teil eines Netzwerks?", + "it": "A quale rete appartiene questa stazione di ricarica?", + "ja": "この充電ステーションの運営チェーンはどこですか?", + "ru": "К какой сети относится эта станция?", + "zh_Hant": "充電站所屬的網路是?" }, "freeform": { "key": "network" @@ -1470,14 +1536,16 @@ "if": "no:network=yes", "then": { "en": "Not part of a bigger network", - "nl": "Maakt geen deel uit van een groter netwerk" + "nl": "Maakt geen deel uit van een groter netwerk", + "de": "Nicht Teil eines größeren Netzwerks" } }, { "if": "network=none", "then": { "en": "Not part of a bigger network", - "nl": "Maakt geen deel uit van een groter netwerk" + "nl": "Maakt geen deel uit van een groter netwerk", + "de": "Nicht Teil eines größeren Netzwerks" }, "hideInAnswer": true }, @@ -1499,11 +1567,13 @@ "id": "Operator", "question": { "en": "Who is the operator of this charging station?", - "nl": "Wie beheert dit oplaadpunt?" + "nl": "Wie beheert dit oplaadpunt?", + "de": "Wer ist der Betreiber dieser Ladestation?" }, "render": { "en": "This charging station is operated by {operator}", - "nl": "Wordt beheerd door {operator}" + "nl": "Wordt beheerd door {operator}", + "de": "Diese Ladestation wird betrieben von {operator}" }, "freeform": { "key": "operator" @@ -1517,7 +1587,8 @@ }, "then": { "en": "Actually, {operator} is the network", - "nl": "Eigenlijk is {operator} het netwerk waarvan het deel uitmaakt" + "nl": "Eigenlijk is {operator} het netwerk waarvan het deel uitmaakt", + "de": "Eigentlich ist {operator} das Netzwerk" }, "hideInAnswer": "operator=" } @@ -1527,11 +1598,13 @@ "id": "phone", "question": { "en": "What number can one call if there is a problem with this charging station?", - "nl": "Wat is het telefoonnummer van de beheerder van dit oplaadpunt?" + "nl": "Wat is het telefoonnummer van de beheerder van dit oplaadpunt?", + "de": "Welche Nummer kann man anrufen, wenn es ein Problem mit dieser Ladestation gibt?" }, "render": { "en": "In case of problems, call {phone}", - "nl": "Bij problemen, bel naar {phone}" + "nl": "Bij problemen, bel naar {phone}", + "de": "Bei Problemen, anrufen unter {phone}" }, "freeform": { "key": "phone", @@ -1542,11 +1615,13 @@ "id": "email", "question": { "en": "What is the email address of the operator?", - "nl": "Wat is het email-adres van de operator?" + "nl": "Wat is het email-adres van de operator?", + "de": "Wie ist die Email-Adresse des Betreibers?" }, "render": { "en": "In case of problems, send an email to {email}", - "nl": "Bij problemen, email naar {email}" + "nl": "Bij problemen, email naar {email}", + "de": "Bei Problemen senden Sie eine E-Mail an {email}" }, "freeform": { "key": "email", @@ -1557,11 +1632,13 @@ "id": "website", "question": { "en": "What is the website where one can find more information about this charging station?", - "nl": "Wat is de website waar men meer info kan vinden over dit oplaadpunt?" + "nl": "Wat is de website waar men meer info kan vinden over dit oplaadpunt?", + "de": "Wie ist die Webseite des Betreibers?" }, "render": { "en": "More info on {website}", - "nl": "Meer informatie op {website}" + "nl": "Meer informatie op {website}", + "de": "Weitere Informationen auf {website}" }, "freeform": { "key": "website", @@ -1573,11 +1650,13 @@ "id": "ref", "question": { "en": "What is the reference number of this charging station?", - "nl": "Wat is het referentienummer van dit oplaadstation?" + "nl": "Wat is het referentienummer van dit oplaadstation?", + "de": "Wie lautet die Kennung dieser Ladestation?" }, "render": { "en": "Reference number is {ref}", - "nl": "Het referentienummer van dit oplaadpunt is {ref}" + "nl": "Het referentienummer van dit oplaadpunt is {ref}", + "de": "Die Kennziffer ist {ref}" }, "freeform": { "key": "ref" @@ -1589,7 +1668,8 @@ "id": "Operational status", "question": { "en": "Is this charging point in use?", - "nl": "Is dit oplaadpunt operationeel?" + "nl": "Is dit oplaadpunt operationeel?", + "de": "Ist dieser Ladepunkt in Betrieb?" }, "mappings": [ { @@ -1604,7 +1684,8 @@ }, "then": { "en": "This charging station works", - "nl": "Dit oplaadpunt werkt" + "nl": "Dit oplaadpunt werkt", + "de": "Diese Ladestation funktioniert" } }, { @@ -1619,7 +1700,8 @@ }, "then": { "en": "This charging station is broken", - "nl": "Dit oplaadpunt is kapot" + "nl": "Dit oplaadpunt is kapot", + "de": "Diese Ladestation ist kaputt" } }, { @@ -1634,7 +1716,8 @@ }, "then": { "en": "A charging station is planned here", - "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden" + "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden", + "de": "Hier ist eine Ladestation geplant" } }, { @@ -1649,7 +1732,8 @@ }, "then": { "en": "A charging station is constructed here", - "nl": "Hier wordt op dit moment een oplaadpunt gebouwd" + "nl": "Hier wordt op dit moment een oplaadpunt gebouwd", + "de": "Hier wird eine Ladestation gebaut" } }, { @@ -1664,7 +1748,8 @@ }, "then": { "en": "This charging station has beed permanently disabled and is not in use anymore but is still visible", - "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig" + "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig", + "de": "Diese Ladestation wurde dauerhaft deaktiviert und wird nicht mehr benutzt, ist aber noch sichtbar" } } ] @@ -1673,21 +1758,24 @@ "id": "Parking:fee", "question": { "en": "Does one have to pay a parking fee while charging?", - "nl": "Moet men parkeergeld betalen tijdens het opladen?" + "nl": "Moet men parkeergeld betalen tijdens het opladen?", + "de": "Muss man beim Laden eine Parkgebühr bezahlen?" }, "mappings": [ { "if": "parking:fee=no", "then": { "en": "No additional parking cost while charging", - "nl": "Geen extra parkeerkost tijdens het opladen" + "nl": "Geen extra parkeerkost tijdens het opladen", + "de": "Keine zusätzlichen Parkgebühren beim Laden" } }, { "if": "parking:fee=yes", "then": { "en": "An additional parking fee should be paid while charging", - "nl": "Tijdens het opladen moet er parkeergeld betaald worden" + "nl": "Tijdens het opladen moet er parkeergeld betaald worden", + "de": "Beim Laden ist eine zusätzliche Parkgebühr zu entrichten" } } ], @@ -1711,8 +1799,10 @@ "socket:typee=1" ], "title": { - "en": "electrical outlet to charge e-bikes", - "nl": "laadpunt met gewone stekker(s) (bedoeld om electrische fietsen op te laden)" + "en": "charging station with a normal european wall plug (meant to charge electrical bikes)", + "nl": "laadpunt met gewone stekker(s) (bedoeld om electrische fietsen op te laden)", + "de": "Ladestation", + "ru": "Зарядная станция" }, "preciseInput": { "preferredBackground": "map" @@ -1767,20 +1857,23 @@ { "question": { "en": "All vehicle types", - "nl": "Alle voertuigen" + "nl": "Alle voertuigen", + "de": "Alle Fahrzeugtypen" } }, { "question": { "en": "Charging station for bicycles", - "nl": "Oplaadpunten voor fietsen" + "nl": "Oplaadpunten voor fietsen", + "de": "Ladestation für Fahrräder" }, "osmTags": "bicycle=yes" }, { "question": { "en": "Charging station for cars", - "nl": "Oplaadpunten voor auto's" + "nl": "Oplaadpunten voor auto's", + "de": "Ladestation für Autos" }, "osmTags": { "or": [ @@ -1797,7 +1890,8 @@ { "question": { "en": "Only working charging stations", - "nl": "Enkel werkende oplaadpunten" + "nl": "Enkel werkende oplaadpunten", + "de": "Nur funktionierende Ladestationen" }, "osmTags": { "and": [ @@ -1814,7 +1908,8 @@ { "question": { "en": "All connectors", - "nl": "Alle types" + "nl": "Alle types", + "de": "Alle Anschlüsse" } }, { @@ -1834,7 +1929,8 @@ { "question": { "en": "Has a
Chademo
connector", - "nl": "Heeft een
Chademo
" + "nl": "Heeft een
Chademo
", + "de": "Hat einen
Chademo
Stecker" }, "osmTags": "socket:chademo~*" }, @@ -1862,7 +1958,8 @@ { "question": { "en": "Has a
Tesla Supercharger
connector", - "nl": "Heeft een
Tesla Supercharger
" + "nl": "Heeft een
Tesla Supercharger
", + "de": "Hat einen
Tesla Supercharger
Stecker" }, "osmTags": "socket:tesla_supercharger~*" }, @@ -1950,11 +2047,15 @@ ], "human": { "en": " minutes", - "nl": " minuten" + "nl": " minuten", + "de": " Minuten", + "ru": " минут" }, "humanSingular": { "en": " minute", - "nl": " minuut" + "nl": " minuut", + "de": " Minute", + "ru": " минута" } }, { @@ -1970,11 +2071,15 @@ ], "human": { "en": " hours", - "nl": " uren" + "nl": " uren", + "de": " Stunden", + "ru": " часов" }, "humanSingular": { "en": " hour", - "nl": " uur" + "nl": " uur", + "de": " Stunde", + "ru": " час" } }, { @@ -1987,11 +2092,15 @@ ], "human": { "en": " days", - "nl": " day" + "nl": " day", + "de": " Tage", + "ru": " дней" }, "humanSingular": { "en": " day", - "nl": " dag" + "nl": " dag", + "de": " Tag", + "ru": " день" } } ] @@ -2027,7 +2136,9 @@ ], "human": { "en": "Volts", - "nl": "volt" + "nl": "volt", + "de": "Volt", + "ru": "Вольт" } } ], @@ -2096,7 +2207,9 @@ ], "human": { "en": "kilowatt", - "nl": "kilowatt" + "nl": "kilowatt", + "de": "Kilowatt", + "ru": "киловатт" } }, { @@ -2106,7 +2219,9 @@ ], "human": { "en": "megawatt", - "nl": "megawatt" + "nl": "megawatt", + "de": "Megawatt", + "ru": "мегаватт" } } ], diff --git a/assets/layers/cycleways_and_roads/cycleways_and_roads.json b/assets/layers/cycleways_and_roads/cycleways_and_roads.json index 11f160c4e..f5f64d797 100644 --- a/assets/layers/cycleways_and_roads/cycleways_and_roads.json +++ b/assets/layers/cycleways_and_roads/cycleways_and_roads.json @@ -201,7 +201,7 @@ "question": { "en": "Is this a cyclestreet?", "nl": "Is dit een fietsstraat?", - "de": "Ist das eine Fahrradstraße" + "de": "Ist das eine Fahrradstraße?" }, "condition": { "and": [ diff --git a/assets/layers/shops/shops.json b/assets/layers/shops/shops.json index 7a5fbe84e..c9d263d81 100644 --- a/assets/layers/shops/shops.json +++ b/assets/layers/shops/shops.json @@ -39,7 +39,8 @@ "fr": "{name}", "ru": "{name}", "ja": "{name}", - "de": "{name}" + "de": "{name}", + "eo": "{name}" } }, { @@ -53,7 +54,8 @@ "fr": "{shop}", "ru": "{shop}", "ja": "{shop}", - "de": "{shop}" + "de": "{shop}", + "eo": "{shop}" } } ] @@ -89,14 +91,16 @@ "en": "This shop sells {shop}", "fr": "Ce magasin vends {shop}", "ja": "こちらのお店では{shop}を販売しております", - "de": "Dieses Geschäft verkauft {shop}" + "de": "Dieses Geschäft verkauft {shop}", + "eo": "Ĉi tiu butiko vendas {shop}" }, "question": { "en": "What does this shop sell?", "fr": "Que vends ce magasin ?", "ja": "このお店では何を売っていますか?", "ru": "Что продаётся в этом магазине?", - "de": "Was wird in diesem Geschäft verkauft?" + "de": "Was wird in diesem Geschäft verkauft?", + "eo": "Kion vendas ĉi tiu butiko?" }, "freeform": { "key": "shop" @@ -112,7 +116,8 @@ "en": "Convenience store", "fr": "Épicerie/superette", "ja": "コンビニエンスストア", - "de": "Lebensmittelladen" + "de": "Lebensmittelladen", + "nl": "Gemakswinkel" } }, { @@ -141,7 +146,8 @@ "fr": "Magasin de vêtements", "ru": "Магазин одежды", "ja": "衣料品店", - "de": "Bekleidungsgeschäft" + "de": "Bekleidungsgeschäft", + "nl": "Kledingwinkel" } }, { @@ -170,7 +176,8 @@ "fr": "Boulangerie", "ja": "ベーカリー", "nl": "Bakkerij", - "de": "Bäckerei" + "de": "Bäckerei", + "eo": "Bakejo" } }, { @@ -181,9 +188,20 @@ }, "then": { "en": "Car repair (garage)", - "fr": "Garagiste", + "fr": "Garage de réparation automobile", "ja": "自動車修理(ガレージ)", - "de": "Autoreparatur (Werkstatt)" + "de": "Autowerkstatt", + "fi": "Autokorjaamo", + "hu": "Autószerelő", + "id": "Bengkel Mobil", + "it": "Autofficina", + "nb_NO": "Bilverksted", + "nl": "Autogarage", + "pl": "Warsztat samochodowy", + "pt": "Oficina de automóveis", + "pt_BR": "Oficina Mecânica", + "ru": "Автомастерская", + "sv": "Bilverkstad" } }, { @@ -197,7 +215,8 @@ "fr": "Concessionnaire", "ru": "Автосалон", "ja": "自動車ディーラー", - "de": "Autohändler" + "de": "Autohändler", + "nl": "Autodealer" } } ], @@ -211,7 +230,9 @@ "id": "{phone}", "ru": "{phone}", "ja": "{phone}", - "de": "{phone}" + "de": "{phone}", + "eo": "{phone}", + "nl": "{phone}" }, "question": { "en": "What is the phone number?", @@ -219,7 +240,8 @@ "ja": "電話番号は何番ですか?", "nl": "Wat is het telefoonnummer?", "ru": "Какой телефон?", - "de": "Wie ist die Telefonnummer?" + "de": "Wie ist die Telefonnummer?", + "eo": "Kio estas la telefonnumero?" }, "freeform": { "key": "phone", @@ -235,7 +257,8 @@ "id": "{website}", "ru": "{website}", "ja": "{website}", - "de": "{website}" + "de": "{website}", + "eo": "{website}" }, "question": { "en": "What is the website of this shop?", @@ -257,7 +280,9 @@ "fr": "{email}", "id": "{email}", "ru": "{email}", - "ja": "{email}" + "ja": "{email}", + "eo": "{email}", + "nl": "{email}" }, "question": { "en": "What is the email address of this shop?", @@ -265,7 +290,8 @@ "ja": "このお店のメールアドレスは何ですか?", "ru": "Каков адрес электронной почты этого магазина?", "nl": "Wat is het e-mailadres van deze winkel?", - "de": "Wie ist die Email-Adresse dieses Geschäfts?" + "de": "Wie ist die Email-Adresse dieses Geschäfts?", + "eo": "Kio estas la retpoŝta adreso de ĉi tiu butiko?" }, "freeform": { "key": "email", @@ -278,7 +304,8 @@ "en": "{opening_hours_table(opening_hours)}", "fr": "{opening_hours_table(opening_hours)}", "ru": "{opening_hours_table(opening_hours)}", - "ja": "{opening_hours_table(opening_hours)}" + "ja": "{opening_hours_table(opening_hours)}", + "nl": "{opening_hours_table(opening_hours)}" }, "question": { "en": "What are the opening hours of this shop?", @@ -327,7 +354,8 @@ "ru": "Магазин", "ja": "店", "nl": "Winkel", - "de": "Geschäft" + "de": "Geschäft", + "eo": "Butiko" }, "description": { "en": "Add a new shop", @@ -335,7 +363,8 @@ "ru": "Добавить новый магазин", "ja": "新しい店を追加する", "nl": "Voeg een nieuwe winkel toe", - "de": "Ein neues Geschäft hinzufügen" + "de": "Ein neues Geschäft hinzufügen", + "eo": "Enmeti novan butikon" } } ], diff --git a/assets/layers/slow_roads/slow_roads.json b/assets/layers/slow_roads/slow_roads.json index 45f6061e9..1de9fc997 100644 --- a/assets/layers/slow_roads/slow_roads.json +++ b/assets/layers/slow_roads/slow_roads.json @@ -117,7 +117,8 @@ "ru": "Поверхность - {surface}", "fr": "La surface en {surface}", "it": "La superficie è {surface}", - "de": "Die Oberfläche ist {surface}" + "de": "Die Oberfläche ist {surface}", + "eo": "La surfaco estas {surface}" }, "freeform": { "key": "surface" @@ -131,7 +132,8 @@ "ru": "Поверхность - трава", "fr": "La surface est en herbe", "it": "La superficie è erba", - "de": "Die Oberfläche ist Gras" + "de": "Die Oberfläche ist Gras", + "eo": "La surfaco estas herba" } }, { @@ -164,7 +166,8 @@ "ru": "Поверхность - песок", "fr": "La surface est en sable", "it": "La superficie è sabbia", - "de": "Die Oberfläche ist Sand" + "de": "Die Oberfläche ist Sand", + "eo": "La surfaco estas sabla" } }, { @@ -197,7 +200,8 @@ "ru": "Поверхность - бетон", "fr": "La surface est en béton", "it": "La superficie è calcestruzzo", - "de": "Die Oberfläche ist Beton" + "de": "Die Oberfläche ist Beton", + "eo": "La surfaco estas betona" } }, { diff --git a/assets/layers/tree_node/tree_node.json b/assets/layers/tree_node/tree_node.json index 94fbacc89..efefb746b 100644 --- a/assets/layers/tree_node/tree_node.json +++ b/assets/layers/tree_node/tree_node.json @@ -37,7 +37,8 @@ "fr": "{name}", "it": "{name}", "ru": "{name}", - "id": "{name}" + "id": "{name}", + "eo": "{name}" } } ] @@ -310,7 +311,8 @@ "ru": "Название: {name}", "fr": "Nom : {name}", "id": "Nama: {name}", - "de": "Name: {name}" + "de": "Name: {name}", + "eo": "Nomo: {name}" }, "question": { "nl": "Heeft de boom een naam?", diff --git a/assets/layers/visitor_information_centre/visitor_information_centre.json b/assets/layers/visitor_information_centre/visitor_information_centre.json index 332c157e9..bbc51fc20 100644 --- a/assets/layers/visitor_information_centre/visitor_information_centre.json +++ b/assets/layers/visitor_information_centre/visitor_information_centre.json @@ -47,7 +47,8 @@ "nl": "{name}", "en": "{name}", "de": "{name}", - "ru": "{name}" + "ru": "{name}", + "eo": "{name}" } } ] diff --git a/assets/layers/waste_basket/waste_basket.json b/assets/layers/waste_basket/waste_basket.json index a7aaddf8e..bed215af4 100644 --- a/assets/layers/waste_basket/waste_basket.json +++ b/assets/layers/waste_basket/waste_basket.json @@ -125,7 +125,7 @@ }, "then": { "en": "This waste basket does not have a dispenser for (dog) excrement bags", - "nl": "Deze vuilbak heeft geen verdeler voor hondenpoepzakjes", + "nl": "Deze vuilnisbak heeft geenverdeler voor hondenpoepzakjes", "de": "Dieser Abfalleimer hat keinen Spender für (Hunde-)Kotbeutel" } }, @@ -133,7 +133,7 @@ "if": "vending=", "then": { "en": "This waste basket does not have a dispenser for (dog) excrement bags", - "nl": "Deze vuilnisbak heeft geen verdeler voor hondenpoepzakjes", + "nl": "Deze vuilnisbaak heeft waarschijnlijk geen verdeler voor hondenpoepzakjes", "de": "Dieser Abfalleimer hat keinen Spender für (Hunde-)Kotbeutel" }, "hideInAnwer": true @@ -160,7 +160,8 @@ "en": "Waste Basket", "nl": "Vuilnisbak", "ru": "Контейнер для мусора", - "de": "Abfalleimer" + "de": "Abfalleimer", + "eo": "Rubujo" } } ] @@ -177,7 +178,8 @@ "en": "Waste Basket", "nl": "Vuilnisbak", "ru": "Контейнер для мусора", - "de": "Abfalleimer" + "de": "Abfalleimer", + "eo": "Rubujo" }, "presiceInput": { "preferredBackground": "photo" diff --git a/assets/themes/aed/aed.json b/assets/themes/aed/aed.json index 8ca029588..78513e08b 100644 --- a/assets/themes/aed/aed.json +++ b/assets/themes/aed/aed.json @@ -13,7 +13,7 @@ "ru": "Открытая карта АВД (Автоматизированных внешних дефибрилляторов)", "ja": "オープンAEDマップ", "zh_Hant": "開放AED地圖", - "nb_NO": "Åpent AED-kart", + "nb_NO": "Åpne AED-kart", "sv": "Öppna AED-karta", "pl": "Otwórz mapę AED", "pt_BR": "Abrir mapa AED" diff --git a/assets/themes/artwork/artwork.json b/assets/themes/artwork/artwork.json index 87b874611..86fe60be2 100644 --- a/assets/themes/artwork/artwork.json +++ b/assets/themes/artwork/artwork.json @@ -19,7 +19,7 @@ "en": "Welcome to Open Artwork Map, a map of statues, busts, grafittis and other artwork all over the world", "nl": "Welkom op de open kunstwerken-kaart, een kaart van standbeelden, bustes, graffiti en andere kunstwerken over de hele wereld", "fr": "Bienvenue sur la carte ouverte des œuvres d'art, une carte des statues, fresques, ... du monde entier", - "de": "Willkommen bei der Freien Kunstwerk-Karte, einer Karte von Statuen, Büsten, Grafitti, ... auf der ganzen Welt", + "de": "Willkommen bei der Freien Kunst-Karte, einer Karte mit Statuen, Büsten, Grafitti, ... auf der ganzen Welt", "id": "Selamat datang di Open Artwork Map, peta untuk patung, grafiti, dan karya seni lain di seluruh dunia", "it": "Benvenuto/a sulla mappa libera dell’arte, una mappa delle statue, i busti, i graffiti e le altre realizzazioni artistiche di tutto il mondo", "ru": "Добро пожаловать на Open Artwork Map, карту статуй, бюстов, граффити и других произведений искусства по всему миру", diff --git a/assets/themes/bookcases/bookcases.json b/assets/themes/bookcases/bookcases.json index 93a1d9f99..252f51048 100644 --- a/assets/themes/bookcases/bookcases.json +++ b/assets/themes/bookcases/bookcases.json @@ -29,7 +29,7 @@ "description": { "en": "A public bookcase is a small streetside cabinet, box, old phone boot or some other objects where books are stored. Everyone can place or take a book. This map aims to collect all these bookcases. You can discover new bookcases nearby and, with a free OpenStreetMap account, quickly add your favourite bookcases.", "nl": "Een boekenruilkast is een kastje waar iedereen een boek kan nemen of achterlaten. Op deze kaart kan je deze boekenruilkasten terugvinden en met een gratis OpenStreetMap-account, ook boekenruilkasten toevoegen of informatie verbeteren", - "de": "Ein Bücherschrank ist ein kleiner Schaltschrank, eine alte Telefonzelle oder eine andere Einrichtung, in der Bücher aufbewahrt werden. Jeder kann ein Buch hinstellen oder mitnehmen. Diese Karte zielt darauf ab, alle Orte mit Bücherschränken zu sammeln. Sie können neue Bücherschränke in der Nähe entdecken und mit einem kostenlosen OpenStreetMap-Konto schnell Ihre Lieblingsbücherschränke hinzufügen.", + "de": "Bücherschränke sind alte Schaltschränke, Telefonzellen oder andere Einrichtungen, zur Aufbewahrung von Büchern. Jeder kann Bücher abstellen oder mitnehmen. Die Karte zielt darauf ab, alle Orte mit Bücherschränken zu sammeln. Sie können neue Bücherschränke in der Nähe entdecken und mit einem kostenlosen OpenStreetMap-Konto schnell Ihre Lieblingsbücherschränke hinzufügen.", "fr": "Une microbibliothèques, également appelée boite à livre, est un élément de mobilier urbain (étagère, armoire, etc) dans lequel sont stockés des livres et autres objets en accès libre. Découvrez les boites à livres prêt de chez vous, ou ajouter en une nouvelle à l'aide de votre compte OpenStreetMap.", "ru": "Общественный книжный шкаф - это небольшой уличный шкаф, коробка, старый телефонный аппарат или другие предметы, где хранятся книги. Каждый может положить или взять книгу. Цель этой карты - собрать все эти книжные шкафы. Вы можете обнаружить новые книжные шкафы поблизости и, имея бесплатный аккаунт OpenStreetMap, быстро добавить свои любимые книжные шкафы.", "ja": "公共の本棚とは、本が保管されている小さな街角のキャビネット、箱、古い電話のトランク、その他の物のことです。誰でも本を置いたり持ったりすることができます。このマップは、すべての公共の本棚を収集することを目的としています。近くで新しい本棚を見つけることができ、無料のOpenStreetMapアカウントを使えば、お気に入りの本棚を簡単に追加できます。", diff --git a/assets/themes/campersite/campersite.json b/assets/themes/campersite/campersite.json index 1b060b824..04dfb5402 100644 --- a/assets/themes/campersite/campersite.json +++ b/assets/themes/campersite/campersite.json @@ -679,7 +679,7 @@ "ja": "新しい公式キャンプサイトを追加します。お客様のキャンピングカーで一泊する指定の場所です。本物のキャンプのように見えるかもしれないし、単なる駐車場のように見えるかもしれない。それらは全く署名されていないかもしれませんが、自治体の決定で定義されているだけです。夜を過ごすことが予想されないキャンパー向けの通常の駐車場は、キャンプサイトではない ", "it": "Aggiungi una nuova area di sosta ufficiale per camper. Si tratta di aree destinate alla sosta notturna dei camper. Potrebbe trattarsi di luoghi di campeggio o semplici parcheggi. Potrebbero anche non essere segnalati sul posto, ma semplicemente indicati in una delibera comunale. Un parcheggio destinato ai camper in cui non è però consentito trascorrere la notte -non- va considerato un'area di sosta per camper. ", "fr": "Ajouter une nouvelle aire de camping officielle, destinée à y passer la nuit avec un camping-car. Elle ne nécessite pas d’infrastructures particulières et peut être simplement désignée sous arrêté municipal, un simple parking ne suffit pas à rentrer dans cette catégorie ", - "de": "Fügen Sie einen neuen offiziellen Wohnmobilstellplatz hinzu. Dies sind ausgewiesene Plätze, an denen Sie in Ihrem Wohnmobil übernachten können. Sie können wie ein richtiger Campingplatz oder nur wie ein Parkplatz aussehen. Möglicherweise sind sie gar nicht ausgeschildert, sondern nur in einem Gemeindebeschluss festgelegt. Ein normaler Parkplatz für Wohnmobile, auf dem Übernachten nicht zulässig ist, ist kein Wohnmobilstellplatz. ", + "de": "Fügen Sie einen neuen offiziellen Wohnmobilstellplatz hinzu. Dies sind ausgewiesene Plätze, an denen Sie in Ihrem Wohnmobil übernachten können. Sie können wie ein richtiger Campingplatz oder nur wie ein Parkplatz aussehen. Möglicherweise sind sie gar nicht ausgeschildert, sondern nur in einem Gemeindebeschluss festgelegt. Ein normaler Parkplatz für Wohnmobile, auf dem übernachten nicht zulässig ist, ist kein Wohnmobilstellplatz. ", "nl": "Voeg een nieuwe officiële camperplaats toe. Dit zijn speciaal aangeduide plaatsen waar het toegestaan is om te overnachten met een camper. Ze kunnen er uitzien als een parking, of soms eerder als een camping. Soms staan ze niet ter plaatse aangeduid, maar heeft de gemeente wel degelijk beslist dat dit een camperplaats is. Een parking voor campers waar je niet mag overnachten is géén camperplaats. " } } diff --git a/assets/themes/climbing/climbing.json b/assets/themes/climbing/climbing.json index 74d5ecc6e..18bc8a0ca 100644 --- a/assets/themes/climbing/climbing.json +++ b/assets/themes/climbing/climbing.json @@ -13,7 +13,7 @@ }, "description": { "nl": "Op deze kaart vind je verschillende klimgelegenheden, zoals klimzalen, bolderzalen en klimmen in de natuur", - "de": "Eine Karte mit verschiedenen Klettermöglichkeiten wie Kletterhallen, Kletterparks oder Felsen in der Natur.", + "de": "Eine Karte mit Klettermöglichkeiten wie Kletterhallen, Kletterparks oder Felsen.", "en": "On this map you will find various climbing opportunities such as climbing gyms, bouldering halls and rocks in nature.", "ru": "На этой карте вы найдете различные возможности для скалолазания, такие как скалодромы, залы для боулдеринга и скалы на природе.", "ja": "この地図には、自然の中のクライミングジム、ボルダリングホール、岩など、さまざまなクライミングの機会があります。", @@ -991,7 +991,8 @@ "en": " meter", "nl": " meter", "fr": " mètres", - "de": " Meter" + "de": " Meter", + "eo": " metro" }, "default": true }, @@ -1005,7 +1006,8 @@ "en": " feet", "nl": " voet", "fr": " pieds", - "de": " Fuß" + "de": " Fuß", + "eo": " futo" } } ] diff --git a/assets/themes/cyclestreets/cyclestreets.json b/assets/themes/cyclestreets/cyclestreets.json index 0fe7ddd78..e52c6bc8d 100644 --- a/assets/themes/cyclestreets/cyclestreets.json +++ b/assets/themes/cyclestreets/cyclestreets.json @@ -21,7 +21,7 @@ }, "description": { "nl": "Een fietsstraat is een straat waar
  • automobilisten geen fietsers mogen inhalen
  • Er een maximumsnelheid van 30km/u geldt
  • Fietsers gemotoriseerde voertuigen links mogen inhalen
  • Fietsers nog steeds voorrang aan rechts moeten verlenen - ook aan auto's en voetgangers op het zebrapad


Op deze open kaart kan je alle gekende fietsstraten zien en kan je ontbrekende fietsstraten aanduiden. Om de kaart aan te passen, moet je je aanmelden met OpenStreetMap en helemaal inzoomen tot straatniveau. ", - "en": "A cyclestreet is a street where motorized traffic is not allowed to overtake cyclists. They are signposted by a special traffic sign. Cyclestreets can be found in the Netherlands and Belgium, but also in Germany and France. ", + "en": "A cyclestreet is is a street where motorized traffic is not allowed to overtake cyclists. They are signposted by a special traffic sign. Cyclestreets can be found in the Netherlands and Belgium, but also in Germany and France. ", "ja": "cyclestreetとは、自動車がサイクリストを追い越すことができない道です。専用の道路標識で表示されます。Cyclestreetsはオランダやベルギーにもありますが、ドイツやフランスにもあります。 ", "zh_Hant": "單車街道是機動車輛受限制,只允許單車通行的道路。通常會有路標顯示特別的交通指標。單車街道通常在荷蘭、比利時看到,但德國與法國也有。 ", "de": "Eine Fahrradstraße ist eine Straße, auf der motorisierter Verkehr Radfahrer nicht überholen darf. Sie sind durch ein spezielles Verkehrsschild gekennzeichnet. Fahrradstraßen gibt es in den Niederlanden und Belgien, aber auch in Deutschland und Frankreich. ", @@ -152,7 +152,8 @@ "nb_NO": "Alle gater", "it": "Tutte le strade", "ru": "Все улицы", - "de": "Alle Straßen" + "de": "Alle Straßen", + "eo": "Ĉiuj stratoj" }, "description": { "nl": "Laag waar je een straat als fietsstraat kan markeren", @@ -180,7 +181,8 @@ "ja": "ストリート", "it": "Strada", "ru": "Улица", - "de": "Straße" + "de": "Straße", + "eo": "Strato" }, "mappings": [ { diff --git a/assets/themes/cyclofix/cyclofix.json b/assets/themes/cyclofix/cyclofix.json index 3b3ccea73..e6983ceeb 100644 --- a/assets/themes/cyclofix/cyclofix.json +++ b/assets/themes/cyclofix/cyclofix.json @@ -1,15 +1,15 @@ { "id": "cyclofix", "title": { - "en": "Cyclofix — an open map for cyclists", + "en": "Cyclofix - an open map for cyclists", "nl": "Cyclofix - een open kaart voor fietsers", "fr": "Cyclofix - Une carte ouverte pour les cyclistes", "gl": "Cyclofix - Un mapa aberto para os ciclistas", - "de": "Cyclofix - eine freie Karte für Radfahrer", + "de": "Cyclofix — eine freie Karte für Radfahrer", "ru": "Cyclofix - открытая карта для велосипедистов", "ja": "Cyclofix - サイクリストのためのオープンマップ", "zh_Hant": "單車修正 - 單車騎士的開放地圖", - "it": "Cyclofix — una mappa libera per chi va in bici", + "it": "Cyclofix - una mappa libera per chi va in bici", "nb_NO": "Cyclofix — et åpent kart for syklister" }, "description": { @@ -17,7 +17,7 @@ "nl": "Het doel van deze kaart is om fietsers een gebruiksvriendelijke oplossing te bieden voor het vinden van de juiste infrastructuur voor hun behoeften.

U kunt uw exacte locatie volgen (enkel mobiel) en in de linkerbenedenhoek categorieën selecteren die voor u relevant zijn. U kunt deze tool ook gebruiken om 'spelden' aan de kaart toe te voegen of te bewerken en meer gegevens te verstrekken door de vragen te beantwoorden.

Alle wijzigingen die u maakt worden automatisch opgeslagen in de wereldwijde database van OpenStreetMap en kunnen door anderen vrij worden hergebruikt.

Bekijk voor meer info over cyclofix ook cyclofix.osm.be.", "fr": "Le but de cette carte est de présenter aux cyclistes une solution facile à utiliser pour trouver l'infrastructure appropriée à leurs besoins.

Vous pouvez suivre votre localisation précise (mobile uniquement) et sélectionner les couches qui vous concernent dans le coin inférieur gauche. Vous pouvez également utiliser cet outil pour ajouter ou modifier des épingles (points d'intérêt) sur la carte et fournir plus de données en répondant aux questions.

Toutes les modifications que vous apportez seront automatiquement enregistrées dans la base de données mondiale d'OpenStreetMap et peuvent être librement réutilisées par d'autres.

Pour plus d'informations sur le projet cyclofix, rendez-vous sur cyclofix.osm.be.", "gl": "O obxectivo deste mapa é amosar ós ciclistas unha solución doada de empregar para atopar a infraestrutura axeitada para as súas necesidades.

Podes obter a túa localización precisa (só para dispositivos móbiles) e escoller as capas que sexan relevantes para ti na esquina inferior esquerda. Tamén podes empregar esta ferramenta para engadir ou editar puntos de interese ó mapa e fornecer máis datos respondendo as cuestións.

Todas as modificacións que fagas serán gardadas de xeito automático na base de datos global do OpenStreetMap e outros poderán reutilizalos libremente.

Para máis información sobre o proxecto cyclofix, vai a cyclofix.osm.be.", - "de": "Mit dieser Karte soll Radfahrern eine einfache Lösung bereitgestellt werden, um die passende Farradinfrastruktur zu finden.

Sie können Ihren genauen Standort verfolgen (nur mobil) und in der linken unteren Ecke die für Sie relevanten Ebenen auswählen. Sie können dieses Tool auch verwenden, um Pins (Points of Interest/Interessante Orte) zur Karte hinzuzufügen oder zu bearbeiten und mehr Daten durch Beantwortung der Fragen bereitstellen.

Ihre Änderungen, werden automatisch in der Datenbank von OpenStreetMap gespeichert und können von anderen frei verwendet werden.

Weitere Informationen über Cyclofix finden Sie unter cyclofix.osm.be.", + "de": "Mit dieser Karte wird Radfahrern eine einfache Lösung bereitgestellt, um die passende Fahrradinfrastruktur zu finden.

Sie können Ihren genauen Standort verfolgen (nur mobil) und in der linken unteren Ecke die für Sie relevanten Ebenen auswählen. Sie können auch interessante Orte zur Karte hinzuzufügen oder bearbeiten und weitere Daten durch Beantwortung von Fragen bereitstellen.

Ihre Änderungen, werden automatisch in OpenStreetMap gespeichert und können von anderen frei verwendet werden.

Weitere Informationen über Cyclofix finden Sie unter cyclofix.osm.be.", "ja": "このマップの目的は、サイクリストのニーズに適した施設を見つけるための使いやすいソリューションを提供することです。

正確な位置を追跡し(モバイルのみ)、左下コーナーで関連するレイヤを選択できます。このツールを使用して、マップにピン(注目点)を追加または編集したり、質問に答えることでより多くのデータを提供することもできます。

変更内容はすべてOpenStreetMapのグローバルデータベースに自動的に保存され、他のユーザーが自由に再利用できます。

cyclofixプロジェクトの詳細については、 cyclofix.osm.be を参照してください。", "zh_Hant": "這份地圖的目的是為單車騎士能夠輕易顯示滿足他們需求的相關設施。

你可以追蹤你確切位置 (只有行動版),以及在左下角選擇相關的圖層。你可以使用這工具在地圖新增或編輯釘子,以及透過回答問題來提供更多資訊。

所有你的變動都會自動存在開放街圖這全球資料圖,並且能被任何人自由取用。

你可以到 cyclofix.osm.be 閱讀更多資訊。", "it": "Questa mappa offre a chi va in bici una soluzione semplice per trovare tutte le infrastrutture di cui ha bisogno.

Puoi tracciare la tua posizione esatta (solo su mobile) e selezionare i livelli che ti interessano nell'angolo in basso a sinistra. Puoi anche usare questo strumento per aggiungere o modificare punti di interesse alla mappa e aggiungere nuove informazioni rispendendo alle domande.

Tutte le modifiche che apporterai saranno automaticamente salvate nel database mondiale di OpenStreetMap e potranno essere liberamente riutilizzate da tutti e tutte.

Per maggiori informazioni sul progetto ciclofix, visita cyclofix.osm.be." diff --git a/assets/themes/etymology.json b/assets/themes/etymology.json index 903dc0421..4216ff041 100644 --- a/assets/themes/etymology.json +++ b/assets/themes/etymology.json @@ -13,7 +13,7 @@ "it": "Qual è l’origine di un toponimo?" }, "description": { - "en": "On this map, you can see what an object is named after. The streets, buildings, ... come from OpenStreetMap which got linked with Wikidata. In the popup, you'll see the Wikipedia article (if it exists) or a Wikidata box of what the object is named after. If the object itself has a Wikipedia page, that'll be shown too.

You can help contribute too!Zoom in enough and all streets will show up. You can click one and a Wikidata-search box will popup. With a few clicks, you can add an etymology link. Note that you need a free OpenStreetMap account to do this.", + "en": "On this map, you can see what an object is named after. The streets, buildings, ... come from OpenStreetMap which got linked with Wikidata. In the popup, you'll see the Wikipedia article (if it exists) or a wikidata box of what the object is named after. If the object itself has a wikipedia page, that'll be shown too.

You can help contribute too!Zoom in enough and all streets will show up. You can click one and a Wikidata-search box will popup. With a few clicks, you can add an etymology link. Note that you need a free OpenStreetMap account to do this.", "nl": "Op deze kaart zie je waar een plaats naar is vernoemd. De straten, gebouwen, ... komen uit OpenStreetMap, waar een link naar Wikidata werd gelegd. In de popup zie je het Wikipedia-artikel van hetgeen naarwaar het vernoemd is of de Wikidata-box.

Je kan zelf ook meehelpen!Als je ver inzoomt, krijg je alle straten te zien. Klik je een straat aan, dan krijg je een zoekfunctie waarmee je snel een nieuwe link kan leggen. Je hebt hiervoor een gratis OpenStreetMap account nodig.", "de": "Auf dieser Karte können Sie sehen, wonach ein Objekt benannt ist. Die Straßen, Gebäude, ... stammen von OpenStreetMap, das mit Wikidata verknüpft wurde. In dem Popup sehen Sie den Wikipedia-Artikel (falls vorhanden) oder ein Wikidata-Feld, nach dem das Objekt benannt ist. Wenn das Objekt selbst eine Wikipedia-Seite hat, wird auch diese angezeigt.

Sie können auch einen Beitrag leisten!Zoomen Sie genug hinein und alle Straßen werden angezeigt. Wenn Sie auf eine Straße klicken, öffnet sich ein Wikidata-Suchfeld. Mit ein paar Klicks können Sie einen Etymologie-Link hinzufügen. Beachten Sie, dass Sie dazu ein kostenloses OpenStreetMap-Konto benötigen.", "it": "Su questa cartina sono visibili i nomi a cui sono riferiti gli oggetti. Le strade, gli edifici, etc. provengono da OpenStreetMap che è a sua volta collegata a Wikidata. Nel popup, se esiste, verrà mostrato l’articolo Wikipedia o l'elemento Wikidata a cui si riferisce il nome di quell’oggetto. Se l’oggetto stesso ha una pagina Wikpedia, anch’essa verrà mostrata.

Anche tu puoi contribuire!Ingrandisci abbastanza e tutte le strade appariranno. Puoi cliccare su una e apparirà un popup con la ricerca Wikidata. Con pochi clic puoi aggiungere un collegamento etimologico. Tieni presente che per farlo, hai bisogno di un account gratuito su OpenStreetMap." diff --git a/assets/themes/facadegardens/facadegardens.json b/assets/themes/facadegardens/facadegardens.json index 2056fa3b8..676424d30 100644 --- a/assets/themes/facadegardens/facadegardens.json +++ b/assets/themes/facadegardens/facadegardens.json @@ -16,7 +16,7 @@ "zh_Hant": "這地圖顯示立面花園的照片以及其他像是方向、日照以及植栽種類等實用訊息。", "it": "Questa mappa mostra i giardini verticali, con foto e informazioni utili sulla loro orientazione, sull'illuminazione solare e sui tipi di piante.", "fr": "Cette carte indique les murs végétalisés avec des photos et des informations comme leur orientation, l’ensoleillement et le type de plantes.", - "de": "Diese Karte zeigt Fassadengärten mit Bildern und nützlichen Informationen über Ausrichtung, Sonneneinstrahlung und Pflanzenarten." + "de": "Diese Karte zeigt Fassadengärten mit Bildern und Details zu Ausrichtung, Sonneneinstrahlung und Pflanzen." }, "description": { "nl": "Ontharde voortuintjes, groene gevels en bomen ín de stad brengen naast rust ook een mooiere stad, een grotere biodiversiteit, een verkoelend effect en een betere luchtkwaliteit.
Klimaan VZW en 'Mechelen Klimaatneutraal' willen met het project Klim(t)aan je Gevel bestaande en nieuwe geveltuintjes in kaart brengen als voorbeeld voor mensen zelf een tuintje willen aanleggen of voor stadwandelaars die houden van de natuur.
Meer info over het project op klimaan.be.", @@ -55,7 +55,7 @@ "ja": "ファサード庭園", "zh_Hant": "立面花園", "fr": "Jardins muraux", - "de": "Fassadenbegrünung" + "de": "Fassadengärten" }, "minzoom": 12, "source": { @@ -73,7 +73,7 @@ "ja": "ファサード庭園", "zh_Hant": "立面花園", "fr": "Jardin mural", - "de": "Fassadenbegrünung" + "de": "Fassadengarten" } }, "description": { @@ -82,7 +82,7 @@ "ja": "ファサード庭園", "zh_Hant": "立面花園", "fr": "Jardins muraux", - "de": "Fassadenbegrünung" + "de": "Fassadengärten" }, "iconOverlays": [ { @@ -377,7 +377,8 @@ "it": "Maggiori dettagli: {description}", "ru": "Подробнее: {description}", "fr": "Plus de détails : {description}", - "de": "Weitere Details: {description}" + "de": "Weitere Details: {description}", + "eo": "Pliaj detaloj: {description}" }, "question": { "nl": "Aanvullende omschrijving van de tuin (indien nodig, en voor zover nog niet omschreven hierboven)", @@ -445,7 +446,7 @@ "ja": "ファサード庭園", "it": "giardino verticale", "fr": "jardin mural", - "de": "Fassadenbegrünung" + "de": "Fassadengarten" }, "description": { "nl": "Voeg geveltuintje toe", @@ -453,7 +454,7 @@ "ja": "ファサード庭園を追加する", "it": "Aggiungi un giardino verticale", "fr": "Ajouter un jardin mural", - "de": "Eine Fassadenbegrünung hinzufügen" + "de": "Einen Fassadengarten hinzufügen" } } ], diff --git a/assets/themes/ghostbikes/ghostbikes.json b/assets/themes/ghostbikes/ghostbikes.json index b1ab8a1d6..9dc192b8a 100644 --- a/assets/themes/ghostbikes/ghostbikes.json +++ b/assets/themes/ghostbikes/ghostbikes.json @@ -43,7 +43,7 @@ "description": { "en": "A ghost bike is a memorial for a cyclist who died in a traffic accident, in the form of a white bicycle placed permanently near the accident location.

On this map, one can see all the ghost bikes which are known by OpenStreetMap. Is a ghost bike missing? Everyone can add or update information here - you only need to have a (free) OpenStreetMap account.", "nl": "Een Witte Fiets of Spookfiets is een aandenken aan een fietser die bij een verkeersongeval om het leven kwam. Het gaat om een fiets die volledig wit is geschilderd en in de buurt van het ongeval werd geinstalleerd.

Op deze kaart zie je alle witte fietsen die door OpenStreetMap gekend zijn. Ontbreekt er een Witte Fiets of wens je informatie aan te passen? Meld je dan aan met een (gratis) OpenStreetMap account.", - "de": "Ein Geisterrad ist ein Denkmal für einen Radfahrer, der bei einem Verkehrsunfall ums Leben kam, in Form eines weißen Fahrrades, das dauerhaft in der Nähe des Unfallortes aufgestellt ist.

Auf dieser Karte kann man alle Geisterräder sehen, die in OpenStreetMap eingetragen sind. Fehlt ein Geisterrad? Jeder kann hier Informationen hinzufügen oder aktualisieren - Sie benötigen lediglich einen (kostenlosen) OpenStreetMap-Account.", + "de": "Ein Geisterrad ist ein weißes Fahrrad, dass zum Gedenken eines tödlich verunglückten Radfahrers vor Ort aufgestellt wurde.

Auf dieser Karte kann man alle Geisterräder sehen, die OpenStreetMap eingetragen sind. Fehlt ein Geisterrad? Jeder kann hier Informationen hinzufügen oder aktualisieren - Sie benötigen lediglich einen (kostenlosen) OpenStreetMap-Account.", "ja": "ゴーストバイクは、交通事故で死亡したサイクリストを記念するもので、事故現場の近くに恒久的に置かれた白い自転車の形をしています。

このマップには、OpenStreetMapで知られているゴーストバイクがすべて表示されます。ゴーストバイクは行方不明ですか?誰でもここで情報の追加や更新ができます。必要なのは(無料の)OpenStreetMapアカウントだけです。", "zh_Hant": "幽靈單車是用來紀念死於交通事故的單車騎士,在事發地點附近放置白色單車。

在這份地圖上面,你可以看到所有在開放街圖已知的幽靈單車。有缺漏的幽靈單車嗎?所有人都可以在這邊新增或是更新資訊-只有你有(免費)開放街圖帳號。", "fr": "Les vélos fantômes sont des mémoriaux pour les cyclistes tuées sur la route, prenant la forme de vélos blancs placés à proximité des faits.

Cette carte indique leur emplacement à partir d’OpenStreetMap. Il est possible de contribuer aux informations ici, sous réserve d’avoir un compte OpenStreetMap (gratuit).", diff --git a/langs/layers/de.json b/langs/layers/de.json index b3b4b32c5..05993e3ec 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -559,6 +559,9 @@ } }, "tagRenderings": { + "Email maintainer": { + "render": "Melde diese Fahrradpumpe als kaputt" + }, "Operational status": { "mappings": { "0": { @@ -666,9 +669,6 @@ }, "question": "Welche Ventile werden unterstützt?", "render": "Diese Pumpe unterstützt die folgenden Ventile: {valves}" - }, - "Email maintainer": { - "render": "Melde diese Fahrradpumpe als kaputt" } }, "title": { @@ -1154,22 +1154,18 @@ "question": "Wie viele Fahrzeuge können hier gleichzeitig geladen werden?", "render": "{capacity} Fahrzeuge können hier gleichzeitig geladen werden" }, + "email": { + "question": "Wie ist die Email-Adresse des Betreibers?", + "render": "Bei Problemen senden Sie eine E-Mail an {email}" + }, "maxstay": { - "render": "Die maximale Parkzeit beträgt {canonical(maxstay)}", - "question": "Was ist die Höchstdauer des Aufenthalts hier?", "mappings": { "0": { "then": "Keine Höchstparkdauer" } - } - }, - "ref": { - "render": "Die Kennziffer ist {ref}", - "question": "Wie lautet die Kennung dieser Ladestation?" - }, - "website": { - "render": "Weitere Informationen auf {website}", - "question": "Wie ist die Webseite des Betreibers?" + }, + "question": "Was ist die Höchstdauer des Aufenthalts hier?", + "render": "Die maximale Parkzeit beträgt {canonical(maxstay)}" }, "payment-options": { "override": { @@ -1183,13 +1179,17 @@ } } }, - "email": { - "question": "Wie ist die Email-Adresse des Betreibers?", - "render": "Bei Problemen senden Sie eine E-Mail an {email}" - }, "phone": { - "render": "Bei Problemen, anrufen unter {phone}", - "question": "Welche Nummer kann man anrufen, wenn es ein Problem mit dieser Ladestation gibt?" + "question": "Welche Nummer kann man anrufen, wenn es ein Problem mit dieser Ladestation gibt?", + "render": "Bei Problemen, anrufen unter {phone}" + }, + "ref": { + "question": "Wie lautet die Kennung dieser Ladestation?", + "render": "Die Kennziffer ist {ref}" + }, + "website": { + "question": "Wie ist die Webseite des Betreibers?", + "render": "Weitere Informationen auf {website}" } }, "units": { @@ -1253,6 +1253,39 @@ }, "question": "Können Radfahrer diese Kreuzung nutzen?" }, + "crossing-button": { + "mappings": { + "1": { + "then": "Diese Ampel hat keine Taste, um ein grünes Signal anzufordern." + } + }, + "question": "Hat diese Ampel eine Taste, um ein grünes Signal anzufordern?" + }, + "crossing-continue-through-red": { + "mappings": { + "0": { + "then": "Ein Radfahrer kann bei roter Ampel geradeaus fahren " + }, + "1": { + "then": "Ein Radfahrer kann bei roter Ampel geradeaus fahren" + }, + "2": { + "then": "Ein Radfahrer kann bei roter Ampel nicht geradeaus fahren" + } + }, + "question": "Kann ein Radfahrer bei roter Ampel geradeaus fahren?" + }, + "crossing-has-island": { + "mappings": { + "0": { + "then": "Der Übergang hat eine Verkehrsinsel" + }, + "1": { + "then": "Diese Ampel hat eine Taste, um ein grünes Signal anzufordern" + } + }, + "question": "Gibt es an diesem Übergang eine Verkehrsinsel?" + }, "crossing-is-zebra": { "mappings": { "0": { @@ -1264,6 +1297,20 @@ }, "question": "Ist das ein Zebrastreifen?" }, + "crossing-right-turn-through-red": { + "mappings": { + "0": { + "then": "Ein Radfahrer kann bei roter Ampel rechts abbiegen " + }, + "1": { + "then": "Ein Radfahrer kann bei roter Ampel rechts abbiegen" + }, + "2": { + "then": "Ein Radfahrer kann bei roter Ampel nicht rechts abbiegen" + } + }, + "question": "Kann ein Radfahrer bei roter Ampel rechts abbiegen?" + }, "crossing-tactile": { "mappings": { "0": { @@ -1399,8 +1446,8 @@ "then": "Dieser Radweg besteht aus Rohboden" } }, - "render": "Der Radweg ist aus {cycleway:surface}", - "question": "Was ist der Belag dieses Radwegs?" + "question": "Was ist der Belag dieses Radwegs?", + "render": "Der Radweg ist aus {cycleway:surface}" }, "Is this a cyclestreet? (For a road)": { "mappings": { @@ -1434,8 +1481,8 @@ "then": "Die Höchstgeschwindigkeit ist 90 km/h" } }, - "render": "Die Höchstgeschwindigkeit auf dieser Straße beträgt {maxspeed} km/h", - "question": "Was ist die Höchstgeschwindigkeit auf dieser Straße?" + "question": "Was ist die Höchstgeschwindigkeit auf dieser Straße?", + "render": "Die Höchstgeschwindigkeit auf dieser Straße beträgt {maxspeed} km/h" }, "Surface of the road": { "mappings": { @@ -1479,8 +1526,8 @@ "then": "Dieser Radweg besteht aus Rohboden" } }, - "render": "Der Radweg ist aus {surface}", - "question": "Was ist der Belag dieser Straße?" + "question": "Was ist der Belag dieser Straße?", + "render": "Der Radweg ist aus {surface}" }, "Surface of the street": { "mappings": { @@ -1602,6 +1649,10 @@ } } }, + "cycleways_and_roads-cycleway:buffer": { + "question": "Wie breit ist der Abstand zwischen Radweg und Straße?", + "render": "Der Sicherheitsabstand zu diesem Radweg beträgt {cycleway:buffer} m" + }, "is lit?": { "mappings": { "0": { @@ -1618,10 +1669,6 @@ } }, "question": "Ist diese Straße beleuchtet?" - }, - "cycleways_and_roads-cycleway:buffer": { - "question": "Wie breit ist der Abstand zwischen Radweg und Straße?", - "render": "Der Sicherheitsabstand zu diesem Radweg beträgt {cycleway:buffer} m" } }, "title": { @@ -1643,6 +1690,13 @@ } }, "defibrillator": { + "icon": { + "mappings": { + "0": { + "then": "./assets/layers/defibrillator/aed_checked.svg" + } + } + }, "name": "Defibrillatoren", "presets": { "0": { @@ -1758,13 +1812,6 @@ }, "title": { "render": "Defibrillator" - }, - "icon": { - "mappings": { - "0": { - "then": "./assets/layers/defibrillator/aed_checked.svg" - } - } } }, "direction": { @@ -1812,9 +1859,20 @@ }, "etymology": { "description": "Alle Objekte, die eine bekannte Namensherkunft haben", + "name": "Hat eine Namensherkunft", "tagRenderings": { "simple etymology": { + "mappings": { + "0": { + "then": "Der Ursprung dieses Namens ist in der gesamten Literatur unbekannt" + } + }, + "question": "Wonach ist dieses Objekt benannt?
Das könnte auf einem Straßenschild stehen", "render": "Benannt nach {name:etymology}" + }, + "wikipedia-etymology": { + "question": "Was ist das Wikidata-Element, nach dem dieses Objekt benannt ist?", + "render": "

Wikipedia Artikel zur Namensherkunft

{wikipedia(name:etymology:wikidata):max-height:20rem}" } } }, @@ -1827,16 +1885,32 @@ } } }, + "1": { + "options": { + "0": { + "question": "Hat vegetarische Speisen" + } + } + }, "2": { "options": { "0": { "question": "Bietet vegan Speisen an" } } + }, + "3": { + "options": { + "0": { + "question": "Hat halal Speisen" + } + } } }, + "name": "Restaurants und Fast Food", "presets": { "0": { + "description": "Ein klassisches Speiselokal mit Sitzgelegenheiten, in dem vollständige Mahlzeiten von Kellnern serviert werden", "title": "Restaurant" }, "1": { @@ -1849,6 +1923,12 @@ "tagRenderings": { "Cuisine": { "mappings": { + "0": { + "then": "Dies ist eine Pizzeria" + }, + "1": { + "then": "Dies ist eine Pommesbude" + }, "2": { "then": "Bietet vorwiegend Pastagerichte an" } @@ -1877,6 +1957,17 @@ }, "question": "Ist an diesem Ort Mitnahme möglich?" }, + "Vegetarian (no friture)": { + "question": "Gibt es im das Restaurant vegetarische Speisen?" + }, + "friture-take-your-container": { + "mappings": { + "0": { + "then": "Sie können ihre eigenen Behälter mitbringen, um Ihre Bestellung zu erhalten, was Einwegverpackungsmaterial und damit Abfall spart" + } + }, + "question": "Wenn Sie Ihr eigenes Behältnis mitbringen (z. B. einen Kochtopf und kleine Töpfe), wird es dann zum Verpacken Ihrer Bestellung verwendet?
" + }, "halal (no friture)": { "mappings": { "0": { @@ -1891,7 +1982,8 @@ "3": { "then": "Es gibt ausschließlich halal Speisen" } - } + }, + "question": "Gibt es im das Restaurant halal Speisen?" } }, "title": { @@ -1903,8 +1995,7 @@ "then": "Schnellrestaurant{name}" } } - }, - "name": "Restaurants und Fast Food" + } }, "ghost_bike": { "name": "Geisterräder", @@ -2004,6 +2095,9 @@ }, "nature_reserve": { "tagRenderings": { + "Curator": { + "question": "Wer ist der Verwalter dieses Naturschutzgebietes?
Respektieren Sie die Privatsphäre - geben Sie nur dann einen Namen an, wenn dieser allgemein bekannt ist" + }, "Dogs?": { "mappings": { "0": { @@ -2021,6 +2115,9 @@ "Email": { "render": "{email}" }, + "Surface area": { + "render": "Grundfläche: {_surface:ha}ha" + }, "Website": { "question": "Auf welcher Webseite kann man mehr Informationen über dieses Naturschutzgebiet finden?" }, @@ -2347,13 +2444,26 @@ } }, "shops": { + "description": "Ein Geschäft", + "name": "Geschäft", "presets": { "0": { - "description": "Ein neues Geschäft hinzufügen" + "description": "Ein neues Geschäft hinzufügen", + "title": "Geschäft" } }, "tagRenderings": { + "shops-email": { + "question": "Wie ist die Email-Adresse dieses Geschäfts?" + }, + "shops-name": { + "question": "Wie ist der Name dieses Geschäfts?" + }, + "shops-opening_hours": { + "question": "Wie sind die Öffnungszeiten dieses Geschäfts?" + }, "shops-phone": { + "question": "Wie ist die Telefonnummer?", "render": "{phone}" }, "shops-shop": { @@ -2398,9 +2508,40 @@ } }, "render": "Geschäft" - }, - "name": "Geschäft", - "description": "Ein Geschäft" + } + }, + "slow_roads": { + "tagRenderings": { + "slow_roads-surface": { + "mappings": { + "0": { + "then": "Die Oberfläche ist Gras" + }, + "1": { + "then": "Die Oberfläche ist Erde" + }, + "2": { + "then": "Die Oberfläche ist ohne festen Belag" + }, + "3": { + "then": "Die Oberfläche ist Sand" + }, + "4": { + "then": "Die Oberfläche ist aus Pflastersteinen" + }, + "5": { + "then": "Die Oberfläche ist Asphalt" + }, + "6": { + "then": "Die Oberfläche ist Beton" + }, + "7": { + "then": "Die Oberfläche ist gepflastert" + } + }, + "render": "Die Oberfläche ist {surface}" + } + } }, "sport_pitch": { "description": "Ein Sportplatz", @@ -2520,7 +2661,8 @@ "2": { "then": "Diese Kamera ist möglicherweise im Freien" } - } + }, + "question": "Handelt es sich bei dem von dieser Kamera überwachten öffentlichen Raum um einen Innen- oder Außenbereich?" }, "Level": { "question": "Auf welcher Ebene befindet sich diese Kamera?", @@ -2530,6 +2672,17 @@ "question": "Wer betreibt diese CCTV Kamera?", "render": "Betrieben von {operator}" }, + "Surveillance type: public, outdoor, indoor": { + "mappings": { + "1": { + "then": "Ein privater Außenbereich wird überwacht (z. B. ein Parkplatz, eine Tankstelle, ein Innenhof, ein Eingang, eine private Einfahrt, ...)" + }, + "2": { + "then": "Ein privater Innenbereich wird überwacht, z. B. ein Geschäft, eine private Tiefgarage, ..." + } + }, + "question": "Um was für eine Überwachungskamera handelt es sich" + }, "Surveillance:zone": { "mappings": { "0": { @@ -2555,8 +2708,22 @@ "render": " Überwacht ein/e {surveillance:zone}" }, "camera:mount": { + "mappings": { + "0": { + "then": "Diese Kamera ist an einer Wand montiert" + }, + "1": { + "then": "Diese Kamera ist an einer Stange montiert" + }, + "2": { + "then": "Diese Kamera ist an der Decke montiert" + } + }, "question": "Wie ist diese Kamera montiert?", "render": "Montageart: {mount}" + }, + "direction. We don't ask this for a dome on a pole or ceiling as it has a 360° view": { + "question": "In welche Himmelsrichtung ist diese Kamera ausgerichtet?" } }, "title": { @@ -2722,12 +2889,38 @@ "tagRenderings": { "tree-decidouous": { "mappings": { + "0": { + "then": "Laubabwerfend: Der Baum verliert für eine gewisse Zeit des Jahres seine Blätter." + }, "1": { "then": "immergrüner Baum." } }, "question": "Ist dies ein Nadelbaum oder ein Laubbaum?" }, + "tree-denotation": { + "mappings": { + "0": { + "then": "Der Baum ist aufgrund seiner Größe oder seiner markanten Lage bedeutsam. Er ist nützlich zur Orientierung." + }, + "1": { + "then": "Der Baum ist ein Naturdenkmal, z. B. weil er besonders alt ist oder zu einer wertvollen Art gehört." + }, + "2": { + "then": "Der Baum wird für landwirtschaftliche Zwecke genutzt, z. B. in einer Obstplantage." + }, + "3": { + "then": "Der Baum steht in einem Park oder ähnlichem (Friedhof, Schulgelände, ...)." + }, + "5": { + "then": "Dieser Baum steht entlang einer Straße." + }, + "7": { + "then": "Dieser Baum steht außerhalb eines städtischen Gebiets." + } + }, + "question": "Wie bedeutsam ist dieser Baum? Wählen Sie die erste Antwort, die zutrifft." + }, "tree-height": { "mappings": { "0": { @@ -2738,6 +2931,12 @@ }, "tree-heritage": { "mappings": { + "0": { + "then": "\"\"/ Als Denkmal registriert von der Onroerend Erfgoed Flandern" + }, + "1": { + "then": "Als Denkmal registriert von der Direction du Patrimoine culturel Brüssel" + }, "3": { "then": "Nicht als Denkmal registriert" } @@ -2755,7 +2954,8 @@ "2": { "then": "\"\"/ Dauerhaft blattlos" } - } + }, + "question": "Ist dies ein Laub- oder Nadelbaum?" }, "tree_node-name": { "mappings": { @@ -2765,6 +2965,12 @@ }, "question": "Hat der Baum einen Namen?", "render": "Name: {name}" + }, + "tree_node-ref:OnroerendErfgoed": { + "question": "Wie lautet die Kennung der Onroerend Erfgoed Flanders?" + }, + "tree_node-wikidata": { + "question": "Was ist das passende Wikidata Element zu diesem Baum?" } }, "title": { @@ -2794,6 +3000,7 @@ } }, "visitor_information_centre": { + "description": "Ein Besucherzentrum bietet Informationen über eine bestimmte Attraktion oder Sehenswürdigkeit, an der es sich befindet.", "name": "Besucherinformationszentrum", "title": { "mappings": { @@ -2802,8 +3009,7 @@ } }, "render": "{name}" - }, - "description": "Ein Besucherzentrum bietet Informationen über eine bestimmte Attraktion oder Sehenswürdigkeit, an der es sich befindet." + } }, "waste_basket": { "description": "Dies ist ein öffentlicher Abfalleimer, in den Sie Ihren Müll entsorgen können.", @@ -2814,6 +3020,17 @@ } } }, + "mapRendering": { + "0": { + "iconSize": { + "mappings": { + "0": { + "then": "Abfalleimer" + } + } + } + } + }, "name": "Abfalleimer", "presets": { "0": { @@ -2821,6 +3038,20 @@ } }, "tagRenderings": { + "dispensing_dog_bags": { + "mappings": { + "0": { + "then": "Dieser Abfalleimer verfügt über einen Spender für (Hunde-)Kotbeutel" + }, + "1": { + "then": "Dieser Abfalleimer hat keinen Spender für (Hunde-)Kotbeutel" + }, + "2": { + "then": "Dieser Abfalleimer hat keinen Spender für (Hunde-)Kotbeutel" + } + }, + "question": "Verfügt dieser Abfalleimer über einen Spender für (Hunde-)Kotbeutel?" + }, "waste-basket-waste-types": { "mappings": { "0": { @@ -2838,22 +3069,12 @@ "4": { "then": "Mülleimer für Drogen" } - } + }, + "question": "Um was für einen Abfalleimer handelt es sich?" } }, "title": { "render": "Abfalleimer" - }, - "mapRendering": { - "0": { - "iconSize": { - "mappings": { - "0": { - "then": "Abfalleimer" - } - } - } - } } }, "watermill": { diff --git a/langs/layers/en.json b/langs/layers/en.json index 1d74494a0..280ecf940 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -2929,6 +2929,154 @@ "render": "Sport pitch" } }, + "street_lamps": { + "name": "Street Lamps", + "presets": { + "0": { + "title": "street lamp" + } + }, + "tagRenderings": { + "colour": { + "mappings": { + "0": { + "then": "This lamp emits white light" + }, + "1": { + "then": "This lamp emits green light" + }, + "2": { + "then": "This lamp emits orange light" + } + }, + "question": "What colour light does this lamp emit?", + "render": "This lamp emits {light:colour} light" + }, + "count": { + "mappings": { + "0": { + "then": "This lamp has 1 fixture" + }, + "1": { + "then": "This lamp has 2 fixtures" + } + }, + "question": "How many fixtures does this light have?", + "render": "This lamp has {light:count} fixtures" + }, + "direction": { + "question": "Where does this lamp point to?", + "render": "This lamp points towards {light:direction}" + }, + "lamp_mount": { + "mappings": { + "0": { + "then": "This lamp sits atop of a straight mast" + }, + "1": { + "then": "This lamp sits at the end of a bent mast" + } + }, + "question": "How is this lamp mounted to the pole?" + }, + "lit": { + "mappings": { + "0": { + "then": "This lamp is lit at night" + }, + "1": { + "then": "This lamp is lit 24/7" + }, + "2": { + "then": "This lamp is lit based on motion" + }, + "3": { + "then": "This lamp is lit based on demand (e.g. with a pushbutton)" + } + }, + "question": "When is this lamp lit?" + }, + "method": { + "mappings": { + "0": { + "then": "This lamp is lit electrically" + }, + "1": { + "then": "This lamp uses LEDs" + }, + "2": { + "then": "This lamp uses incandescent lighting" + }, + "3": { + "then": "This lamp uses halogen lighting" + }, + "4": { + "then": "This lamp uses discharge lamps (unknown type)" + }, + "5": { + "then": "This lamp uses a mercury-vapour lamp (lightly blueish)" + }, + "6": { + "then": "This lamp uses metal-halide lamps (bright white)" + }, + "7": { + "then": "This lamp uses fluorescent lighting" + }, + "8": { + "then": "This lamp uses sodium lamps (unknown type)" + }, + "9": { + "then": "This lamp uses low pressure sodium lamps (monochrome orange)" + }, + "10": { + "then": "This lamp uses high pressure sodium lamps (orange with white)" + }, + "11": { + "then": "This lamp is lit using gas" + } + }, + "question": "What kind of lighting does this lamp use?" + }, + "ref": { + "question": "What is the reference number of this street lamp?", + "render": "This street lamp has the reference number {ref}" + }, + "support": { + "mappings": { + "0": { + "then": "This lamp is suspended using cables" + }, + "1": { + "then": "This lamp is mounted on a ceiling" + }, + "2": { + "then": "This lamp is mounted in the ground" + }, + "3": { + "then": "This lamp is mounted on a short pole (mostly < 1.5m)" + }, + "4": { + "then": "This lamp is mounted on a pole" + }, + "5": { + "then": "This lamp is mounted directly to the wall" + }, + "6": { + "then": "This lamp is mounted to the wall using a metal bar" + } + }, + "question": "How is this street lamp mounted?" + } + }, + "title": { + "mappings": { + "0": { + "then": "Street Lamp {ref}" + } + }, + "render": "Street Lamp" + } + }, "surveillance_camera": { "name": "Surveillance camera's", "tagRenderings": { @@ -3444,153 +3592,5 @@ }, "watermill": { "name": "Watermill" - }, - "street_lamps": { - "name": "Street Lamps", - "presets": { - "0": { - "title": "street lamp" - } - }, - "tagRenderings": { - "colour": { - "mappings": { - "0": { - "then": "This lamp emits white light" - }, - "1": { - "then": "This lamp emits green light" - }, - "2": { - "then": "This lamp emits orange light" - } - }, - "question": "What colour light does this lamp emit?", - "render": "This lamp emits {light:colour} light" - }, - "count": { - "mappings": { - "0": { - "then": "This lamp has 1 fixture" - }, - "1": { - "then": "This lamp has 2 fixtures" - } - }, - "question": "How many fixtures does this light have?", - "render": "This lamp has {light:count} fixtures" - }, - "direction": { - "question": "Where does this lamp point to?", - "render": "This lamp points towards {light:direction}" - }, - "lamp_mount": { - "mappings": { - "0": { - "then": "This lamp sits atop of a straight mast" - }, - "1": { - "then": "This lamp sits at the end of a bent mast" - } - }, - "question": "How is this lamp mounted to the pole?" - }, - "lit": { - "mappings": { - "0": { - "then": "This lamp is lit at night" - }, - "1": { - "then": "This lamp is lit 24/7" - }, - "2": { - "then": "This lamp is lit based on motion" - }, - "3": { - "then": "This lamp is lit based on demand (e.g. with a pushbutton)" - } - }, - "question": "When is this lamp lit?" - }, - "method": { - "mappings": { - "0": { - "then": "This lamp is lit electrically" - }, - "1": { - "then": "This lamp uses LEDs" - }, - "2": { - "then": "This lamp uses incandescent lighting" - }, - "3": { - "then": "This lamp uses halogen lighting" - }, - "4": { - "then": "This lamp uses discharge lamps (unknown type)" - }, - "5": { - "then": "This lamp uses a mercury-vapour lamp (lightly blueish)" - }, - "6": { - "then": "This lamp uses metal-halide lamps (bright white)" - }, - "7": { - "then": "This lamp uses fluorescent lighting" - }, - "8": { - "then": "This lamp uses sodium lamps (unknown type)" - }, - "9": { - "then": "This lamp uses low pressure sodium lamps (monochrome orange)" - }, - "10": { - "then": "This lamp uses high pressure sodium lamps (orange with white)" - }, - "11": { - "then": "This lamp is lit using gas" - } - }, - "question": "What kind of lighting does this lamp use?" - }, - "ref": { - "question": "What is the reference number of this street lamp?", - "render": "This street lamp has the reference number {ref}" - }, - "support": { - "mappings": { - "0": { - "then": "This lamp is suspended using cables" - }, - "1": { - "then": "This lamp is mounted on a ceiling" - }, - "2": { - "then": "This lamp is mounted in the ground" - }, - "3": { - "then": "This lamp is mounted on a short pole (mostly < 1.5m)" - }, - "4": { - "then": "This lamp is mounted on a pole" - }, - "5": { - "then": "This lamp is mounted directly to the wall" - }, - "6": { - "then": "This lamp is mounted to the wall using a metal bar" - } - }, - "question": "How is this street lamp mounted?" - } - }, - "title": { - "mappings": { - "0": { - "then": "Street Lamp {ref}" - } - }, - "render": "Street Lamp" - } } } \ No newline at end of file diff --git a/langs/layers/fr.json b/langs/layers/fr.json index 8ff8c41bb..90d41cf41 100644 --- a/langs/layers/fr.json +++ b/langs/layers/fr.json @@ -746,6 +746,13 @@ } }, "defibrillator": { + "icon": { + "mappings": { + "0": { + "then": "./assets/layers/defibrillator/aed_checked.svg" + } + } + }, "name": "Défibrillateurs", "presets": { "0": { @@ -862,13 +869,6 @@ }, "title": { "render": "Défibrillateur" - }, - "icon": { - "mappings": { - "0": { - "then": "./assets/layers/defibrillator/aed_checked.svg" - } - } } }, "direction": { diff --git a/langs/layers/it.json b/langs/layers/it.json index c16d50cd2..48c8dda42 100644 --- a/langs/layers/it.json +++ b/langs/layers/it.json @@ -757,6 +757,13 @@ } }, "defibrillator": { + "icon": { + "mappings": { + "0": { + "then": "./assets/layers/defibrillator/aed_checked.svg" + } + } + }, "name": "Defibrillatori", "presets": { "0": { @@ -873,13 +880,6 @@ }, "title": { "render": "Defibrillatore" - }, - "icon": { - "mappings": { - "0": { - "then": "./assets/layers/defibrillator/aed_checked.svg" - } - } } }, "direction": { @@ -1319,6 +1319,17 @@ "render": "Microbiblioteca" } }, + "shops": { + "tagRenderings": { + "shops-shop": { + "mappings": { + "5": { + "then": "Autofficina" + } + } + } + } + }, "slow_roads": { "tagRenderings": { "slow_roads-surface": { @@ -1821,16 +1832,5 @@ "title": { "render": "Punto panoramico" } - }, - "shops": { - "tagRenderings": { - "shops-shop": { - "mappings": { - "5": { - "then": "Autofficina" - } - } - } - } } } \ No newline at end of file diff --git a/langs/layers/nb_NO.json b/langs/layers/nb_NO.json index 481b13462..e66382ae2 100644 --- a/langs/layers/nb_NO.json +++ b/langs/layers/nb_NO.json @@ -63,12 +63,12 @@ } }, "title": { - "render": "Kunstverk", "mappings": { "0": { "then": "Kunstverk {name}" } - } + }, + "render": "Kunstverk" } }, "bench": { diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 1c6c5bb61..0c2a569f7 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -3313,6 +3313,154 @@ "render": "Sportterrein" } }, + "street_lamps": { + "name": "Straatlantaarns", + "presets": { + "0": { + "title": "straatlantaarn" + } + }, + "tagRenderings": { + "colour": { + "mappings": { + "0": { + "then": "Deze lantaarn geeft wit licht" + }, + "1": { + "then": "Deze lantaarn geeft groen licht" + }, + "2": { + "then": "Deze lantaarn geeft oranje licht" + } + }, + "question": "Wat voor kleur licht geeft deze lantaarn?", + "render": "Deze lantaarn geeft {light:colour} licht" + }, + "count": { + "mappings": { + "0": { + "then": "Deze lantaarn heeft 1 lamp" + }, + "1": { + "then": "Deze lantaarn heeft 2 lampen" + } + }, + "question": "Hoeveel lampen heeft deze lantaarn?", + "render": "Deze lantaarn heeft {light:count} lampen" + }, + "direction": { + "question": "Waar is deze lamp heengericht?", + "render": "Deze lantaarn is gericht naar {light:direction}" + }, + "lamp_mount": { + "mappings": { + "0": { + "then": "Deze lantaarn zit boven op een rechte paal" + }, + "1": { + "then": "Deze lantaarn zit aan het eind van een gebogen paal" + } + }, + "question": "Hoe zit deze lantaarn aan de paal?" + }, + "lit": { + "mappings": { + "0": { + "then": "Deze lantaarn is 's nachts verlicht" + }, + "1": { + "then": "Deze lantaarn is 24/7 verlicht" + }, + "2": { + "then": "Deze lantaarn is verlicht op basis van beweging" + }, + "3": { + "then": "Deze lantaarn is verlicht op verzoek (bijv. met een drukknop)" + } + }, + "question": "Wanneer is deze lantaarn verlicht?" + }, + "method": { + "mappings": { + "0": { + "then": "Deze lantaarn is elektrisch verlicht" + }, + "1": { + "then": "Deze lantaarn gebruikt LEDs" + }, + "2": { + "then": "Deze lantaarn gebruikt gloeilampen" + }, + "3": { + "then": "Deze lantaarn gebruikt halogeen verlichting" + }, + "4": { + "then": "Deze lantaarn gebruikt gasontladingslampen (onbekend type)" + }, + "5": { + "then": "Deze lantaarn gebruikt een kwiklamp (enigszins blauwachtig)" + }, + "6": { + "then": "Deze lantaarn gebruikt metaalhalidelampen" + }, + "7": { + "then": "Deze lantaarn gebruikt fluorescentieverlichting (TL en spaarlamp)" + }, + "8": { + "then": "Deze lantaarn gebruikt natriumlampen (onbekend type)" + }, + "9": { + "then": "Deze lantaarn gebruikt lagedruknatriumlampen (monochroom oranje)" + }, + "10": { + "then": "Deze lantaarn gebruikt hogedruknatriumlampen (oranje met wit)" + }, + "11": { + "then": "Deze lantaarn wordt verlicht met gas" + } + }, + "question": "Wat voor verlichting gebruikt deze lantaarn?" + }, + "ref": { + "question": "Wat is het nummer van deze straatlantaarn?", + "render": "Deze straatlantaarn heeft het nummer {ref}" + }, + "support": { + "mappings": { + "0": { + "then": "Deze lantaarn hangt aan kabels" + }, + "1": { + "then": "Deze lantaarn hangt aan een plafond" + }, + "2": { + "then": "Deze lantaarn zit in de grond" + }, + "3": { + "then": "Deze lantaarn zit op een korte paal (meestal < 1.5m)" + }, + "4": { + "then": "Deze lantaarn zit op een paal" + }, + "5": { + "then": "Deze lantaarn hangt direct aan de muur" + }, + "6": { + "then": "Deze lantaarn hangt aan de muur met een metalen balk" + } + }, + "question": "Hoe is deze straatlantaarn gemonteerd?" + } + }, + "title": { + "mappings": { + "0": { + "then": "Straatlantaarn {ref}" + } + }, + "render": "Straatlantaarn" + } + }, "surveillance_camera": { "name": "Bewakingscamera's", "tagRenderings": { @@ -3933,153 +4081,5 @@ }, "render": "Watermolens" } - }, - "street_lamps": { - "name": "Straatlantaarns", - "presets": { - "0": { - "title": "straatlantaarn" - } - }, - "tagRenderings": { - "colour": { - "mappings": { - "0": { - "then": "Deze lantaarn geeft wit licht" - }, - "1": { - "then": "Deze lantaarn geeft groen licht" - }, - "2": { - "then": "Deze lantaarn geeft oranje licht" - } - }, - "question": "Wat voor kleur licht geeft deze lantaarn?", - "render": "Deze lantaarn geeft {light:colour} licht" - }, - "count": { - "mappings": { - "0": { - "then": "Deze lantaarn heeft 1 lamp" - }, - "1": { - "then": "Deze lantaarn heeft 2 lampen" - } - }, - "question": "Hoeveel lampen heeft deze lantaarn?", - "render": "Deze lantaarn heeft {light:count} lampen" - }, - "direction": { - "question": "Waar is deze lamp heengericht?", - "render": "Deze lantaarn is gericht naar {light:direction}" - }, - "lamp_mount": { - "mappings": { - "0": { - "then": "Deze lantaarn zit boven op een rechte paal" - }, - "1": { - "then": "Deze lantaarn zit aan het eind van een gebogen paal" - } - }, - "question": "Hoe zit deze lantaarn aan de paal?" - }, - "lit": { - "mappings": { - "0": { - "then": "Deze lantaarn is 's nachts verlicht" - }, - "1": { - "then": "Deze lantaarn is 24/7 verlicht" - }, - "2": { - "then": "Deze lantaarn is verlicht op basis van beweging" - }, - "3": { - "then": "Deze lantaarn is verlicht op verzoek (bijv. met een drukknop)" - } - }, - "question": "Wanneer is deze lantaarn verlicht?" - }, - "method": { - "mappings": { - "0": { - "then": "Deze lantaarn is elektrisch verlicht" - }, - "1": { - "then": "Deze lantaarn gebruikt LEDs" - }, - "2": { - "then": "Deze lantaarn gebruikt gloeilampen" - }, - "3": { - "then": "Deze lantaarn gebruikt halogeen verlichting" - }, - "4": { - "then": "Deze lantaarn gebruikt gasontladingslampen (onbekend type)" - }, - "5": { - "then": "Deze lantaarn gebruikt een kwiklamp (enigszins blauwachtig)" - }, - "6": { - "then": "Deze lantaarn gebruikt metaalhalidelampen" - }, - "7": { - "then": "Deze lantaarn gebruikt fluorescentieverlichting (TL en spaarlamp)" - }, - "8": { - "then": "Deze lantaarn gebruikt natriumlampen (onbekend type)" - }, - "9": { - "then": "Deze lantaarn gebruikt lagedruknatriumlampen (monochroom oranje)" - }, - "10": { - "then": "Deze lantaarn gebruikt hogedruknatriumlampen (oranje met wit)" - }, - "11": { - "then": "Deze lantaarn wordt verlicht met gas" - } - }, - "question": "Wat voor verlichting gebruikt deze lantaarn?" - }, - "ref": { - "question": "Wat is het nummer van deze straatlantaarn?", - "render": "Deze straatlantaarn heeft het nummer {ref}" - }, - "support": { - "mappings": { - "0": { - "then": "Deze lantaarn hangt aan kabels" - }, - "1": { - "then": "Deze lantaarn hangt aan een plafond" - }, - "2": { - "then": "Deze lantaarn zit in de grond" - }, - "3": { - "then": "Deze lantaarn zit op een korte paal (meestal < 1.5m)" - }, - "4": { - "then": "Deze lantaarn zit op een paal" - }, - "5": { - "then": "Deze lantaarn hangt direct aan de muur" - }, - "6": { - "then": "Deze lantaarn hangt aan de muur met een metalen balk" - } - }, - "question": "Hoe is deze straatlantaarn gemonteerd?" - } - }, - "title": { - "mappings": { - "0": { - "then": "Straatlantaarn {ref}" - } - }, - "render": "Straatlantaarn" - } } } \ No newline at end of file diff --git a/langs/layers/ru.json b/langs/layers/ru.json index cfb5eb162..8aede7391 100644 --- a/langs/layers/ru.json +++ b/langs/layers/ru.json @@ -707,6 +707,13 @@ } }, "defibrillator": { + "icon": { + "mappings": { + "0": { + "then": "./assets/layers/defibrillator/aed_checked.svg" + } + } + }, "name": "Дефибрилляторы", "presets": { "0": { @@ -754,13 +761,6 @@ }, "title": { "render": "Дефибриллятор" - }, - "icon": { - "mappings": { - "0": { - "then": "./assets/layers/defibrillator/aed_checked.svg" - } - } } }, "direction": { @@ -1441,15 +1441,6 @@ } } }, - "name": "Контейнер для мусора", - "presets": { - "0": { - "title": "Контейнер для мусора" - } - }, - "title": { - "render": "Контейнер для мусора" - }, "mapRendering": { "0": { "iconSize": { @@ -1460,6 +1451,15 @@ } } } + }, + "name": "Контейнер для мусора", + "presets": { + "0": { + "title": "Контейнер для мусора" + } + }, + "title": { + "render": "Контейнер для мусора" } }, "watermill": { diff --git a/langs/shared-questions/en.json b/langs/shared-questions/en.json index 0de90284a..22861f315 100644 --- a/langs/shared-questions/en.json +++ b/langs/shared-questions/en.json @@ -65,7 +65,7 @@ "wheelchair-access": { "mappings": { "0": { - "then": "This place is specially adapated for wheelchair users" + "then": "This place is specially adapted for wheelchair users" }, "1": { "then": "This place is easily reachable with a wheelchair" diff --git a/langs/shared-questions/it.json b/langs/shared-questions/it.json index f287d00f2..2b13ebb8b 100644 --- a/langs/shared-questions/it.json +++ b/langs/shared-questions/it.json @@ -3,18 +3,97 @@ "description": { "question": "C'è ancora qualche informazione importante che non è stato possibile fornire nelle domande precedenti? Aggiungila qui.
Non ripetere informazioni già fornite" }, + "dog-access": { + "mappings": { + "0": { + "then": "Cani ammessi" + }, + "1": { + "then": "I cani non sono ammessi" + }, + "2": { + "then": "Cani ammessi ma solo se tenuti al guinzaglio" + }, + "3": { + "then": "I cani sono ammessi e possono andare in giro liberamente" + } + }, + "question": "I cani sono ammessi in quest’attività?" + }, "email": { "question": "Qual è l'indirizzo email di {name}?" }, + "level": { + "mappings": { + "0": { + "then": "Si trova sotto il livello stradale" + }, + "1": { + "then": "Si trova al pianoterra" + }, + "2": { + "then": "Si trova al pianoterra" + }, + "3": { + "then": "Si trova al primo piano" + } + }, + "question": "A quale piano si trova questo elemento?", + "render": "Si trova al piano numero {level}" + }, "opening_hours": { "question": "Quali sono gli orari di apertura di {name}?", "render": "

Orari di apertura

{opening_hours_table(opening_hours)}" }, + "payment-options": { + "mappings": { + "0": { + "then": "I contanti sono accettati" + }, + "1": { + "then": "I pagamenti con la carta sono accettati" + } + }, + "question": "Quali metodi di pagamento sono accettati qui?" + }, "phone": { "question": "Qual è il numero di telefono di {name}?" }, "website": { "question": "Qual è il sito web di {name}?" + }, + "wheelchair-access": { + "mappings": { + "0": { + "then": "Questo luogo è stato adattato per favorire le persone in sedia a rotelle" + }, + "1": { + "then": "Questo luogo è facilmente raggiungibile con una sedia a rotelle" + }, + "2": { + "then": "È possibile raggiungere questo luogo con una sedia a rotella ma non è semplice" + }, + "3": { + "then": "Questo luogo non è accessibile con una sedia a rotelle" + } + }, + "question": "Questo luogo è accessibile con una sedia a rotelle?" + }, + "wikipedia": { + "mappings": { + "0": { + "then": "Nessuna pagina Wikipedia è ancora stata collegata" + } + }, + "question": "Qual è l’elemento Wikidata corrispondente?" + }, + "wikipedialink": { + "mappings": { + "0": { + "then": "Non collegato a Wikipedia" + } + }, + "question": "Qual è il corrispondente elemento su Wikipedia?" } } } \ No newline at end of file diff --git a/langs/shared-questions/nb_NO.json b/langs/shared-questions/nb_NO.json index a8241aecf..1dfffd0e3 100644 --- a/langs/shared-questions/nb_NO.json +++ b/langs/shared-questions/nb_NO.json @@ -3,18 +3,95 @@ "description": { "question": "Er det noe mer som er relevant du ikke kunne opplyse om i tidligere svar? Legg det til her.
Ikke gjenta fakta som allerede er nevnt" }, + "dog-access": { + "mappings": { + "0": { + "then": "Hunder tillates" + }, + "1": { + "then": "Hunder tillates ikke" + }, + "2": { + "then": "Hunder tillates, men de må være i bånd" + }, + "3": { + "then": "Hunder tillates og kan gå fritt" + } + }, + "question": "Tillates hunder i denne forretningen?" + }, "email": { "question": "Hva er e-postadressen til {name}?" }, + "level": { + "mappings": { + "0": { + "then": "Under bakken" + }, + "1": { + "then": "På gateplan" + }, + "2": { + "then": "På gateplan" + }, + "3": { + "then": "I andre etasje" + } + } + }, "opening_hours": { "question": "Hva er åpningstidene for {name})", "render": "

Åpningstider

{opening_hours_table(opening_hours)}" }, + "payment-options": { + "mappings": { + "0": { + "then": "Kontanter godtas her" + }, + "1": { + "then": "Betalingskort godtas her" + } + }, + "question": "Hvilke betalingsmetoder godtas her?" + }, "phone": { "question": "Hva er telefonnummeret til {name}?" }, "website": { "question": "Hva er nettsiden til {name}?" + }, + "wheelchair-access": { + "mappings": { + "0": { + "then": "Dette stedet er spesielt tilpasset rullestolsbrukere" + }, + "1": { + "then": "Dette stedet kan enkelt besøkes med rullestol" + }, + "2": { + "then": "Det er mulig å besøke dette stedet i rullestol, men det er ikke lett" + }, + "3": { + "then": "Dette stedet er ikke tilgjengelig for besøk med rullestol" + } + }, + "question": "Er dette stedet tilgjengelig for rullestoler?" + }, + "wikipedia": { + "mappings": { + "0": { + "then": "Ingen Wikipedia-side lenket enda" + } + }, + "question": "Hva er respektivt Wikipedia-element?" + }, + "wikipedialink": { + "mappings": { + "0": { + "then": "Ikke lenket med Wikipedia" + } + }, + "question": "Hva er respektivt element på Wikipedia?" } } } \ No newline at end of file diff --git a/langs/themes/de.json b/langs/themes/de.json index d60d26ff8..f375807f5 100644 --- a/langs/themes/de.json +++ b/langs/themes/de.json @@ -318,6 +318,18 @@ } }, "tagRenderings": { + "Bolts": { + "mappings": { + "0": { + "then": "Auf dieser Kletterroute sind keine Haken vorhanden" + }, + "1": { + "then": "Auf dieser Kletterroute sind keine Haken vorhanden" + } + }, + "question": "Wie viele Haken gibt es auf dieser Kletterroute bevor der Umlenker bzw. Standhaken erreicht ist?", + "render": "Diese Kletterroute hat {climbing:bolts} Haken" + }, "Difficulty": { "question": "Wie hoch ist der Schwierigkeitsgrad dieser Kletterroute nach dem französisch/belgischen System?", "render": "Die Schwierigkeit ist {climbing:grade:french} entsprechend des französisch/belgischen Systems" @@ -650,6 +662,7 @@ "title": "Open Etymology Map" }, "facadegardens": { + "description": "Fassadengärten, grüne Fassaden und Bäume in der Stadt bringen nicht nur Ruhe und Frieden, sondern auch eine schönere Stadt, eine größere Artenvielfalt, einen Kühleffekt und eine bessere Luftqualität.
Klimaan VZW und Mechelen Klimaatneutraal wollen bestehende und neue Fassadengärten als Beispiel für Menschen, die ihren eigenen Garten anlegen wollen, oder für naturverbundene Stadtspaziergänger kartieren.
Mehr Informationen über das Projekt unter klimaan.be.", "layers": { "0": { "description": "Fassadengärten", @@ -822,17 +835,6 @@ } }, "render": "Hackerspace" - }, - "mapRendering": { - "0": { - "icon": { - "mappings": { - "0": { - "then": "./assets/themes/hackerspaces/led.png" - } - } - } - } } } }, @@ -840,7 +842,7 @@ "title": "Hackerspaces" }, "hailhydrant": { - "description": "Auf dieser Karte können Sie Hydranten, Feuerwachen, Krankenwagen und Feuerlöscher in Ihren bevorzugten Stadtvierteln finden und aktualisieren.\n\nSie können Ihren genauen Standort verfolgen (nur mobil) und in der unteren linken Ecke die für Sie relevanten Ebenen auswählen. Sie können mit diesem Tool auch Pins (Points of Interest) zur Karte hinzufügen oder bearbeiten und durch die Beantwortung verfügbarer Fragen zusätzliche Angaben machen.\n\nAlle von Ihnen vorgenommenen Änderungen werden automatisch in der globalen Datenbank von OpenStreetMap gespeichert und können von anderen frei weiterverwendet werden.", + "description": "Auf dieser Karte können Sie Hydranten, Feuerwachen, Krankenwagen und Feuerlöscher in Ihren bevorzugten Stadtvierteln finden und aktualisieren. \n\nSie können Ihren genauen Standort verfolgen (nur mobil) und in der unteren linken Ecke die für Sie relevanten Ebenen auswählen. Sie können mit diesem Tool auch Pins (Points of Interest) zur Karte hinzufügen oder bearbeiten und durch die Beantwortung verfügbarer Fragen zusätzliche Angaben machen. \n\nAlle von Ihnen vorgenommenen Änderungen werden automatisch in der globalen Datenbank von OpenStreetMap gespeichert und können von anderen frei weiterverwendet werden.", "layers": { "0": { "description": "Kartenebene zur Anzeige von Hydranten.", @@ -948,6 +950,9 @@ "station-name": { "question": "Wie lautet der Name dieser Feuerwache?" } + }, + "title": { + "render": "Feuerwache" } }, "3": { @@ -957,12 +962,16 @@ } } } - } + }, + "shortDescription": "Hydranten, Feuerlöscher, Feuerwachen und Rettungswachen." }, "maps": { + "description": "Auf dieser Karte findest du alle Karten, die OpenStreetMap kennt - typischerweise eine große Karte auf einer Informationstafel, die das Gebiet, die Stadt oder die Region zeigt, z.B. eine touristische Karte auf der Rückseite einer Plakatwand, eine Karte eines Naturschutzgebietes, eine Karte der Radwegenetze in der Region, ...)

Wenn eine Karte fehlt, können Sie diese leicht auf OpenStreetMap kartieren.", + "shortDescription": "Dieses Thema zeigt alle (touristischen) Karten, die OpenStreetMap kennt", "title": "Eine Karte der Karten" }, "natuurpunt": { + "description": "Auf dieser Karte können Sie alle Naturschutzgebiete von Natuurpunt finden ", "shortDescription": "Diese Karte zeigt Naturschutzgebiete des flämischen Naturverbands Natuurpunt", "title": "Naturschutzgebiete" }, @@ -973,7 +982,45 @@ }, "openwindpowermap": { "description": "Eine Karte zum Anzeigen und Bearbeiten von Windkraftanlagen.", - "title": "Freie Windenergie-Karte" + "layers": { + "0": { + "name": "Windrad", + "presets": { + "0": { + "title": "Windrad" + } + }, + "title": { + "render": "Windrad" + }, + "units": { + "0": { + "applicableUnits": { + "0": { + "human": " Megawatt" + }, + "1": { + "human": " Kilowatt" + }, + "2": { + "human": " Watt" + }, + "3": { + "human": " Gigawatt" + } + } + }, + "1": { + "applicableUnits": { + "0": { + "human": " Meter" + } + } + } + } + } + }, + "title": "OpenWindPowerMap" }, "parkings": { "description": "Diese Karte zeigt Parkplätze", @@ -981,17 +1028,45 @@ "title": "Parken" }, "personal": { - "description": "Erstellen Sie ein persönliches Thema, das auf allen verfügbaren Ebenen aller Themen basiert. Um Daten anzuzeigen, öffnen Sie die Ebenenauswahl", + "description": "Erstellen Sie ein persönliches Thema auf der Grundlage aller verfügbaren Ebenen aller Themen", "title": "Persönliches Thema" }, "playgrounds": { + "description": "Auf dieser Karte finden Sie Spielplätze und können weitere Informationen hinzufügen", "shortDescription": "Eine Karte mit Spielplätzen", - "title": "Spielplätze" + "title": "Spielpläzte" }, "postboxes": { "layers": { + "0": { + "description": "Die Ebene zeigt Briefkästen.", + "name": "Brieflästen", + "presets": { + "0": { + "title": "Briefkasten" + } + }, + "title": { + "render": "Briefkasten" + } + }, "1": { "description": "Eine Ebene mit Postämtern.", + "filter": { + "0": { + "options": { + "0": { + "question": "Aktuell geöffnet" + } + } + } + }, + "name": "Poststellen", + "presets": { + "0": { + "title": "Poststelle" + } + }, "tagRenderings": { "OH": { "mappings": { @@ -1000,6 +1075,9 @@ } } } + }, + "title": { + "render": "Poststelle" } } }, @@ -1007,6 +1085,7 @@ "title": "Karte mit Briefkästen und Poststellen" }, "shops": { + "description": "Auf dieser Karte kann man grundlegende Informationen über Geschäfte markieren, Öffnungszeiten und Telefonnummern hinzufügen", "shortDescription": "Eine bearbeitbare Karte mit grundlegenden Geschäftsinformationen", "title": "Freie Geschäftskarte" }, diff --git a/langs/themes/en.json b/langs/themes/en.json index 1e4fdb232..cedade2d0 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -816,17 +816,6 @@ } } }, - "mapRendering": { - "0": { - "icon": { - "mappings": { - "0": { - "then": "./assets/themes/hackerspaces/led.png" - } - } - } - } - }, "name": "Hackerspace", "presets": { "0": { @@ -893,7 +882,7 @@ "title": "Hackerspaces" }, "hailhydrant": { - "description": "On this map you can find and update hydrants, fire stations, ambulance stations, and extinguishers in your favorite neighborhoods. \n\nYou can track your precise location (mobile only) and select layers that are relevant for you in the bottom left corner. You can also use this tool to add or edit pins (points of interest) to the map and provide additional details by answering available questions. \n\nAll changes you make will automatically be saved in the global database of OpenStreetMap and can be freely re-used by others.", + "description": "On this map you can find and update hydrants, fire stations, ambulance stations, and extinguishers in your favorite neighborhoods.\n\nYou can track your precise location (mobile only) and select layers that are relevant for you in the bottom left corner. You can also use this tool to add or edit pins (points of interest) to the map and provide additional details by answering available questions.\n\nAll changes you make will automatically be saved in the global database of OpenStreetMap and can be freely re-used by others.", "layers": { "0": { "description": "Map layer to show fire hydrants.", @@ -1273,6 +1262,62 @@ "shortDescription": "A map showing sport pitches", "title": "Sport pitches" }, + "street_lighting": { + "description": "On this map you can find everything about street lighting", + "layers": { + "1": { + "name": "Lit streets", + "tagRenderings": { + "lit": { + "mappings": { + "0": { + "then": "This street is lit" + }, + "1": { + "then": "This street is not lit" + }, + "2": { + "then": "This street is lit at night" + }, + "3": { + "then": "This street is lit 24/7" + } + }, + "question": "Is this street lit?" + } + }, + "title": { + "render": "Lit street" + } + }, + "2": { + "name": "All streets", + "tagRenderings": { + "lit": { + "mappings": { + "0": { + "then": "This street is lit" + }, + "1": { + "then": "This street is not lit" + }, + "2": { + "then": "This street is lit at night" + }, + "3": { + "then": "This street is lit 24/7" + } + }, + "question": "Is this street lit?" + } + }, + "title": { + "render": "Street" + } + } + }, + "title": "Street Lighting" + }, "surveillance": { "description": "On this open map, you can find surveillance cameras.", "shortDescription": "Surveillance cameras and other means of surveillance", @@ -1328,61 +1373,5 @@ "description": "On this map, you'll find waste baskets near you. If a waste basket is missing on this map, you can add it yourself", "shortDescription": "A map with waste baskets", "title": "Waste Basket" - }, - "street_lighting": { - "description": "On this map you can find everything about street lighting", - "layers": { - "1": { - "name": "Lit streets", - "tagRenderings": { - "lit": { - "mappings": { - "0": { - "then": "This street is lit" - }, - "1": { - "then": "This street is not lit" - }, - "2": { - "then": "This street is lit at night" - }, - "3": { - "then": "This street is lit 24/7" - } - }, - "question": "Is this street lit?" - } - }, - "title": { - "render": "Lit street" - } - }, - "2": { - "name": "All streets", - "tagRenderings": { - "lit": { - "mappings": { - "0": { - "then": "This street is lit" - }, - "1": { - "then": "This road is not lit" - }, - "2": { - "then": "This street is lit at night" - }, - "3": { - "then": "This street is lit 24/7" - } - }, - "question": "Is this street lit?" - } - }, - "title": { - "render": "Street" - } - } - }, - "title": "Street Lighting" } } \ No newline at end of file diff --git a/langs/themes/eo.json b/langs/themes/eo.json index e598b007f..4912597b3 100644 --- a/langs/themes/eo.json +++ b/langs/themes/eo.json @@ -39,81 +39,7 @@ "ghostbikes": { "title": "Fantombicikloj" }, - "hackerspaces": { - "layers": { - "0": { - "icon": { - "mappings": { - "0": { - "then": "./assets/themes/hackerspaces/led.png" - } - } - }, - "tagRenderings": { - "hackerspaces-opening_hours": { - "render": "{opening_hours_table()}" - } - }, - "title": { - "mappings": { - "0": { - "then": " {name}" - } - } - } - } - } - }, - "hailhydrant": { - "layers": { - "1": { - "tagRenderings": { - "extinguisher-location": { - "render": "Loko: {location}" - } - } - } - } - }, "maps": { "title": "Mapo de mapoj" - }, - "openwindpowermap": { - "layers": { - "0": { - "title": { - "mappings": { - "0": { - "then": "{name}" - } - } - }, - "units": { - "0": { - "applicableUnits": { - "0": { - "human": " megavatoj" - }, - "1": { - "human": " kilovatoj" - }, - "2": { - "human": " vatoj" - }, - "3": { - "human": " gigavatoj" - } - } - }, - "1": { - "applicableUnits": { - "0": { - "human": " metro" - } - } - } - } - } - } } } \ No newline at end of file diff --git a/langs/themes/it.json b/langs/themes/it.json index 2b71cfcff..32d90f489 100644 --- a/langs/themes/it.json +++ b/langs/themes/it.json @@ -16,10 +16,18 @@ "description": "«Biciclette in prestito» è un luogo dove le biciclette possono essere prese in prestito, spesso in cambio di un piccolo contributo annuale. Un caso degno di nota è quello delle biciclette in prestito per bambini che permettono loro di cambiare le dimensioni della propria bici quando quella attuale diventa troppo piccola", "title": "Biciclette in prestito" }, + "binoculars": { + "description": "Una cartina dei binocoli su un palo fissi in un luogo. Si trovano tipicamente nei luoghi turistici, nei belvedere, in cima a torri panoramiche oppure occasionalmente nelle riserve naturali.", + "shortDescription": "Una cartina dei binocoli pubblici fissi", + "title": "Binocoli" + }, "bookcases": { "description": "Una minibiblioteca è una piccola cabina a lato della strada, una scatola, una vecchia cabina telefonica o qualche altro contenitore che ospita libri. Tutti può lasciare o prendere un libro. Questa mappa punta a rappresentarle tutte. Puoi facilmente scoprire nuove minibiblioteche nelle tue vicinanze e, con un account gratuito su OpenStreetMap, puoi aggiungerne altre.", "title": "Mappa libera delle microbiblioteche" }, + "cafes_and_pubs": { + "title": "Caffè e pub" + }, "campersite": { "description": "Questo sito raccoglie tutti i luoghi ufficiali dove sostare con il camper e aree dove è possibile scaricare acque grigie e nere. Puoi aggiungere dettagli riguardanti i servizi forniti e il loro costo. Aggiungi foto e recensioni. Questo è al contempo un sito web e una web app. I dati sono memorizzati su OpenStreetMap in modo tale che siano per sempre liberi e riutilizzabili da qualsiasi app.", "layers": { @@ -253,6 +261,8 @@ "title": "Stazioni di ricarica" }, "climbing": { + "description": "In questa cartina puoi trovare vari luoghi per arrampicata come ad esempio palestre di arrampicata, sale di pratica e rocce naturali.", + "descriptionTail": "La cartina di arrampicata è stata originariamente creata da Christian Neumann. Si prega di scrivere qua se si hanno commenti o domande da fare.

Il progetto usa i dati del progetto OpenStreetMap.

", "layers": { "2": { "tagRenderings": { @@ -286,7 +296,17 @@ }, "title": "Mappa aperta per le arrampicate" }, + "cycle_highways": { + "description": "Questa cartina mostra le strade per velocipedi", + "title": "Strade per velocipedi" + }, + "cycle_infra": { + "description": "Una cartina dove vedere e modificare gli elementi riguardanti l’infrastruttura dei velocipedi. Realizzata durante #osoc21.", + "shortDescription": "Una cartina dove vedere e modificare gli elementi riguardanti l’infrastruttura dei velocipedi.", + "title": "Infrastruttura dei velocipedi" + }, "cyclestreets": { + "description": "Una strada ciclabile è una strada dove il traffico motorizzato non può superare i velocipedi. La sua presenza è segnalata da un cartello stradale specifico. Le strade ciclabili sono diffuse in Olanda e Belgio, ma si possono trovare anche in Germania e in Francia. ", "layers": { "0": { "description": "Una strada ciclabile è una strada in cui i veicoli a motore non possono sorpassare le persone in bicicletta", @@ -326,7 +346,9 @@ "render": "Questa strada diventerà una strada ciclabile dal {cyclestreet:start_date}" } } - } + }, + "shortDescription": "Una cartina per le strade ciclabili", + "title": "Strade ciclabili" }, "cyclofix": { "description": "Questa mappa offre a chi va in bici una soluzione semplice per trovare tutte le infrastrutture di cui ha bisogno.

Puoi tracciare la tua posizione esatta (solo su mobile) e selezionare i livelli che ti interessano nell'angolo in basso a sinistra. Puoi anche usare questo strumento per aggiungere o modificare punti di interesse alla mappa e aggiungere nuove informazioni rispendendo alle domande.

Tutte le modifiche che apporterai saranno automaticamente salvate nel database mondiale di OpenStreetMap e potranno essere liberamente riutilizzate da tutti e tutte.

Per maggiori informazioni sul progetto ciclofix, visita cyclofix.osm.be.", @@ -336,7 +358,25 @@ "description": "Questa mappa mostra tutti i luoghi in cui è disponibile acqua potabile ed è possibile aggiungerne di nuovi", "title": "Acqua potabile" }, + "etymology": { + "description": "Su questa cartina sono visibili i nomi a cui sono riferiti gli oggetti. Le strade, gli edifici, etc. provengono da OpenStreetMap che è a sua volta collegata a Wikidata. Nel popup, se esiste, verrà mostrato l’articolo Wikipedia o l'elemento Wikidata a cui si riferisce il nome di quell’oggetto. Se l’oggetto stesso ha una pagina Wikpedia, anch’essa verrà mostrata.

Anche tu puoi contribuire!Ingrandisci abbastanza e tutte le strade appariranno. Puoi cliccare su una e apparirà un popup con la ricerca Wikidata. Con pochi clic puoi aggiungere un collegamento etimologico. Tieni presente che per farlo, hai bisogno di un account gratuito su OpenStreetMap.", + "layers": { + "1": { + "override": { + "name": "Strade senza informazioni etimologiche" + } + }, + "2": { + "override": { + "name": "Parchi e foreste senza informazioni etimologiche" + } + } + }, + "shortDescription": "Qual è l’origine di un toponimo?", + "title": "Apri Carta Etimologica" + }, "facadegardens": { + "description": "I giardini veritcali e gli alberi in città non solo portano pace e tranquillità ma creano anche un ambiente più bello, aumentano la biodiversità, rendono il clima più fresco e migliorano la qualità dell’aria.
Klimaan VZW e Mechelen Klimaatneutraal vogliono mappare sia i giardini verticali esistenti che quelli nuovi per mostrarli a quanti vogliono costruire un loro proprio giardino o per quelli che amano la natura e vogliono camminare per la città.
Per ulteriori informazioni visita klimaan.be.", "layers": { "0": { "presets": { @@ -412,10 +452,29 @@ "shortDescription": "Questa mappa mostra i giardini verticali, con foto e informazioni utili sulla loro orientazione, sull'illuminazione solare e sui tipi di piante.", "title": "Giardini verticali" }, + "food": { + "title": "Ristoranti e fast food" + }, + "fritures": { + "layers": { + "0": { + "override": { + "name": "Friggitoria" + } + } + } + }, "ghostbikes": { + "description": "Una bici fantasma è un monumento in ricordo di un ciclista che è morto in un incidente stradale, che ha la forma di un una bicicletta bianca installata in maniera permanente ne luogo dell’incidente.

In questa cartina, è possibile vedere tutte le bici fantasma che sono state aggiunte su OpenStreetMap. Ne manca una? Chiunque può aggiungere o migliorare le informazioni qui presenti (è solo richiesto un account gratuito su OpenStreetMap).", "title": "Bici fantasma" }, + "hackerspaces": { + "description": "Su questa cartina è possibile vedere gli hackerspace, aggiungerne di nuovi o aggiornare le informazioni tutto in maniera pratica", + "shortDescription": "Una cartina degli hackerspace", + "title": "Hackerspace" + }, "hailhydrant": { + "description": "In questa cartina puoi vedere e aggiornare idranti, stazioni dei pompieri, stazioni delle ambulanze ed estintori del tuo quartiere preferito.\n\nPuoi seguire la tua posizione precisa (solo su cellulare) e selezionare i livelli che ti interessano nell’angolo in basso a sinistra. Puoi anche usare questo strumento per aggiungere o modificare i PDI sulla mappa e fornire ulteriori dettagli rispondendo alle domande.\n\nTutte le modifiche che farai verranno automaticamente salvate nel database globale di OpenStreetMap e potranno essere riutilizzate liberamente da tutti.", "layers": { "0": { "tagRenderings": { @@ -453,11 +512,80 @@ "render": "Caserma dei vigili del fuoco" } } - } + }, + "shortDescription": "Carta che mostra gli idranti, gli estintori, le caserme dei vigili del fuoco e le stazioni delle ambulanze.", + "title": "Idranti, estintori, caserme dei vigili del fuoco e stazioni delle ambulanze." + }, + "maps": { + "description": "In questa carta puoi trovare tutte le mappe conosciute da OpenStreetMap (tipicamente una grossa mappa su di un pannello informativo che mostra l’area, la città o la regione, ad es. una mappa turistica dietro a un manifesto, la mappa di una riserva naturale, la mappa della rete ciclistica regionale, etc.)

Se manca una mappa, puoi aggiungerla facilmente a questa su OpenStreetMap.", + "shortDescription": "Questo tema mostra tutte le mappe (turistiche) conosciute da OpenStreetMap", + "title": "Una cartina delle cartine" + }, + "natuurpunt": { + "description": "In questa cartina è possibile trovare tutte le riserve naturali offerte da Natuupunt. ", + "shortDescription": "Questa cartina mostra le riserve naturali di Natuurpunt", + "title": "Riserve naturali" + }, + "observation_towers": { + "description": "Torri pubblicamente accessibili per godere della vista", + "shortDescription": "Torri pubblicamente accessibili per godere della vista", + "title": "Torri di osservazione" + }, + "openwindpowermap": { + "description": "Una cartina per la visione e la modifica delle turbine eoliche.", + "title": "OpenWindPowerMap" + }, + "parkings": { + "description": "Questa cartina mostra diversi posti dove parcheggiare", + "shortDescription": "Questa cartina mostra diversi posti dove parcheggiare", + "title": "Parcheggio" + }, + "personal": { + "description": "Crea un tema personale basato sui livelli disponibili per tutti i temi. Per mostrare dei dati, apri selezione livello", + "title": "Tema personalizzato" + }, + "playgrounds": { + "description": "In questa cartina vengono mostrati i parchi giochi a cui è possibile aggiungere dettagli", + "shortDescription": "Una cartina dei parchi giochi", + "title": "Parchi giochi" + }, + "postboxes": { + "description": "In questa cartina puoi veder e modificare gli uffici postali e le buche delle lettere. Puoi usare questa cartina per trovare dove imbucare la tua prossima cartolina! :)
Hai trovato un errore o una buca delle lettere mancante? Puoi modificare questa cartina con un account gratuito su OpenStreetMap. ", + "shortDescription": "Una cartina che mostra le buche delle lettere e gli uffici postali", + "title": "Buche delle lettere e uffici postali" + }, + "shops": { + "description": "In questa cartina è possibile aggiungere informazioni di base di un negozio, orari di apertura e numeri di telefono", + "shortDescription": "Una cartina modificabile con informazioni di base dei negozi", + "title": "Mappa dei negozi" + }, + "sport_pitches": { + "description": "Una campo sportivo è un’area dove vengono praticati gli sport", + "shortDescription": "Una cartina che mostra i campi sportivi", + "title": "Campi sportivi" + }, + "surveillance": { + "description": "In questa cartina puoi trovare le telecamera di sorveglianza.", + "shortDescription": "Telecamere e altri di mezzi di sorveglianza", + "title": "Sorveglianza sotto controllo" + }, + "toilets": { + "description": "Una cartina dei servizi igienici pubblici", + "title": "Mappa libera delle toilet" }, "trees": { "description": "Mappa tutti gli alberi!", "shortDescription": "Mappa tutti gli alberi", "title": "Alberi" + }, + "uk_addresses": { + "description": "Contribuisci a OpenStreetMap inserendo le informazioni sull’indirizzo", + "shortDescription": "Aiuta a costruire un dataset libero per gli indirizzi nel Regno Unito", + "title": "Indirizzi UK" + }, + "waste_basket": { + "description": "In questa cartina troverai i cestini dei rifiuti nei tuoi paraggi. Se manca un cestino, puoi inserirlo tu stesso", + "shortDescription": "Una cartina dei cestini dei rifiuti", + "title": "Cestino dei rifiuti" } } \ No newline at end of file diff --git a/langs/themes/nb_NO.json b/langs/themes/nb_NO.json index d00366f03..06d427c96 100644 --- a/langs/themes/nb_NO.json +++ b/langs/themes/nb_NO.json @@ -1,7 +1,11 @@ { "aed": { + "description": "Defibrillatorer i nærheten", "title": "Åpne AED-kart" }, + "artwork": { + "description": "Velkommen til det åpne kunstverkskartet, et kart over statuer, byster, grafitti, og andre kunstverk i verden" + }, "benches": { "shortDescription": "Et benkekart", "title": "Benker" @@ -9,6 +13,16 @@ "bicyclelib": { "title": "Sykkelbibliotek" }, + "binoculars": { + "shortDescription": "Et kart over fastmonterte kikkerter", + "title": "Kikkerter" + }, + "bookcases": { + "title": "Kart over åpne bokhyller" + }, + "cafes_and_pubs": { + "title": "Kafeer og kneiper" + }, "campersite": { "layers": { "0": { @@ -46,6 +60,10 @@ } } }, + "charging_stations": { + "shortDescription": "Et verdensomspennende kart over ladestasjoner", + "title": "Ladestasjoner" + }, "climbing": { "layers": { "0": { @@ -125,6 +143,10 @@ }, "title": "Åpent klatrekart" }, + "cycle_infra": { + "shortDescription": "Alt relatert til sykkelinfrastruktur.", + "title": "Sykkelinfrastruktur" + }, "cyclestreets": { "layers": { "1": { @@ -159,7 +181,15 @@ } } }, - "shortDescription": "Et kart over sykkelveier" + "shortDescription": "Et kart over sykkelveier", + "title": "Sykkelgater" + }, + "cyclofix": { + "title": "Cyclofix — et åpent kart for syklister" + }, + "drinking_water": { + "description": "Offentlig tilgjengelig drikkevannssteder", + "title": "Drikkevann" }, "facadegardens": { "layers": { @@ -176,6 +206,9 @@ } } }, + "food": { + "title": "Restauranter og søppelmat" + }, "ghostbikes": { "title": "Spøkelsessykler" }, @@ -217,6 +250,38 @@ "render": "Brannstasjon" } } - } + }, + "title": "Hydranter, brannslukkere, brannstasjoner, og ambulansestasjoner." + }, + "maps": { + "title": "Et kart over kart" + }, + "natuurpunt": { + "title": "Naturreservater" + }, + "parkings": { + "shortDescription": "Dette kartet viser forskjellige parkeringsplasser", + "title": "Parkering" + }, + "personal": { + "title": "Personlig tema" + }, + "playgrounds": { + "shortDescription": "Et kart med lekeplasser", + "title": "Lekeplasser" + }, + "postboxes": { + "title": "Postboks og postkontor-kart" + }, + "shops": { + "title": "Kart over åpne butikker" + }, + "toilets": { + "title": "Åpent toalettkart" + }, + "trees": { + "description": "Kartlegg trærne.", + "shortDescription": "Kartlegg alle trærne", + "title": "Trær" } } \ No newline at end of file diff --git a/langs/themes/nl.json b/langs/themes/nl.json index fce0d71ef..47419e1fd 100644 --- a/langs/themes/nl.json +++ b/langs/themes/nl.json @@ -97,8 +97,6 @@ } } }, - "shortDescription": "Met deze tool kan je natuur in je buurt in kaart brengen en meer informatie geven over je favoriete plekje", - "title": "Breng jouw buurtnatuur in kaart", "overrideAll": { "tagRenderings+": { "0": { @@ -163,7 +161,9 @@ "render": "Dit gebied heet {name}" } } - } + }, + "shortDescription": "Met deze tool kan je natuur in je buurt in kaart brengen en meer informatie geven over je favoriete plekje", + "title": "Breng jouw buurtnatuur in kaart" }, "cafes_and_pubs": { "description": "Cafés, kroegen en drinkgelegenheden", @@ -175,11 +175,25 @@ "0": { "description": "camperplaatsen", "name": "Camperplaatsen", + "presets": { + "0": { + "description": "Voeg een nieuwe officiële camperplaats toe. Dit zijn speciaal aangeduide plaatsen waar het toegestaan is om te overnachten met een camper. Ze kunnen er uitzien als een parking, of soms eerder als een camping. Soms staan ze niet ter plaatse aangeduid, maar heeft de gemeente wel degelijk beslist dat dit een camperplaats is. Een parking voor campers waar je niet mag overnachten is géén camperplaats. ", + "title": "camperplaats" + } + }, "tagRenderings": { + "caravansites-capacity": { + "question": "Hoeveel campers kunnen hier overnachten? (sla dit over als er geen duidelijk aantal plaatsen of aangeduid maximum is)", + "render": "{capacity} campers kunnen deze plaats tegelijk gebruiken" + }, "caravansites-charge": { "question": "Hoeveel kost deze plaats?", "render": "Deze plaats vraagt {charge}" }, + "caravansites-description": { + "question": "Wil je graag een algemene beschrijving toevoegen van deze plaats? (Herhaal hier niet de antwoorden op de vragen die reeds gesteld zijn. Hou het objectief - je kan je mening geven via een review)", + "render": "Meer details over deze plaats: {description}" + }, "caravansites-fee": { "mappings": { "0": { @@ -191,6 +205,16 @@ }, "question": "Moet men betalen om deze camperplaats te gebruiken?" }, + "caravansites-internet": { + "mappings": { + "0": { + "then": "Er is internettoegang" + }, + "1": { + "then": "Er is internettoegang" + } + } + }, "caravansites-name": { "question": "Wat is de naam van deze plaats?", "render": "Deze plaats heet {name}" @@ -206,12 +230,6 @@ } }, "render": "Camperplaats {name}" - }, - "presets": { - "0": { - "description": "Voeg een nieuwe officiële camperplaats toe. Dit zijn speciaal aangeduide plaatsen waar het toegestaan is om te overnachten met een camper. Ze kunnen er uitzien als een parking, of soms eerder als een camping. Soms staan ze niet ter plaatse aangeduid, maar heeft de gemeente wel degelijk beslist dat dit een camperplaats is. Een parking voor campers waar je niet mag overnachten is géén camperplaats. ", - "title": "camperplaats" - } } } }, @@ -376,18 +394,6 @@ } }, "overrideAll": { - "units+": { - "0": { - "applicableUnits": { - "0": { - "human": " meter" - }, - "1": { - "human": " voet" - } - } - } - }, "tagRenderings+": { "0": { "question": "Is er een (onofficiële) website met meer informatie (b.v. met topos)?" @@ -487,6 +493,18 @@ }, "question": "Is er een snelklimmuur (speed climbing)?" } + }, + "units+": { + "0": { + "applicableUnits": { + "0": { + "human": " meter" + }, + "1": { + "human": " voet" + } + } + } } }, "title": "Open klimkaart" @@ -523,8 +541,6 @@ } } }, - "shortDescription": "Een kaart met alle gekende fietsstraten", - "title": "Fietsstraten", "overrideAll": { "tagRenderings+": { "0": { @@ -549,7 +565,9 @@ "render": "Deze straat wordt fietsstraat op {cyclestreet:start_date}" } } - } + }, + "shortDescription": "Een kaart met alle gekende fietsstraten", + "title": "Fietsstraten" }, "cyclofix": { "description": "Het doel van deze kaart is om fietsers een gebruiksvriendelijke oplossing te bieden voor het vinden van de juiste infrastructuur voor hun behoeften.

U kunt uw exacte locatie volgen (enkel mobiel) en in de linkerbenedenhoek categorieën selecteren die voor u relevant zijn. U kunt deze tool ook gebruiken om 'spelden' aan de kaart toe te voegen of te bewerken en meer gegevens te verstrekken door de vragen te beantwoorden.

Alle wijzigingen die u maakt worden automatisch opgeslagen in de wereldwijde database van OpenStreetMap en kunnen door anderen vrij worden hergebruikt.

Bekijk voor meer info over cyclofix ook cyclofix.osm.be.", @@ -950,53 +968,6 @@ "shortDescription": "Deze kaart toont sportvelden", "title": "Sportvelden" }, - "surveillance": { - "description": "Op deze open kaart kan je bewakingscamera's vinden.", - "shortDescription": "Bewakingscameras en dergelijke", - "title": "Surveillance under Surveillance" - }, - "toerisme_vlaanderen": { - "description": "Op deze kaart kan je info zien die relevant is voor toerisme, zoals:
  • Eetgelegenheden
  • Cafés en bars
  • (Fiets)oplaadpunten
  • Fietspompen, fietserverhuur en fietswinkels
  • Uitkijktorens
  • ...
Zie je fouten op de kaart? Dan kan je zelf makkelijk aanpasingen maken, die zichtbaar zijn voor iedereen. Hiervoor dien je een gratis OpenStreetMap account voor te maken.", - "descriptionTail": "Met de steun van Toerisme Vlaanderen", - "shortDescription": "Een kaart om toeristisch relevante info op aan te duiden", - "title": "Toeristisch relevante info" - }, - "toilets": { - "description": "Een kaart met openbare toiletten", - "title": "Open Toilettenkaart" - }, - "trees": { - "description": "Breng bomen in kaart!", - "shortDescription": "Breng bomen in kaart", - "title": "Bomen" - }, - "uk_addresses": { - "description": "Draag bij aan OpenStreetMap door adresinformatie in te vullen", - "layers": { - "1": { - "description": "Adressen", - "tagRenderings": { - "uk_addresses_housenumber": { - "mappings": { - "0": { - "then": "Dit gebouw heeft geen huisnummer" - } - }, - "render": "Het huisnummer is {addr:housenumber}" - } - } - } - } - }, - "waste_basket": { - "description": "Op deze kaart vind je vuilnisbakken waar je afval in kan smijten. Ontbreekt er een vuilnisbak? Dan kan je die zelf toevoegen", - "shortDescription": "Een kaart met vuilnisbakken", - "title": "Vuilnisbak" - }, - "street_lighting_assen": { - "description": "Op deze kaart vind je alles over straatlantaarns + een dataset van Assen", - "title": "Straatverlichting - Assen" - }, "street_lighting": { "description": "Op deze kaart vind je alles over straatlantaarns", "layers": { @@ -1052,5 +1023,52 @@ } }, "title": "Straatverlichting" + }, + "street_lighting_assen": { + "description": "Op deze kaart vind je alles over straatlantaarns + een dataset van Assen", + "title": "Straatverlichting - Assen" + }, + "surveillance": { + "description": "Op deze open kaart kan je bewakingscamera's vinden.", + "shortDescription": "Bewakingscameras en dergelijke", + "title": "Surveillance under Surveillance" + }, + "toerisme_vlaanderen": { + "description": "Op deze kaart kan je info zien die relevant is voor toerisme, zoals:
  • Eetgelegenheden
  • Cafés en bars
  • (Fiets)oplaadpunten
  • Fietspompen, fietserverhuur en fietswinkels
  • Uitkijktorens
  • ...
Zie je fouten op de kaart? Dan kan je zelf makkelijk aanpasingen maken, die zichtbaar zijn voor iedereen. Hiervoor dien je een gratis OpenStreetMap account voor te maken.", + "descriptionTail": "Met de steun van Toerisme Vlaanderen", + "shortDescription": "Een kaart om toeristisch relevante info op aan te duiden", + "title": "Toeristisch relevante info" + }, + "toilets": { + "description": "Een kaart met openbare toiletten", + "title": "Open Toilettenkaart" + }, + "trees": { + "description": "Breng bomen in kaart!", + "shortDescription": "Breng bomen in kaart", + "title": "Bomen" + }, + "uk_addresses": { + "description": "Draag bij aan OpenStreetMap door adresinformatie in te vullen", + "layers": { + "1": { + "description": "Adressen", + "tagRenderings": { + "uk_addresses_housenumber": { + "mappings": { + "0": { + "then": "Dit gebouw heeft geen huisnummer" + } + }, + "render": "Het huisnummer is {addr:housenumber}" + } + } + } + } + }, + "waste_basket": { + "description": "Op deze kaart vind je vuilnisbakken waar je afval in kan smijten. Ontbreekt er een vuilnisbak? Dan kan je die zelf toevoegen", + "shortDescription": "Een kaart met vuilnisbakken", + "title": "Vuilnisbak" } } \ No newline at end of file From bf768a9f84d9766475ed8b3c6de186bb65062fad Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 4 Nov 2021 22:16:57 +0100 Subject: [PATCH 84/95] Add small contributorRights document --- Docs/ContributorRights.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 Docs/ContributorRights.md diff --git a/Docs/ContributorRights.md b/Docs/ContributorRights.md new file mode 100644 index 000000000..f845f0672 --- /dev/null +++ b/Docs/ContributorRights.md @@ -0,0 +1,32 @@ + + Rights of contributors + ====================== + +If a contributor is quite active within MapComplete, this contributor might be granted access to the main repository. + +If you have access to the repository, you can make a fork of an already existing branch and push this new branch to github. +This means that this branch will be _automatically built_ and be **deployed** to `https://pietervdvn.github.io/mc/`. You can see the deploy process on [Github Actions](https://github.com/pietervdvn/MapComplete/actions). +Don't worry about pushing too much. These deploys are free and totally automatic. They might fail if something is wrong, but this will hinder no-one. + +Additionaly, some other maintainer might step in and merge the latest develop with your branch, making later pull requests easier. + +Don't worry about bugs +---------------------- + +As a non-admin contributor, you can _not_ make changes to the `master` nor to the `develop` branch. This is because, as soon as master is changed, this is built and deployed on `mapcomplete.osm.be`, which a lot of people use. An error there will cause a lot of grieve. + +A push on `develop` is automatically deployed to [pietervdvn.github.io/mc/develop] and is used by quite some people to. People using this version should know that this is a testing ground for new features and might contain a bug every now and then. + +In other words, to get your theme deployed on the main instances, you'll still have to create a pull request. The maintainers will then doublecheck and pull it in. + +If you have a local repository +------------------------------ + +If you have made a fork earlier and have received contributor rights, you need to tell your local git repository that pushing to the main repository is possible. + +To do this: + +1. type `git remote add upstream git@github.com:pietervdvn/MapComplete` +2. Run `git push upstream` to push your latest changes to the main repo (and not your fork). Running `git push` will push to your fork. + +Alternatively, if you don't have any unmerged changes, you can remove your local copy and clone `pietervdvn/MapComplete` again to start fresh. \ No newline at end of file From f1f955233b4291dd065a7ddef407082326634aff Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 4 Nov 2021 22:22:26 +0100 Subject: [PATCH 85/95] Add extra sync step in deploy script --- .github/workflows/deploy_pietervdvn.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/deploy_pietervdvn.yml b/.github/workflows/deploy_pietervdvn.yml index dfa564468..9a2cf8c7c 100644 --- a/.github/workflows/deploy_pietervdvn.yml +++ b/.github/workflows/deploy_pietervdvn.yml @@ -37,6 +37,13 @@ jobs: git clone --depth 1 --single-branch --branch master "https://x-access-token:$DEPLOY_KEY_PIETERVDVN@github.com/pietervdvn/pietervdvn.github.io.git" echo "Destination repo is cloned" + - name: Sync repo + env: + DEPLOY_KEY_PIETERVDVN: ${{ secrets.DEPLOY_KEY_PIETERVDVN }} + run: | + cd pietervdvn.github.io + git pull + - name: "Copying files" run: | echo "Deploying" From d3de5475cd56371d7fcd4cbd6606fe6f20fda4fa Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 4 Nov 2021 22:31:14 +0100 Subject: [PATCH 86/95] Relinting themes, move rendering info to 'mapRendering' --- Models/ThemeConfig/LegacyJsonConvert.ts | 10 +- assets/themes/campersite/campersite.json | 88 ++++++---- assets/themes/climbing/climbing.json | 163 +++++++++++------- .../themes/cycle_highways/cycle_highways.json | 70 ++++---- assets/themes/cyclestreets/cyclestreets.json | 71 +++++--- .../themes/facadegardens/facadegardens.json | 136 +++++++-------- assets/themes/hackerspaces/hackerspaces.json | 63 ++++--- assets/themes/hailhydrant/hailhydrant.json | 131 ++++++++------ .../openwindpowermap/openwindpowermap.json | 28 +-- assets/themes/postboxes/postboxes.json | 85 +++++---- assets/themes/uk_addresses/uk_addresses.json | 148 +++++++++------- 11 files changed, 573 insertions(+), 420 deletions(-) diff --git a/Models/ThemeConfig/LegacyJsonConvert.ts b/Models/ThemeConfig/LegacyJsonConvert.ts index 43274643c..7f7b0e30f 100644 --- a/Models/ThemeConfig/LegacyJsonConvert.ts +++ b/Models/ThemeConfig/LegacyJsonConvert.ts @@ -16,12 +16,12 @@ export default class LegacyJsonConvert { if (config.tagRenderings !== undefined) { for (const tagRendering of config.tagRenderings) { - if (tagRendering["#"] !== undefined) { - tagRendering["id"] = tagRendering["#"] - delete tagRendering["#"] - } if (tagRendering["id"] === undefined) { - if (tagRendering["freeform"]?.key !== undefined) { + + if (tagRendering["#"] !== undefined) { + tagRendering["id"] = tagRendering["#"] + delete tagRendering["#"] + } else if (tagRendering["freeform"]?.key !== undefined) { tagRendering["id"] = config.id + "-" + tagRendering["freeform"]["key"] } } diff --git a/assets/themes/campersite/campersite.json b/assets/themes/campersite/campersite.json index 04dfb5402..f45403b00 100644 --- a/assets/themes/campersite/campersite.json +++ b/assets/themes/campersite/campersite.json @@ -635,28 +635,6 @@ "questions", "reviews" ], - "icon": { - "render": "circle:white;./assets/themes/campersite/caravan.svg", - "mappings": [ - { - "if": { - "and": [ - "fee=no" - ] - }, - "then": "circle:white;./assets/themes/campersite/caravan_green.svg" - } - ] - }, - "width": { - "render": "8" - }, - "iconSize": { - "render": "40,40,center" - }, - "color": { - "render": "#00f" - }, "presets": [ { "tags": [ @@ -684,7 +662,38 @@ } } ], - "wayHandling": 2 + "mapRendering": [ + { + "icon": { + "render": "circle:white;./assets/themes/campersite/caravan.svg", + "mappings": [ + { + "if": { + "and": [ + "fee=no" + ] + }, + "then": "circle:white;./assets/themes/campersite/caravan_green.svg" + } + ] + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point", + "centroid" + ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "8" + } + } + ] }, { "id": "dumpstations", @@ -1051,18 +1060,6 @@ "id": "dumpstations-network" } ], - "icon": { - "render": "circle:white;./assets/themes/campersite/sanitary_dump_station.svg" - }, - "width": { - "render": "8" - }, - "iconSize": { - "render": "32,32,center" - }, - "color": { - "render": "#00f" - }, "presets": [ { "tags": [ @@ -1083,6 +1080,27 @@ "de": "Fügen Sie eine neue sanitäre Entsorgungsstation hinzu. Hier können Camper Abwasser oder chemischen Toilettenabfälle entsorgen. Oft gibt es auch Trinkwasser und Strom." } } + ], + "mapRendering": [ + { + "icon": { + "render": "circle:white;./assets/themes/campersite/sanitary_dump_station.svg" + }, + "iconSize": { + "render": "32,32,center" + }, + "location": [ + "point" + ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "8" + } + } ] } ], diff --git a/assets/themes/climbing/climbing.json b/assets/themes/climbing/climbing.json index 18bc8a0ca..6a37b5e13 100644 --- a/assets/themes/climbing/climbing.json +++ b/assets/themes/climbing/climbing.json @@ -153,25 +153,6 @@ "phone", "opening_hours" ], - "icon": { - "render": "./assets/themes/climbing/club.svg" - }, - "iconOverlays": [ - { - "if": "opening_hours~*", - "then": "isOpen", - "badge": true - } - ], - "width": { - "render": "8" - }, - "iconSize": { - "render": "40,40,center" - }, - "color": { - "render": "#00f" - }, "presets": [ { "tags": [ @@ -218,7 +199,25 @@ } } ], - "wayHandling": 1 + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/climbing/club.svg" + }, + "iconBadges": [ + { + "if": "opening_hours~*", + "then": "isOpen" + } + ], + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } + ] }, { "id": "climbing_gym", @@ -302,21 +301,25 @@ "opening_hours", "reviews" ], - "icon": { - "render": "./assets/themes/climbing/climbing_gym.svg" - }, - "iconOverlays": [ + "mapRendering": [ { - "if": "opening_hours~*", - "then": "isOpen", - "badge": true + "icon": { + "render": "./assets/themes/climbing/climbing_gym.svg" + }, + "iconBadges": [ + { + "if": "opening_hours~*", + "then": "isOpen" + } + ], + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] } - ], - "width": "0", - "iconSize": { - "render": "40,40,center" - }, - "wayHandling": 1 + ] }, { "id": "climbing_route", @@ -513,18 +516,6 @@ }, "reviews" ], - "icon": { - "render": "circle:white;./assets/themes/climbing/climbing_route.svg" - }, - "width": { - "render": "4" - }, - "iconSize": { - "render": "28,28,center" - }, - "color": { - "render": "#0f0" - }, "presets": [ { "title": { @@ -539,7 +530,28 @@ ] } ], - "wayHandling": 2 + "mapRendering": [ + { + "icon": { + "render": "circle:white;./assets/themes/climbing/climbing_route.svg" + }, + "iconSize": { + "render": "28,28,center" + }, + "location": [ + "point", + "centroid" + ] + }, + { + "color": { + "render": "#0f0" + }, + "width": { + "render": "4" + } + } + ] }, { "id": "climbing", @@ -765,18 +777,6 @@ }, "reviews" ], - "icon": { - "render": "./assets/themes/climbing/climbing_no_rope.svg" - }, - "width": { - "render": "8" - }, - "iconSize": { - "render": "40,40,center" - }, - "color": { - "render": "#d38d5fAA" - }, "presets": [ { "tags": [ @@ -800,7 +800,6 @@ } } ], - "wayHandling": 2, "calculatedTags": [ "_contained_climbing_routes_properties=feat.overlapWith('climbing_route').map(f => f.feat.properties).map(p => {return {id: p.id, name: p.name, 'climbing:grade:french': p['climbing:grade:french'], 'climbing:length': p['climbing:length']} })", "_contained_climbing_routes=JSON.parse(feat.properties._contained_climbing_routes_properties ?? '[]').map(p => `
  • ${p.name ?? 'climbing route'} (${p['climbing:grade:french'] ?? 'unknown difficulty'}, ${p['climbing:length'] ?? 'unkown length'} meter)
  • `).join('')", @@ -808,6 +807,28 @@ "_difficulty_hist=JSON.parse(feat.properties._contained_climbing_routes_properties ?? '[]').map(p => p['climbing:grade:french'])", "_length_hist=JSON.parse(feat.properties._contained_climbing_routes_properties ?? '[]').map(p => p['climbing:length'])", "_contained_climbing_routes_count=JSON.parse(feat.properties._contained_climbing_routes_properties ?? '[]').length" + ], + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/climbing/climbing_no_rope.svg" + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point", + "centroid" + ] + }, + { + "color": { + "render": "#d38d5fAA" + }, + "width": { + "render": "8" + } + } ] }, { @@ -930,14 +951,22 @@ ] } ], - "icon": "./assets/themes/climbing/climbing_unknown.svg", - "width": { - "render": "2" - }, - "color": { - "render": "#ddff55AA" - }, - "wayHandling": 0 + "mapRendering": [ + { + "icon": "./assets/themes/climbing/climbing_unknown.svg", + "location": [ + "point" + ] + }, + { + "color": { + "render": "#ddff55AA" + }, + "width": { + "render": "2" + } + } + ] } ], "overrideAll": { diff --git a/assets/themes/cycle_highways/cycle_highways.json b/assets/themes/cycle_highways/cycle_highways.json index 696e0df0b..266312e59 100644 --- a/assets/themes/cycle_highways/cycle_highways.json +++ b/assets/themes/cycle_highways/cycle_highways.json @@ -116,39 +116,6 @@ "de": "Radschnellweg" } }, - "width": { - "render": "4" - }, - "color": { - "render": "#ff7392", - "mappings": [ - { - "if": "state=", - "then": "#00acfc" - }, - { - "if": "state=temporary", - "then": "#00acfc" - } - ] - }, - "dashArray": { - "render": "", - "mappings": [ - { - "if": "state=temporary", - "then": "12 10" - }, - { - "if": "note:state=has_highway_no", - "then": "0 8" - }, - { - "if": "note:state=has_highway_under_construction", - "then": "12 10" - } - ] - }, "filter": [ { "id": "name-alt", @@ -236,6 +203,43 @@ } ] } + ], + "mapRendering": [ + { + "color": { + "render": "#ff7392", + "mappings": [ + { + "if": "state=", + "then": "#00acfc" + }, + { + "if": "state=temporary", + "then": "#00acfc" + } + ] + }, + "width": { + "render": "4" + }, + "dashArray": { + "render": "", + "mappings": [ + { + "if": "state=temporary", + "then": "12 10" + }, + { + "if": "note:state=has_highway_no", + "then": "0 8" + }, + { + "if": "note:state=has_highway_under_construction", + "then": "12 10" + } + ] + } + } ] } ], diff --git a/assets/themes/cyclestreets/cyclestreets.json b/assets/themes/cyclestreets/cyclestreets.json index e52c6bc8d..57e56a370 100644 --- a/assets/themes/cyclestreets/cyclestreets.json +++ b/assets/themes/cyclestreets/cyclestreets.json @@ -84,11 +84,20 @@ "de": "Eine Fahrradstraße ist eine Straße, auf der motorisierter Verkehr einen Radfahrer nicht überholen darf" }, "title": "{name}", - "icon": "./assets/themes/cyclestreets/F111.svg", - "color": "#0000ff", - "width": "10", "tagRenderings": [ "images" + ], + "mapRendering": [ + { + "icon": "./assets/themes/cyclestreets/F111.svg", + "location": [ + "point" + ] + }, + { + "color": "#0000ff", + "width": "10" + } ] }, { @@ -110,7 +119,6 @@ "de": "Diese Straße wird bald eine Fahrradstraße sein" }, "minzoom": 9, - "wayHandling": 0, "source": { "osmTags": "proposed:cyclestreet=yes" }, @@ -136,11 +144,20 @@ } ] }, - "icon": "./assets/themes/cyclestreets/F113.svg", - "color": "#09f9dd", - "width": "5", "tagRenderings": [ "images" + ], + "mapRendering": [ + { + "icon": "./assets/themes/cyclestreets/F113.svg", + "location": [ + "point" + ] + }, + { + "color": "#09f9dd", + "width": "5" + } ] }, { @@ -173,7 +190,6 @@ } }, "minzoom": 18, - "wayHandling": 0, "title": { "render": { "nl": "Straat", @@ -191,23 +207,32 @@ } ] }, - "icon": "./assets/svg/pencil.svg", - "width": "5", - "color": { - "render": "#aaaaaa", - "mappings": [ - { - "then": "#0000ff", - "if": "cyclestreet=yes" - }, - { - "then": "#09f9dd", - "if": "proposed:cyclestreet=yes" - } - ] - }, "tagRenderings": [ "images" + ], + "mapRendering": [ + { + "icon": "./assets/svg/pencil.svg", + "location": [ + "point" + ] + }, + { + "color": { + "render": "#aaaaaa", + "mappings": [ + { + "then": "#0000ff", + "if": "cyclestreet=yes" + }, + { + "then": "#09f9dd", + "if": "proposed:cyclestreet=yes" + } + ] + }, + "width": "5" + } ] } ], diff --git a/assets/themes/facadegardens/facadegardens.json b/assets/themes/facadegardens/facadegardens.json index 676424d30..955b17ae4 100644 --- a/assets/themes/facadegardens/facadegardens.json +++ b/assets/themes/facadegardens/facadegardens.json @@ -84,38 +84,6 @@ "fr": "Jardins muraux", "de": "Fassadengärten" }, - "iconOverlays": [ - { - "if": "plant~.*vine.*", - "then": "circle:white;./assets/themes/facadegardens/klimplant.svg", - "badge": true - }, - { - "if": "plant~.*groundcover.*", - "then": "circle:white;./assets/themes/facadegardens/bodembedekker.svg", - "badge": true - }, - { - "if": "edible=true", - "then": "circle:white;./assets/themes/facadegardens/eetbaar.svg", - "badge": true - }, - { - "if": "rain_barel=yes", - "then": "circle:white;./assets/themes/facadegardens/gevelton.svg", - "badge": true - }, - { - "if": "plant~.*shrub.*", - "then": "circle:white;./assets/themes/facadegardens/struik.svg", - "badge": true - }, - { - "if": "plant~.*flower.*", - "then": "circle:white;./assets/themes/facadegardens/bloei.svg", - "badge": true - } - ], "tagRenderings": [ "images", { @@ -396,44 +364,6 @@ "id": "facadegardens-description" } ], - "icon": { - "render": "circle:white;./assets/themes/facadegardens/geveltuin.svg", - "mappings": [ - { - "if": { - "and": [ - "direct_sunlight=yes" - ] - }, - "then": "circle:white;./assets/themes/facadegardens/zon.svg" - }, - { - "if": { - "and": [ - "direct_sunlight=partial" - ] - }, - "then": "circle:white;./assets/themes/facadegardens/halfzon.svg" - }, - { - "if": { - "and": [ - "direct_sunlight=no" - ] - }, - "then": "circle:white;./assets/themes/facadegardens/schaduw.svg" - } - ] - }, - "width": { - "render": "8" - }, - "iconSize": { - "render": "50,50,center" - }, - "color": { - "render": "#00f" - }, "presets": [ { "tags": [ @@ -458,7 +388,71 @@ } } ], - "wayHandling": 1 + "mapRendering": [ + { + "icon": { + "render": "circle:white;./assets/themes/facadegardens/geveltuin.svg", + "mappings": [ + { + "if": { + "and": [ + "direct_sunlight=yes" + ] + }, + "then": "circle:white;./assets/themes/facadegardens/zon.svg" + }, + { + "if": { + "and": [ + "direct_sunlight=partial" + ] + }, + "then": "circle:white;./assets/themes/facadegardens/halfzon.svg" + }, + { + "if": { + "and": [ + "direct_sunlight=no" + ] + }, + "then": "circle:white;./assets/themes/facadegardens/schaduw.svg" + } + ] + }, + "iconBadges": [ + { + "if": "plant~.*vine.*", + "then": "circle:white;./assets/themes/facadegardens/klimplant.svg" + }, + { + "if": "plant~.*groundcover.*", + "then": "circle:white;./assets/themes/facadegardens/bodembedekker.svg" + }, + { + "if": "edible=true", + "then": "circle:white;./assets/themes/facadegardens/eetbaar.svg" + }, + { + "if": "rain_barel=yes", + "then": "circle:white;./assets/themes/facadegardens/gevelton.svg" + }, + { + "if": "plant~.*shrub.*", + "then": "circle:white;./assets/themes/facadegardens/struik.svg" + }, + { + "if": "plant~.*flower.*", + "then": "circle:white;./assets/themes/facadegardens/bloei.svg" + } + ], + "iconSize": { + "render": "50,50,center" + }, + "location": [ + "point" + ] + } + ] } ] } \ No newline at end of file diff --git a/assets/themes/hackerspaces/hackerspaces.json b/assets/themes/hackerspaces/hackerspaces.json index 1761e33f0..f177c4591 100644 --- a/assets/themes/hackerspaces/hackerspaces.json +++ b/assets/themes/hackerspaces/hackerspaces.json @@ -177,31 +177,6 @@ "id": "hackerspaces-start_date" } ], - "icon": { - "render": "./assets/themes/hackerspaces/glider.svg", - "mappings": [ - { - "if": { - "and": [ - "hackerspace=makerspace" - ] - }, - "then": { - "en": "./assets/themes/hackerspaces/led.png", - "de": "./assets/themes/hackerspaces/led.png" - } - } - ] - }, - "width": { - "render": "8" - }, - "iconSize": { - "render": "40,40,center" - }, - "color": { - "render": "#00f" - }, "presets": [ { "tags": [ @@ -231,14 +206,48 @@ } } ], - "wayHandling": 2, "source": { "osmTags": { "and": [ "leisure=hackerspace" ] } - } + }, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/hackerspaces/glider.svg", + "mappings": [ + { + "if": { + "and": [ + "hackerspace=makerspace" + ] + }, + "then": { + "en": "./assets/themes/hackerspaces/led.png", + "de": "./assets/themes/hackerspaces/led.png" + } + } + ] + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point", + "centroid" + ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "8" + } + } + ] } ] } \ No newline at end of file diff --git a/assets/themes/hailhydrant/hailhydrant.json b/assets/themes/hailhydrant/hailhydrant.json index 728c15903..4fd07734b 100644 --- a/assets/themes/hailhydrant/hailhydrant.json +++ b/assets/themes/hailhydrant/hailhydrant.json @@ -313,18 +313,6 @@ }, "images" ], - "icon": { - "render": "./assets/themes/hailhydrant/hydrant.svg" - }, - "width": { - "render": "8" - }, - "iconSize": { - "render": "20,20,center" - }, - "color": { - "render": "#00f" - }, "presets": [ { "tags": [ @@ -346,7 +334,28 @@ } } ], - "wayHandling": 2 + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/hailhydrant/hydrant.svg" + }, + "iconSize": { + "render": "20,20,center" + }, + "location": [ + "point", + "centroid" + ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "8" + } + } + ] }, { "id": "extinguisher", @@ -438,18 +447,6 @@ }, "images" ], - "icon": { - "render": "./assets/themes/hailhydrant/Twemoji12_1f9ef.svg" - }, - "width": { - "render": "8" - }, - "iconSize": { - "render": "20,20,center" - }, - "color": { - "render": "#00f" - }, "presets": [ { "tags": [ @@ -472,7 +469,19 @@ } } ], - "wayHandling": 1 + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/hailhydrant/Twemoji12_1f9ef.svg" + }, + "iconSize": { + "render": "20,20,center" + }, + "location": [ + "point" + ] + } + ] }, { "id": "fire_stations", @@ -493,7 +502,6 @@ ] } }, - "wayHandling": 2, "title": { "render": { "en": "Fire Station", @@ -671,18 +679,6 @@ }, "images" ], - "icon": { - "render": "./assets/themes/hailhydrant/Twemoji12_1f692.svg" - }, - "width": { - "render": "1" - }, - "iconSize": { - "render": "35,35,center" - }, - "color": { - "render": "#c22" - }, "presets": [ { "tags": [ @@ -702,6 +698,28 @@ "de": "Eine Feuerwache ist ein Ort, an dem die Feuerwehrfahrzeuge und die Feuerwehrleute untergebracht sind, wenn sie nicht im Einsatz sind." } } + ], + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/hailhydrant/Twemoji12_1f692.svg" + }, + "iconSize": { + "render": "35,35,center" + }, + "location": [ + "point", + "centroid" + ] + }, + { + "color": { + "render": "#c22" + }, + "width": { + "render": "1" + } + } ] }, { @@ -872,18 +890,6 @@ }, "images" ], - "icon": { - "render": "./assets/themes/hailhydrant/Twemoji_1f691.svg" - }, - "width": { - "render": "1" - }, - "iconSize": { - "render": "35,35,center" - }, - "color": { - "render": "#00f" - }, "presets": [ { "tags": [ @@ -904,7 +910,28 @@ } } ], - "wayHandling": 2 + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/hailhydrant/Twemoji_1f691.svg" + }, + "iconSize": { + "render": "35,35,center" + }, + "location": [ + "point", + "centroid" + ] + }, + { + "color": { + "render": "#00f" + }, + "width": { + "render": "1" + } + } + ] } ], "defaultBackgroundId": "HDM_HOT" diff --git a/assets/themes/openwindpowermap/openwindpowermap.json b/assets/themes/openwindpowermap/openwindpowermap.json index 0b832140f..a6b9f2c5c 100644 --- a/assets/themes/openwindpowermap/openwindpowermap.json +++ b/assets/themes/openwindpowermap/openwindpowermap.json @@ -43,7 +43,6 @@ "osmTags": "generator:source=wind" }, "minzoom": 10, - "wayHandling": 1, "title": { "render": { "en": "wind turbine", @@ -61,16 +60,6 @@ } ] }, - "icon": "./assets/themes/openwindpowermap/wind_turbine.svg", - "iconSize": "40, 40, bottom", - "label": { - "mappings": [ - { - "if": "generator:output:electricity~^[0-9]+.*[W]$", - "then": "
    {generator:output:electricity}
    " - } - ] - }, "tagRenderings": [ { "id": "turbine-output", @@ -243,6 +232,23 @@ } ] } + ], + "mapRendering": [ + { + "icon": "./assets/themes/openwindpowermap/wind_turbine.svg", + "label": { + "mappings": [ + { + "if": "generator:output:electricity~^[0-9]+.*[W]$", + "then": "
    {generator:output:electricity}
    " + } + ] + }, + "iconSize": "40, 40, bottom", + "location": [ + "point" + ] + } ] } ], diff --git a/assets/themes/postboxes/postboxes.json b/assets/themes/postboxes/postboxes.json index 7713dcaee..086b5f423 100644 --- a/assets/themes/postboxes/postboxes.json +++ b/assets/themes/postboxes/postboxes.json @@ -61,18 +61,6 @@ "render": "{minimap(18): height: 5rem; overflow: hidden; border-radius:3rem; }" } ], - "icon": { - "render": "./assets/themes/postboxes/postbox.svg" - }, - "width": { - "render": "1" - }, - "iconSize": { - "render": "40,40,bottom" - }, - "color": { - "render": "#DADADA" - }, "presets": [ { "tags": [ @@ -84,7 +72,6 @@ } } ], - "wayHandling": 2, "deletion": { "softDeletionTags": { "and": [ @@ -92,7 +79,29 @@ "razed:amenity=post_box" ] } - } + }, + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/postboxes/postbox.svg" + }, + "iconSize": { + "render": "40,40,bottom" + }, + "location": [ + "point", + "centroid" + ] + }, + { + "color": { + "render": "#DADADA" + }, + "width": { + "render": "1" + } + } + ] }, { "id": "postoffices", @@ -143,25 +152,6 @@ "id": "OH" } ], - "icon": { - "render": "square:white;./assets/themes/postboxes/post_office.svg" - }, - "iconOverlays": [ - { - "if": "opening_hours~*", - "then": "isOpen", - "badge": true - } - ], - "width": { - "render": "1" - }, - "iconSize": { - "render": "40,40,bottom" - }, - "color": { - "render": "#DADADA" - }, "presets": [ { "tags": [ @@ -173,7 +163,6 @@ } } ], - "wayHandling": 2, "filter": [ { "id": "is_open", @@ -187,6 +176,34 @@ } ] } + ], + "mapRendering": [ + { + "icon": { + "render": "square:white;./assets/themes/postboxes/post_office.svg" + }, + "iconBadges": [ + { + "if": "opening_hours~*", + "then": "isOpen" + } + ], + "iconSize": { + "render": "40,40,bottom" + }, + "location": [ + "point", + "centroid" + ] + }, + { + "color": { + "render": "#DADADA" + }, + "width": { + "render": "1" + } + } ] } ] diff --git a/assets/themes/uk_addresses/uk_addresses.json b/assets/themes/uk_addresses/uk_addresses.json index f37a641cd..fc980965a 100644 --- a/assets/themes/uk_addresses/uk_addresses.json +++ b/assets/themes/uk_addresses/uk_addresses.json @@ -62,23 +62,6 @@ }, "name": "Addresses to check", "minzoom": 14, - "wayHandling": 1, - "icon": { - "render": "./assets/themes/uk_addresses/housenumber_unknown.svg", - "mappings": [ - { - "if": "_embedding_object:id~*", - "then": "./assets/themes/uk_addresses/housenumber_unknown_small.svg" - }, - { - "if": "_imported=yes", - "then": "./assets/themes/uk_addresses/housenumber_unknown_small.svg" - } - ] - }, - "iconSize": { - "render": "40,40,center" - }, "title": { "render": "Address to be determined" }, @@ -118,6 +101,29 @@ } ] } + ], + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/uk_addresses/housenumber_unknown.svg", + "mappings": [ + { + "if": "_embedding_object:id~*", + "then": "./assets/themes/uk_addresses/housenumber_unknown_small.svg" + }, + { + "if": "_imported=yes", + "then": "./assets/themes/uk_addresses/housenumber_unknown_small.svg" + } + ] + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + } ] }, { @@ -230,50 +236,59 @@ } } ], - "icon": { - "render": "./assets/themes/uk_addresses/housenumber_ok.svg", - "mappings": [ - { - "if": { - "or": [ - { - "and": [ - "addr:housenumber=", - "nohousenumber!=yes" + "mapRendering": [ + { + "icon": { + "render": "./assets/themes/uk_addresses/housenumber_ok.svg", + "mappings": [ + { + "if": { + "or": [ + { + "and": [ + "addr:housenumber=", + "nohousenumber!=yes" + ] + }, + "addr:street=" ] }, - "addr:street=" - ] - }, - "then": "./assets/themes/uk_addresses/housenumber_unknown.svg" - } - ] - }, - "width": { - "render": "8" - }, - "iconSize": { - "render": "40,40,center" - }, - "color": { - "render": "#00f", - "mappings": [ - { - "if": { - "or": [ - { - "and": [ - "addr:housenumber=", - "nohousenumber!=yes" + "then": "./assets/themes/uk_addresses/housenumber_unknown.svg" + } + ] + }, + "iconSize": { + "render": "40,40,center" + }, + "location": [ + "point" + ] + }, + { + "color": { + "render": "#00f", + "mappings": [ + { + "if": { + "or": [ + { + "and": [ + "addr:housenumber=", + "nohousenumber!=yes" + ] + }, + "addr:street=" ] }, - "addr:street=" - ] - }, - "then": "#ff0" + "then": "#ff0" + } + ] + }, + "width": { + "render": "8" } - ] - } + } + ] }, { "id": "named_streets", @@ -286,12 +301,21 @@ ] } }, - "color": { - "render": "#ccc" - }, - "width": { - "render": "0" - } + "mapRendering": [ + { + "location": [ + "point" + ] + }, + { + "color": { + "render": "#ccc" + }, + "width": { + "render": "0" + } + } + ] } ], "enableShareScreen": false, From be3ac93ecae43e3ec9b87a69998064351489a5e3 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 4 Nov 2021 22:34:22 +0100 Subject: [PATCH 87/95] Fix rotation attribute, small fixes to hackerspace theme --- Models/ThemeConfig/PointRenderingConfig.ts | 2 +- assets/themes/hackerspaces/hackerspaces.json | 5 +---- langs/themes/de.json | 7 ------- langs/themes/en.json | 7 ------- 4 files changed, 2 insertions(+), 19 deletions(-) diff --git a/Models/ThemeConfig/PointRenderingConfig.ts b/Models/ThemeConfig/PointRenderingConfig.ts index 666f9d047..90fde701d 100644 --- a/Models/ThemeConfig/PointRenderingConfig.ts +++ b/Models/ThemeConfig/PointRenderingConfig.ts @@ -132,7 +132,7 @@ export default class PointRenderingConfig extends WithContextLoader { return undefined; } return new VariableUiElement(tags.map(tags => { - const rotation = self.rotation?.GetRenderValue(tags)?.txt ?? "0deg" + const rotation = Utils.SubstituteKeys(self.rotation?.GetRenderValue(tags)?.txt ?? "0deg", tags) const htmlDefs = Utils.SubstituteKeys(self.icon.GetRenderValue(tags)?.txt, tags) let defaultPin : BaseUIElement = undefined diff --git a/assets/themes/hackerspaces/hackerspaces.json b/assets/themes/hackerspaces/hackerspaces.json index f177c4591..3b6ded699 100644 --- a/assets/themes/hackerspaces/hackerspaces.json +++ b/assets/themes/hackerspaces/hackerspaces.json @@ -224,10 +224,7 @@ "hackerspace=makerspace" ] }, - "then": { - "en": "./assets/themes/hackerspaces/led.png", - "de": "./assets/themes/hackerspaces/led.png" - } + "then": "./assets/themes/hackerspaces/led.png" } ] }, diff --git a/langs/themes/de.json b/langs/themes/de.json index f375807f5..aa51b8a74 100644 --- a/langs/themes/de.json +++ b/langs/themes/de.json @@ -769,13 +769,6 @@ "layers": { "0": { "description": "Hackerspace", - "icon": { - "mappings": { - "0": { - "then": "./assets/themes/hackerspaces/led.png" - } - } - }, "name": "Hackerspace", "presets": { "0": { diff --git a/langs/themes/en.json b/langs/themes/en.json index cedade2d0..37cd65603 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -809,13 +809,6 @@ "layers": { "0": { "description": "Hackerspace", - "icon": { - "mappings": { - "0": { - "then": "./assets/themes/hackerspaces/led.png" - } - } - }, "name": "Hackerspace", "presets": { "0": { From 2c466455814fc045ddc11939378b5e44e961d177 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 5 Nov 2021 01:19:27 +0100 Subject: [PATCH 88/95] UK-themes: add inspire polygons to detect addresses with, various small fixes --- Logic/ExtraFunction.ts | 8 +-- Logic/GeoOperations.ts | 2 + Models/ThemeConfig/LayerConfig.ts | 4 +- Models/ThemeConfig/PointRenderingConfig.ts | 4 ++ UI/AllThemesGui.ts | 11 +++- assets/themes/speelplekken/speelplekken.json | 5 -- assets/themes/uk_addresses/uk_addresses.json | 60 ++++++++++++++++---- 7 files changed, 71 insertions(+), 23 deletions(-) diff --git a/Logic/ExtraFunction.ts b/Logic/ExtraFunction.ts index df7e3a585..a3e12567f 100644 --- a/Logic/ExtraFunction.ts +++ b/Logic/ExtraFunction.ts @@ -54,10 +54,10 @@ export class ExtraFunction { private static readonly OverlapFunc = new ExtraFunction( { name: "overlapWith", - doc: "Gives a list of features from the specified layer which this feature (partly) overlaps with. " + - "If the current feature is a point, all features that embed the point are given. " + + doc: "Gives a list of features from the specified layer which this feature (partly) overlaps with. A point which is embedded in the feature is detected as well." + + "If the current feature is a point, all features that this point is embeded in are given.\n\n" + "The returned value is `{ feat: GeoJSONFeature, overlap: number}[]` where `overlap` is the overlapping surface are (in m²) for areas, the overlapping length (in meter) if the current feature is a line or `undefined` if the current feature is a point.\n" + - "The resulting list is sorted in descending order by overlap. The feature with the most overlap will thus be the first in the list" + + "The resulting list is sorted in descending order by overlap. The feature with the most overlap will thus be the first in the list\n" + "\n" + "For example to get all objects which overlap or embed from a layer, use `_contained_climbing_routes_properties=feat.overlapWith('climbing_route')`", args: ["...layerIds - one or more layer ids of the layer from which every feature is checked for overlap)"] @@ -233,7 +233,7 @@ export class ExtraFunction { return new Combine([ ExtraFunction.intro, - new List(ExtraFunction.allFuncs.map(func => func._name)), + new List(ExtraFunction.allFuncs.map(func => `[${func._name}](#${func._name})`)), ...elems ]); } diff --git a/Logic/GeoOperations.ts b/Logic/GeoOperations.ts index b5c9e4c45..a4fd2ea35 100644 --- a/Logic/GeoOperations.ts +++ b/Logic/GeoOperations.ts @@ -37,8 +37,10 @@ export class GeoOperations { * The features with which 'feature' overlaps, are returned together with their overlap area in m² * * If 'feature' is a LineString, the features in which this feature is (partly) embedded is returned, the overlap length in meter is given + * If 'feature' is a Polygon, overlapping points and points within the polygon will be returned * * If 'feature' is a point, it will return every feature the point is embedded in. Overlap will be undefined + * */ static calculateOverlap(feature: any, otherFeatures: any[]): { feat: any, overlap: number }[] { diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts index 3ef796cd7..5c8090d61 100644 --- a/Models/ThemeConfig/LayerConfig.ts +++ b/Models/ThemeConfig/LayerConfig.ts @@ -192,11 +192,11 @@ export default class LayerConfig extends WithContextLoader { } this.mapRendering = json.mapRendering - .filter(r => r["icon"] !== undefined || r["label"] !== undefined) + .filter(r => r["location"] !== undefined) .map((r, i) => new PointRenderingConfig(r, context + ".mapRendering[" + i + "]")) this.lineRendering = json.mapRendering - .filter(r => r["icon"] === undefined && r["label"] === undefined) + .filter(r => r["location"] === undefined) .map((r, i) => new LineRenderingConfig(r, context + ".mapRendering[" + i + "]")) diff --git a/Models/ThemeConfig/PointRenderingConfig.ts b/Models/ThemeConfig/PointRenderingConfig.ts index 90fde701d..911a2ed54 100644 --- a/Models/ThemeConfig/PointRenderingConfig.ts +++ b/Models/ThemeConfig/PointRenderingConfig.ts @@ -39,6 +39,10 @@ export default class PointRenderingConfig extends WithContextLoader { throw `A point rendering has an invalid location: '${l}' is not one of ${Array.from(allowed).join(", ")} (at ${context}.location)` } }) + + if(json.icon === undefined && json.label === undefined){ + throw `A point rendering should define at least an icon or a label` + } if(this.location.size == 0){ throw "A pointRendering should have at least one 'location' to defined where it should be rendered. (At "+context+".location)" diff --git a/UI/AllThemesGui.ts b/UI/AllThemesGui.ts index be4e0d6e4..ec5e2481d 100644 --- a/UI/AllThemesGui.ts +++ b/UI/AllThemesGui.ts @@ -7,11 +7,14 @@ import UserRelatedState from "../Logic/State/UserRelatedState"; import {Utils} from "../Utils"; import LanguagePicker from "./LanguagePicker"; import IndexText from "./BigComponents/IndexText"; -import {feature} from "@turf/turf"; import FeaturedMessage from "./BigComponents/FeaturedMessage"; +import {AllKnownLayouts} from "../Customizations/AllKnownLayouts"; export default class AllThemesGui { constructor() { + + try{ + new FixedUiElement("").AttachTo("centermessage") const state = new UserRelatedState(undefined); const intro = new Combine([ @@ -30,5 +33,9 @@ export default class AllThemesGui { ]).SetClass("block m-5 lg:w-3/4 lg:ml-40") .SetStyle("pointer-events: all;") .AttachTo("topleft-tools"); + }catch (e) { + new FixedUiElement("Seems like no layers are compiled - check the output of `npm run generate:layeroverview`. Is this visible online? Contact pietervdvn immediately!").SetClass("alert") + .AttachTo("centermessage") + } } -} \ No newline at end of file +} diff --git a/assets/themes/speelplekken/speelplekken.json b/assets/themes/speelplekken/speelplekken.json index 175f7685b..ad8255ed4 100644 --- a/assets/themes/speelplekken/speelplekken.json +++ b/assets/themes/speelplekken/speelplekken.json @@ -38,11 +38,6 @@ "render": "1" }, "mapRendering": [ - { - "location": [ - "point" - ] - }, { "color": "#444444", "width": { diff --git a/assets/themes/uk_addresses/uk_addresses.json b/assets/themes/uk_addresses/uk_addresses.json index fc980965a..e289c2981 100644 --- a/assets/themes/uk_addresses/uk_addresses.json +++ b/assets/themes/uk_addresses/uk_addresses.json @@ -51,10 +51,37 @@ } ], "layers": [ + { + "id": "raw_inspire_polygons", + "source": { + "geoJson": "https://osm-uk-addresses.russss.dev/inspire/{z}/{x}/{y}.json", + "osmTags": "inspireid~*", + "geoJsonZoomLevel": 18, + "isOsmCache": false + }, + "minzoom": 18, + "calculatedTags": [ + "_has_address=feat.overlapWith('addresses').length > 0" + ], + "#mapRendering": [ + { + "width": 2, + "color": { + "render": "#00f", + "mappings": [ + { + "if": "_has_address=true", + "then": "#0f0" + } + ] + } + } + ], + "mapRendering": [] + }, { "id": "to_import", "source": { - "#geoJson": "https://raw.githubusercontent.com/pietervdvn/MapComplete/develop/assets/themes/uk_addresses/islington_small_piece.geojson", "geoJson": "https://osm-uk-addresses.russss.dev/addresses/{z}/{x}/{y}.json", "osmTags": "inspireid~*", "geoJsonZoomLevel": 16, @@ -73,18 +100,26 @@ { "id": "uk_addresses_embedding_outline", "render": "An outline embedding this point with an address already exists in OpenStreetMap.
    This object has address {_embedding_object:addr:street} {_embedding_object:addr:housenumber}", + "mappings": [{ + "if": "_embedding_object:id=true", + "then": "The INSPIRE-polygon containing this point has at least one address contained" + },{ + "if": "_embedding_object:id=false", + "then": "The INSPIRE-polygon containing this point has no addresses contained" + }], "condition": "_embedding_object:id~*" }, { "id": "uk_addresses_import_button", - "render": "{import_button(ref:inspireid=$inspireid, Add this address, ./assets/themes/uk_addresses/housenumber_add.svg)}" + "render": "{import_button(addresses,ref:inspireid=$inspireid, Add this address, ./assets/themes/uk_addresses/housenumber_add.svg)}" } ], "calculatedTags": [ "_embedding_object=feat.overlapWith('addresses')[0]?.feat?.properties ?? null", "_embedding_object:addr:housenumber=JSON.parse(feat.properties._embedding_object)?.['addr:housenumber']", "_embedding_object:addr:street=JSON.parse(feat.properties._embedding_object)?.['addr:street']", - "_embedding_object:id=JSON.parse(feat.properties._embedding_object)?.id" + "_embedding_inspire_polygon_has_address=feat.overlapWith('raw_inspire_polygons')[0]?.feat?.properties?._has_address", + "_embedding_object:id=feat.get('_embedding_object')?.id ?? feat.properties._embedding_inspire_polygon_has_address" ], "filter": [ { @@ -95,7 +130,12 @@ "osmTags": { "and": [ "_imported=", - "_embedding_object:id=" + { + "or": [ + "_embedding_object:id=", + "_embedding_object:id=false" + ] + } ] } } @@ -108,7 +148,12 @@ "render": "./assets/themes/uk_addresses/housenumber_unknown.svg", "mappings": [ { - "if": "_embedding_object:id~*", + "if": { + "and": [ + "_embedding_object:id~*", + "_embedding_object:id!=false" + ] + }, "then": "./assets/themes/uk_addresses/housenumber_unknown_small.svg" }, { @@ -302,11 +347,6 @@ } }, "mapRendering": [ - { - "location": [ - "point" - ] - }, { "color": { "render": "#ccc" From 8ea6f494bf26953945b38fddf2fc60f7d98aaa2a Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 5 Nov 2021 01:20:33 +0100 Subject: [PATCH 89/95] Documentation regeneration --- Docs/CalculatedTags.md | 17 ++-- Docs/SpecialRenderings.md | 9 +- Docs/TagInfo/mapcomplete_aed.json | 2 +- Docs/TagInfo/mapcomplete_cafes_and_pubs.json | 2 +- .../mapcomplete_charging_stations.json | 94 +++++++++---------- Docs/TagInfo/mapcomplete_food.json | 2 +- Docs/TagInfo/mapcomplete_fritures.json | 4 +- Docs/TagInfo/mapcomplete_hackerspaces.json | 2 +- .../mapcomplete_observation_towers.json | 2 +- 9 files changed, 71 insertions(+), 63 deletions(-) diff --git a/Docs/CalculatedTags.md b/Docs/CalculatedTags.md index 02345f1b7..07f334db6 100644 --- a/Docs/CalculatedTags.md +++ b/Docs/CalculatedTags.md @@ -166,12 +166,12 @@ The above code will be executed for every feature in the layer. The feature is a Some advanced functions are available on **feat** as well: - - distanceTo - - overlapWith - - closest - - closestn - - memberships - - get + - [distanceTo](#distanceTo) + - [overlapWith](#overlapWith) + - [closest](#closest) + - [closestn](#closestn) + - [memberships](#memberships) + - [get](#get) ### distanceTo @@ -182,8 +182,11 @@ Some advanced functions are available on **feat** as well: ### overlapWith - Gives a list of features from the specified layer which this feature (partly) overlaps with. If the current feature is a point, all features that embed the point are given. The returned value is `{ feat: GeoJSONFeature, overlap: number}[]` where `overlap` is the overlapping surface are (in m²) for areas, the overlapping length (in meter) if the current feature is a line or `undefined` if the current feature is a point. + Gives a list of features from the specified layer which this feature (partly) overlaps with. A point which is embedded in the feature is detected as well.If the current feature is a point, all features that this point is embeded in are given. + +The returned value is `{ feat: GeoJSONFeature, overlap: number}[]` where `overlap` is the overlapping surface are (in m²) for areas, the overlapping length (in meter) if the current feature is a line or `undefined` if the current feature is a point. The resulting list is sorted in descending order by overlap. The feature with the most overlap will thus be the first in the list + For example to get all objects which overlap or embed from a layer, use `_contained_climbing_routes_properties=feat.overlapWith('climbing_route')` 0. ...layerIds - one or more layer ids of the layer from which every feature is checked for overlap) diff --git a/Docs/SpecialRenderings.md b/Docs/SpecialRenderings.md index af4a3948b..35d03abc0 100644 --- a/Docs/SpecialRenderings.md +++ b/Docs/SpecialRenderings.md @@ -219,7 +219,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be #### Specifying which tags to copy or add -The first argument of the import button takes a `;`-seperated list of tags to add. +The argument `tags` of the import button takes a `;`-seperated list of tags to add. These can either be a tag to add, such as `amenity=fast_food` or can use a substitution, e.g. `addr:housenumber=$number`. This new point will then have the tags `amenity=fast_food` and `addr:housenumber` with the value that was saved in `number` in the original feature. @@ -232,19 +232,24 @@ Remark that the syntax is slightly different then expected; it uses '$' to note Note that these values can be prepare with javascript in the theme by using a [calculatedTag](calculatedTags.md#calculating-tags-with-javascript) + name | default | description ------ | --------- | ------------- +targetLayer | _undefined_ | The id of the layer where this point should end up. This is not very strict, it will simply result in checking that this layer is shown preventing possible duplicate elements tags | _undefined_ | The tags to add onto the new object - see specification above text | Import this data into OpenStreetMap | The text to show on the button icon | ./assets/svg/addSmall.svg | A nice icon to show in the button minzoom | 18 | How far the contributor must zoom in before being able to import the point +Snap onto layer(s)/replace geometry with this other way | _undefined_ | - If the value corresponding with this key starts with 'way/' and the feature is a LineString or Polygon, the original OSM-way geometry will be changed to match the new geometry + - If a way of the given layer(s) is closeby, will snap the new point onto this way (similar as preset might snap). To show multiple layers to snap onto, use a `;`-seperated list +snap max distance | 5 | The maximum distance that this point will move to snap onto a layer (in meters) #### Example usage - `{import_button(,Import this data into OpenStreetMap,./assets/svg/addSmall.svg,18)}` + `{import_button(,,Import this data into OpenStreetMap,./assets/svg/addSmall.svg,18,,5)}` ### multi_apply diff --git a/Docs/TagInfo/mapcomplete_aed.json b/Docs/TagInfo/mapcomplete_aed.json index 115eb3525..b88de395d 100644 --- a/Docs/TagInfo/mapcomplete_aed.json +++ b/Docs/TagInfo/mapcomplete_aed.json @@ -112,7 +112,7 @@ }, { "key": "wheelchair", - "description": "Layer 'Defibrillators' shows wheelchair=designated with a fixed text, namely 'This place is specially adapated for wheelchair users' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open AED Map')", + "description": "Layer 'Defibrillators' shows wheelchair=designated with a fixed text, namely 'This place is specially adapted for wheelchair users' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open AED Map')", "value": "designated" }, { diff --git a/Docs/TagInfo/mapcomplete_cafes_and_pubs.json b/Docs/TagInfo/mapcomplete_cafes_and_pubs.json index 630d8e52b..0e002e665 100644 --- a/Docs/TagInfo/mapcomplete_cafes_and_pubs.json +++ b/Docs/TagInfo/mapcomplete_cafes_and_pubs.json @@ -103,7 +103,7 @@ }, { "key": "wheelchair", - "description": "Layer 'Cafés and pubs' shows wheelchair=designated with a fixed text, namely 'This place is specially adapated for wheelchair users' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cafés and pubs')", + "description": "Layer 'Cafés and pubs' shows wheelchair=designated with a fixed text, namely 'This place is specially adapted for wheelchair users' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cafés and pubs')", "value": "designated" }, { diff --git a/Docs/TagInfo/mapcomplete_charging_stations.json b/Docs/TagInfo/mapcomplete_charging_stations.json index d1c40a915..41754afc4 100644 --- a/Docs/TagInfo/mapcomplete_charging_stations.json +++ b/Docs/TagInfo/mapcomplete_charging_stations.json @@ -48,7 +48,7 @@ }, { "key": "bicycle", - "description": "Layer 'Charging stations' shows bicycle=yes with a fixed text, namely 'bicycles can be charged here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows bicycle=yes with a fixed text, namely 'Bcycles can be charged here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "yes" }, { @@ -427,7 +427,7 @@ }, { "key": "authentication:short_message", - "description": "Layer 'Charging stations' shows authentication:short_message=yes with a fixed text, namely 'Authentication via phone call is available' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows authentication:short_message=yes with a fixed text, namely 'Authentication via SMS is available' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "yes" }, { @@ -543,129 +543,129 @@ }, { "key": "planned:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.", "value": "" }, { "key": "construction:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.", "value": "" }, { "key": "disused:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.", "value": "" }, { "key": "operational_status", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.", + "value": "" + }, + { + "key": "amenity", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "charging_station" + }, + { + "key": "planned:amenity", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.", + "value": "" + }, + { + "key": "construction:amenity", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.", + "value": "" + }, + { + "key": "disused:amenity", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.", + "value": "" + }, + { + "key": "operational_status", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "broken" }, { "key": "amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "charging_station" }, { "key": "planned:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "charging_station" }, { "key": "construction:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.", "value": "" }, { "key": "disused:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.", "value": "" }, { "key": "operational_status", - "description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.", + "description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.", "value": "" }, { "key": "amenity", - "description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key amenity.", "value": "" }, { "key": "planned:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.", "value": "" }, { "key": "construction:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "charging_station" }, { "key": "disused:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.", "value": "" }, { "key": "operational_status", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.", "value": "" }, { "key": "amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key amenity.", "value": "" }, { "key": "planned:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.", "value": "" }, { "key": "construction:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.", "value": "" }, { "key": "disused:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "charging_station" }, { "key": "operational_status", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.", "value": "" }, { "key": "amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key amenity.", + "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key amenity.", "value": "" }, - { - "key": "planned:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.", - "value": "" - }, - { - "key": "construction:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.", - "value": "" - }, - { - "key": "disused:amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.", - "value": "" - }, - { - "key": "operational_status", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.", - "value": "" - }, - { - "key": "amenity", - "description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", - "value": "charging_station" - }, { "key": "parking:fee", "description": "Layer 'Charging stations' shows parking:fee=no with a fixed text, namely 'No additional parking cost while charging' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", diff --git a/Docs/TagInfo/mapcomplete_food.json b/Docs/TagInfo/mapcomplete_food.json index 4666bf436..1bd972575 100644 --- a/Docs/TagInfo/mapcomplete_food.json +++ b/Docs/TagInfo/mapcomplete_food.json @@ -88,7 +88,7 @@ }, { "key": "wheelchair", - "description": "Layer 'Restaurants and fast food' shows wheelchair=designated with a fixed text, namely 'This place is specially adapated for wheelchair users' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Restaurants and fast food')", + "description": "Layer 'Restaurants and fast food' shows wheelchair=designated with a fixed text, namely 'This place is specially adapted for wheelchair users' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Restaurants and fast food')", "value": "designated" }, { diff --git a/Docs/TagInfo/mapcomplete_fritures.json b/Docs/TagInfo/mapcomplete_fritures.json index 54b743f87..1f4161ac1 100644 --- a/Docs/TagInfo/mapcomplete_fritures.json +++ b/Docs/TagInfo/mapcomplete_fritures.json @@ -93,7 +93,7 @@ }, { "key": "wheelchair", - "description": "Layer 'Fries shop' shows wheelchair=designated with a fixed text, namely 'This place is specially adapated for wheelchair users' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')", + "description": "Layer 'Fries shop' shows wheelchair=designated with a fixed text, namely 'This place is specially adapted for wheelchair users' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')", "value": "designated" }, { @@ -418,7 +418,7 @@ }, { "key": "wheelchair", - "description": "Layer 'Restaurants and fast food' shows wheelchair=designated with a fixed text, namely 'This place is specially adapated for wheelchair users' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')", + "description": "Layer 'Restaurants and fast food' shows wheelchair=designated with a fixed text, namely 'This place is specially adapted for wheelchair users' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')", "value": "designated" }, { diff --git a/Docs/TagInfo/mapcomplete_hackerspaces.json b/Docs/TagInfo/mapcomplete_hackerspaces.json index dad544320..bb607f4f3 100644 --- a/Docs/TagInfo/mapcomplete_hackerspaces.json +++ b/Docs/TagInfo/mapcomplete_hackerspaces.json @@ -52,7 +52,7 @@ }, { "key": "wheelchair", - "description": "Layer 'Hackerspace' shows wheelchair=designated with a fixed text, namely 'This place is specially adapated for wheelchair users' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Hackerspaces')", + "description": "Layer 'Hackerspace' shows wheelchair=designated with a fixed text, namely 'This place is specially adapted for wheelchair users' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Hackerspaces')", "value": "designated" }, { diff --git a/Docs/TagInfo/mapcomplete_observation_towers.json b/Docs/TagInfo/mapcomplete_observation_towers.json index f7f7caa24..4a22ae603 100644 --- a/Docs/TagInfo/mapcomplete_observation_towers.json +++ b/Docs/TagInfo/mapcomplete_observation_towers.json @@ -88,7 +88,7 @@ }, { "key": "wheelchair", - "description": "Layer 'Observation towers' shows wheelchair=designated with a fixed text, namely 'This place is specially adapated for wheelchair users' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')", + "description": "Layer 'Observation towers' shows wheelchair=designated with a fixed text, namely 'This place is specially adapted for wheelchair users' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')", "value": "designated" }, { From eb56672663f9b85d05ff185e9a9470585ae9b56b Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 5 Nov 2021 01:23:01 +0100 Subject: [PATCH 90/95] Translation reset --- assets/themes/hackerspaces/hackerspaces.json | 2 +- langs/themes/de.json | 2 +- langs/themes/en.json | 2 +- langs/themes/nl.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/themes/hackerspaces/hackerspaces.json b/assets/themes/hackerspaces/hackerspaces.json index 3b6ded699..c301c7b23 100644 --- a/assets/themes/hackerspaces/hackerspaces.json +++ b/assets/themes/hackerspaces/hackerspaces.json @@ -224,7 +224,7 @@ "hackerspace=makerspace" ] }, - "then": "./assets/themes/hackerspaces/led.png" + "then": "./assets/themes/hackerspaces/led.png" } ] }, diff --git a/langs/themes/de.json b/langs/themes/de.json index aa51b8a74..93b3b2ac1 100644 --- a/langs/themes/de.json +++ b/langs/themes/de.json @@ -1104,7 +1104,7 @@ "uk_addresses": { "description": "Tragen Sie zu OpenStreetMap bei, indem Sie Adressinformationen ausfüllen", "layers": { - "1": { + "2": { "description": "Adressen", "name": "Bekannte Adressen in OSM", "tagRenderings": { diff --git a/langs/themes/en.json b/langs/themes/en.json index 37cd65603..ff3351ae1 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -1328,7 +1328,7 @@ "uk_addresses": { "description": "Contribute to OpenStreetMap by filling out address information", "layers": { - "1": { + "2": { "description": "Addresses", "name": "Known addresses in OSM", "tagRenderings": { diff --git a/langs/themes/nl.json b/langs/themes/nl.json index 47419e1fd..e3eb92270 100644 --- a/langs/themes/nl.json +++ b/langs/themes/nl.json @@ -1051,7 +1051,7 @@ "uk_addresses": { "description": "Draag bij aan OpenStreetMap door adresinformatie in te vullen", "layers": { - "1": { + "2": { "description": "Adressen", "tagRenderings": { "uk_addresses_housenumber": { From 57707181f589acb76bfed0e02cf4c8a8a58fb353 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 5 Nov 2021 01:23:27 +0100 Subject: [PATCH 91/95] Add taginfo doc for streetlighting object --- Docs/TagInfo/mapcomplete_street_lighting.json | 225 ++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 Docs/TagInfo/mapcomplete_street_lighting.json diff --git a/Docs/TagInfo/mapcomplete_street_lighting.json b/Docs/TagInfo/mapcomplete_street_lighting.json new file mode 100644 index 000000000..9a3a67e87 --- /dev/null +++ b/Docs/TagInfo/mapcomplete_street_lighting.json @@ -0,0 +1,225 @@ +{ + "data_format": 1, + "project": { + "name": "MapComplete Street Lighting", + "description": "On this map you can find everything about street lighting", + "project_url": "https://mapcomplete.osm.be/street_lighting", + "doc_url": "https://github.com/pietervdvn/MapComplete/tree/master/assets/themes/", + "icon_url": "https://mapcomplete.osm.be/assets/layers/street_lamps/street_lamp.svg", + "contact_name": "Pieter Vander Vennet, Robin van der Linde", + "contact_email": "pietervdvn@posteo.net" + }, + "tags": [ + { + "key": "highway", + "description": "The MapComplete theme Street Lighting has a layer Street Lamps showing features with this tag", + "value": "street_lamp" + }, + { + "key": "ref", + "description": "Layer 'Street Lamps' shows and asks freeform values for key 'ref' (in the MapComplete.osm.be theme 'Street Lighting')" + }, + { + "key": "support", + "description": "Layer 'Street Lamps' shows support=catenary with a fixed text, namely 'This lamp is suspended using cables' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "catenary" + }, + { + "key": "support", + "description": "Layer 'Street Lamps' shows support=ceiling with a fixed text, namely 'This lamp is mounted on a ceiling' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "ceiling" + }, + { + "key": "support", + "description": "Layer 'Street Lamps' shows support=ground with a fixed text, namely 'This lamp is mounted in the ground' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "ground" + }, + { + "key": "support", + "description": "Layer 'Street Lamps' shows support=pedestal with a fixed text, namely 'This lamp is mounted on a short pole (mostly < 1.5m)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "pedestal" + }, + { + "key": "support", + "description": "Layer 'Street Lamps' shows support=pole with a fixed text, namely 'This lamp is mounted on a pole' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "pole" + }, + { + "key": "support", + "description": "Layer 'Street Lamps' shows support=wall with a fixed text, namely 'This lamp is mounted directly to the wall' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "wall" + }, + { + "key": "support", + "description": "Layer 'Street Lamps' shows support=wall_mount with a fixed text, namely 'This lamp is mounted to the wall using a metal bar' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "wall_mount" + }, + { + "key": "lamp_mount", + "description": "Layer 'Street Lamps' shows lamp_mount=straight_mast with a fixed text, namely 'This lamp sits atop of a straight mast' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "straight_mast" + }, + { + "key": "lamp_mount", + "description": "Layer 'Street Lamps' shows lamp_mount=bent_mast with a fixed text, namely 'This lamp sits at the end of a bent mast' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "bent_mast" + }, + { + "key": "light:method", + "description": "Layer 'Street Lamps' shows light:method=electric with a fixed text, namely 'This lamp is lit electrically' (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "electric" + }, + { + "key": "light:method", + "description": "Layer 'Street Lamps' shows light:method=LED with a fixed text, namely 'This lamp uses LEDs' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "LED" + }, + { + "key": "light:method", + "description": "Layer 'Street Lamps' shows light:method=incandescent with a fixed text, namely 'This lamp uses incandescent lighting' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "incandescent" + }, + { + "key": "light:method", + "description": "Layer 'Street Lamps' shows light:method=halogen with a fixed text, namely 'This lamp uses halogen lighting' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "halogen" + }, + { + "key": "light:method", + "description": "Layer 'Street Lamps' shows light:method=discharge with a fixed text, namely 'This lamp uses discharge lamps (unknown type)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "discharge" + }, + { + "key": "light:method", + "description": "Layer 'Street Lamps' shows light:method=mercury with a fixed text, namely 'This lamp uses a mercury-vapour lamp (lightly blueish)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "mercury" + }, + { + "key": "light:method", + "description": "Layer 'Street Lamps' shows light:method=metal-halide with a fixed text, namely 'This lamp uses metal-halide lamps (bright white)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "metal-halide" + }, + { + "key": "light:method", + "description": "Layer 'Street Lamps' shows light:method=fluorescent with a fixed text, namely 'This lamp uses fluorescent lighting' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "fluorescent" + }, + { + "key": "light:method", + "description": "Layer 'Street Lamps' shows light:method=sodium with a fixed text, namely 'This lamp uses sodium lamps (unknown type)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "sodium" + }, + { + "key": "light:method", + "description": "Layer 'Street Lamps' shows light:method=low_pressure_sodium with a fixed text, namely 'This lamp uses low pressure sodium lamps (monochrome orange)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "low_pressure_sodium" + }, + { + "key": "light:method", + "description": "Layer 'Street Lamps' shows light:method=high_pressure_sodium with a fixed text, namely 'This lamp uses high pressure sodium lamps (orange with white)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "high_pressure_sodium" + }, + { + "key": "light:method", + "description": "Layer 'Street Lamps' shows light:method=gas with a fixed text, namely 'This lamp is lit using gas' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "gas" + }, + { + "key": "light:colour", + "description": "Layer 'Street Lamps' shows and asks freeform values for key 'light:colour' (in the MapComplete.osm.be theme 'Street Lighting')" + }, + { + "key": "light:colour", + "description": "Layer 'Street Lamps' shows light:colour=white with a fixed text, namely 'This lamp emits white light' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "white" + }, + { + "key": "light:colour", + "description": "Layer 'Street Lamps' shows light:colour=green with a fixed text, namely 'This lamp emits green light' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "green" + }, + { + "key": "light:colour", + "description": "Layer 'Street Lamps' shows light:colour=orange with a fixed text, namely 'This lamp emits orange light' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "orange" + }, + { + "key": "light:count", + "description": "Layer 'Street Lamps' shows and asks freeform values for key 'light:count' (in the MapComplete.osm.be theme 'Street Lighting')" + }, + { + "key": "light:count", + "description": "Layer 'Street Lamps' shows light:count=1 with a fixed text, namely 'This lamp has 1 fixture' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "1" + }, + { + "key": "light:count", + "description": "Layer 'Street Lamps' shows light:count=2 with a fixed text, namely 'This lamp has 2 fixtures' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "2" + }, + { + "key": "light:lit", + "description": "Layer 'Street Lamps' shows light:lit=dusk-dawn with a fixed text, namely 'This lamp is lit at night' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "dusk-dawn" + }, + { + "key": "light:lit", + "description": "Layer 'Street Lamps' shows light:lit=24/7 with a fixed text, namely 'This lamp is lit 24/7' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "24/7" + }, + { + "key": "light:lit", + "description": "Layer 'Street Lamps' shows light:lit=motion with a fixed text, namely 'This lamp is lit based on motion' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "motion" + }, + { + "key": "light:lit", + "description": "Layer 'Street Lamps' shows light:lit=demand with a fixed text, namely 'This lamp is lit based on demand (e.g. with a pushbutton)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "demand" + }, + { + "key": "light:direction", + "description": "Layer 'Street Lamps' shows and asks freeform values for key 'light:direction' (in the MapComplete.osm.be theme 'Street Lighting')" + }, + { + "key": "lit", + "description": "Layer 'Lit streets' shows lit=yes with a fixed text, namely 'This street is lit' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "yes" + }, + { + "key": "lit", + "description": "Layer 'Lit streets' shows lit=no with a fixed text, namely 'This street is not lit' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "no" + }, + { + "key": "lit", + "description": "Layer 'Lit streets' shows lit=sunset-sunrise with a fixed text, namely 'This street is lit at night' (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "sunset-sunrise" + }, + { + "key": "lit", + "description": "Layer 'Lit streets' shows lit=24/7 with a fixed text, namely 'This street is lit 24/7' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "24/7" + }, + { + "key": "lit", + "description": "Layer 'All streets' shows lit=yes with a fixed text, namely 'This street is lit' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "yes" + }, + { + "key": "lit", + "description": "Layer 'All streets' shows lit=no with a fixed text, namely 'This street is not lit' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "no" + }, + { + "key": "lit", + "description": "Layer 'All streets' shows lit=sunset-sunrise with a fixed text, namely 'This street is lit at night' (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "sunset-sunrise" + }, + { + "key": "lit", + "description": "Layer 'All streets' shows lit=24/7 with a fixed text, namely 'This street is lit 24/7' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Street Lighting')", + "value": "24/7" + } + ] +} \ No newline at end of file From 5014cfe4c7b7175e4318000370d49e7a384a3790 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Fri, 5 Nov 2021 11:20:34 +0100 Subject: [PATCH 92/95] Added separate colors for lit=no and missing tags, filter driveways --- .../themes/street_lighting/street_lighting.json | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/assets/themes/street_lighting/street_lighting.json b/assets/themes/street_lighting/street_lighting.json index 305eb18ef..8ba9ebd51 100644 --- a/assets/themes/street_lighting/street_lighting.json +++ b/assets/themes/street_lighting/street_lighting.json @@ -31,7 +31,8 @@ "and": [ "highway!=", "lit!=no", - "lit!=" + "lit!=", + "service!=driveway" ] } }, @@ -102,7 +103,9 @@ "nl": "Alle straten" }, "source": { - "osmTags": "highway!=" + "osmTags": { + "and": ["highway!=", "service!=driveway", "highway!=platform"] + } }, "minZoom": 19, "title": { @@ -119,7 +122,15 @@ }, "mapRendering": [ { - "color": "#a9a9a9" + "color": { + "render":"#a9a9a9", + "mappings": [ + { + "if":"lit=no", + "then": "#303030" + } + ] + } } ], "tagRenderings": [ From 0d4c3ed6d97d3e040ae9301947277b1279767385 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 5 Nov 2021 16:52:10 +0100 Subject: [PATCH 93/95] Re-add housename and fixme on UK_addresses --- assets/themes/uk_addresses/uk_addresses.json | 67 ++++++++++++++++++-- 1 file changed, 60 insertions(+), 7 deletions(-) diff --git a/assets/themes/uk_addresses/uk_addresses.json b/assets/themes/uk_addresses/uk_addresses.json index e289c2981..2c4753068 100644 --- a/assets/themes/uk_addresses/uk_addresses.json +++ b/assets/themes/uk_addresses/uk_addresses.json @@ -100,13 +100,16 @@ { "id": "uk_addresses_embedding_outline", "render": "An outline embedding this point with an address already exists in OpenStreetMap.
    This object has address {_embedding_object:addr:street} {_embedding_object:addr:housenumber}", - "mappings": [{ - "if": "_embedding_object:id=true", - "then": "The INSPIRE-polygon containing this point has at least one address contained" - },{ - "if": "_embedding_object:id=false", - "then": "The INSPIRE-polygon containing this point has no addresses contained" - }], + "mappings": [ + { + "if": "_embedding_object:id=true", + "then": "The INSPIRE-polygon containing this point has at least one address contained" + }, + { + "if": "_embedding_object:id=false", + "then": "The INSPIRE-polygon containing this point has no addresses contained" + } + ], "condition": "_embedding_object:id~*" }, { @@ -244,6 +247,33 @@ } ] }, + { + "id": "uk_addresses_housename", + "question": "What is the name of this house?
    This is normally indicated on a plaque.
    Do NOT add names of inhabitants!
    ", + "render": "This house is named {addr:housename}", + "freeform": { + "key": "addr:housename", + "addExtraTags": [ + "nohousename=" + ] + }, + "mappings": [ + { + "if": "nohousename=yes", + "then": "This building has no housename" + }, + { + "if": { + "and": [ + "addr:housename=", + "nohousenumber!=yes" + ] + }, + "then": "This building has no housename", + "hideInAnswer": true + } + ] + }, { "id": "uk_addresses_street", "render": { @@ -279,6 +309,29 @@ "nohousenumber!~yes" ] } + }, + { + "id": "fixme", + "render": "Fixme description{render}", + "question": { + "en": "What should be fixed here? Please explain" + }, + "freeform": { + "key": "fixme" + }, + "mappings": [ + { + "if": "fixme=", + "then": "No fixme - write something here to explain complicated cases" + } + ] + }, + "questions", + { + "id": "address-sign-image", + "render": { + "en": "{image_carousel(image:address)}
    {image_upload(image:address, Add image of the address)}" + } } ], "mapRendering": [ From ae26ee5383664db25483324b8734288de041c8f5 Mon Sep 17 00:00:00 2001 From: riQQ Date: Fri, 5 Nov 2021 20:08:26 +0100 Subject: [PATCH 94/95] Add unclassified highways to Sidewalks theme In the hierarchy, unclassified highways are between residential and tertiary / secondary, which are already considered in the theme. --- assets/themes/sidewalks/sidewalks.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/themes/sidewalks/sidewalks.json b/assets/themes/sidewalks/sidewalks.json index 540cdb57a..f48503f39 100644 --- a/assets/themes/sidewalks/sidewalks.json +++ b/assets/themes/sidewalks/sidewalks.json @@ -32,6 +32,7 @@ "osmTags": { "or": [ "highway=residential", + "highway=unclassified", "highway=tertiary", "highway=secondary" ] @@ -177,4 +178,4 @@ "allowSplit": true } ] -} \ No newline at end of file +} From 700cfeba66b3bca706e499651db7b61fca49dd9f Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sat, 6 Nov 2021 14:01:11 +0100 Subject: [PATCH 95/95] Create codeql-analysis.yml --- .github/workflows/codeql-analysis.yml | 70 +++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 .github/workflows/codeql-analysis.yml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 000000000..db4308eb6 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,70 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ develop, master ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ develop ] + schedule: + - cron: '21 18 * * 3' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'javascript' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://git.io/codeql-language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1