From 2e80fff92eb4c223a84cefa75b5e62beff7338c8 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 26 Oct 2021 02:09:04 +0200 Subject: [PATCH 1/8] Small styling tweaks --- UI/Popup/FeatureInfoBox.ts | 6 +- .../public_bookcase/public_bookcase.json | 971 +++++++++--------- 2 files changed, 489 insertions(+), 488 deletions(-) diff --git a/UI/Popup/FeatureInfoBox.ts b/UI/Popup/FeatureInfoBox.ts index d46ebf711b..1f13262462 100644 --- a/UI/Popup/FeatureInfoBox.ts +++ b/UI/Popup/FeatureInfoBox.ts @@ -83,7 +83,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen { layerConfig.allowMove ); }) - ) + ).SetClass("text-base") ); } @@ -94,14 +94,14 @@ export default class FeatureInfoBox extends ScrollableFullScreen { id, layerConfig.deletion )) - )) + ).SetClass("text-base")) } if (layerConfig.allowSplit) { editElements.push( new VariableUiElement(tags.map(tags => tags.id).map(id => new SplitRoadWizard(id)) - )) + ).SetClass("text-base")) } diff --git a/assets/layers/public_bookcase/public_bookcase.json b/assets/layers/public_bookcase/public_bookcase.json index 407751fd95..b8b884c33d 100644 --- a/assets/layers/public_bookcase/public_bookcase.json +++ b/assets/layers/public_bookcase/public_bookcase.json @@ -1,490 +1,491 @@ { - "id": "public_bookcase", - "name": { - "en": "Bookcases", - "nl": "Boekenruilkastjes", - "de": "Bücherschränke", - "fr": "Microbibliothèque", - "ru": "Книжные шкафы", - "it": "Microbiblioteche" + "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" }, - "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", - "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=" - ] - } - } - ] + "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", + "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" + } + ], + "allowMove": true, + "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=" + ] + } + } + ] + } + ] } \ No newline at end of file From bd3395af226387319aa71a28e2f343280dfe0d12 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 26 Oct 2021 21:29:50 +0200 Subject: [PATCH 2/8] Remove small alert ox --- UI/Popup/TagRenderingQuestion.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts index a45172a98c..e0e3a2b73a 100644 --- a/UI/Popup/TagRenderingQuestion.ts +++ b/UI/Popup/TagRenderingQuestion.ts @@ -108,7 +108,7 @@ export default class TagRenderingQuestion extends Combine { const saveButton = new Combine([ options.saveButtonConstr(inputElement.GetValue()), - new Toggle(Translations.t.general.testing, undefined, State.state.featureSwitchIsTesting).SetClass("alert") + new Toggle(Translations.t.general.testing.SetClass("alert"), undefined, State.state.featureSwitchIsTesting) ]) let bottomTags: BaseUIElement; From 78dbe0baa22676dc3c1acd1552b857f5b3bbf757 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 26 Oct 2021 22:53:27 +0200 Subject: [PATCH 3/8] Fix 'addExtraTags' in mappings --- .../Json/TagRenderingConfigJson.ts | 8 + Models/ThemeConfig/TagRenderingConfig.ts | 25 +- UI/Popup/TagRenderingQuestion.ts | 10 +- .../charging_station/charging_station.json | 4514 ++++++++--------- .../cycleways_and_roads.json | 1 - 5 files changed, 2219 insertions(+), 2339 deletions(-) diff --git a/Models/ThemeConfig/Json/TagRenderingConfigJson.ts b/Models/ThemeConfig/Json/TagRenderingConfigJson.ts index 532690af70..6d35eff4ff 100644 --- a/Models/ThemeConfig/Json/TagRenderingConfigJson.ts +++ b/Models/ThemeConfig/Json/TagRenderingConfigJson.ts @@ -83,6 +83,7 @@ export interface TagRenderingConfigJson { * Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes */ mappings?: { + /** * If this condition is met, then the text under `then` will be shown. * If no value matches, and the user selects this mapping as an option, then these tags will be uploaded to OSM. @@ -168,6 +169,13 @@ export interface TagRenderingConfigJson { */ ifnot?: AndOrTagConfigJson | string + + /** + * If chosen as answer, these tags will be applied as well onto the object. + * Not compatible with multiAnswer + */ + addExtraTags: string[] + }[] /** diff --git a/Models/ThemeConfig/TagRenderingConfig.ts b/Models/ThemeConfig/TagRenderingConfig.ts index 40b7bdfe14..0b0e804376 100644 --- a/Models/ThemeConfig/TagRenderingConfig.ts +++ b/Models/ThemeConfig/TagRenderingConfig.ts @@ -6,6 +6,7 @@ import {TagUtils} from "../../Logic/Tags/TagUtils"; import {And} from "../../Logic/Tags/And"; import ValidatedTextField from "../../UI/Input/ValidatedTextField"; import {Utils} from "../../Utils"; +import {Tag} from "../../Logic/Tags/Tag"; /*** * The parsed version of TagRenderingConfigJSON @@ -36,6 +37,7 @@ export default class TagRenderingConfig { readonly ifnot?: TagsFilter, readonly then: Translation readonly hideInAnswer: boolean | TagsFilter + readonly addExtraTags: Tag[] }[] readonly roaming: boolean; @@ -119,21 +121,24 @@ export default class TagRenderingConfig { this.mappings = json.mappings.map((mapping, i) => { - + const ctx = `${context}.mapping[${i}]` if (mapping.then === undefined) { - throw `${context}.mapping[${i}]: Invalid mapping: if without body` + throw `${ctx}: Invalid mapping: if without body` } if (mapping.ifnot !== undefined && !this.multiAnswer) { - throw `${context}.mapping[${i}]: Invalid mapping: ifnot defined, but the tagrendering is not a multianswer` + throw `${ctx}: Invalid mapping: ifnot defined, but the tagrendering is not a multianswer` } if (mapping.if === undefined) { - throw `${context}.mapping[${i}]: Invalid mapping: "if" is not defined, but the tagrendering is not a multianswer` + throw `${ctx}: Invalid mapping: "if" is not defined, but the tagrendering is not a multianswer` } if (typeof mapping.if !== "string" && mapping.if["length"] !== undefined) { - throw `${context}.mapping[${i}]: Invalid mapping: "if" is defined as an array. Use {"and": } or {"or": } instead` + throw `${ctx}: Invalid mapping: "if" is defined as an array. Use {"and": } or {"or": } instead` + } + + if(mapping.addExtraTags !== undefined && this.multiAnswer){ + throw `${ctx}: Invalid mapping: got a multi-Answer with addExtraTags; this is not allowed` } - let hideInAnswer: boolean | TagsFilter = false; if (typeof mapping.hideInAnswer === "boolean") { @@ -141,12 +146,12 @@ export default class TagRenderingConfig { } else if (mapping.hideInAnswer !== undefined) { hideInAnswer = TagUtils.Tag(mapping.hideInAnswer, `${context}.mapping[${i}].hideInAnswer`); } - const mappingContext = `${context}.mapping[${i}]` const mp = { - if: TagUtils.Tag(mapping.if, `${mappingContext}.if`), - ifnot: (mapping.ifnot !== undefined ? TagUtils.Tag(mapping.ifnot, `${mappingContext}.ifnot`) : undefined), + if: TagUtils.Tag(mapping.if, `${ctx}.if`), + ifnot: (mapping.ifnot !== undefined ? TagUtils.Tag(mapping.ifnot, `${ctx}.ifnot`) : undefined), then: Translations.T(mapping.then, `{mappingContext}.then`), - hideInAnswer: hideInAnswer + hideInAnswer: hideInAnswer, + addExtraTags: (mapping.addExtraTags??[]).map((str, j) => TagUtils.SimpleTag(str, `${ctx}.addExtraTags[${j}]`)) }; if (this.question) { if (hideInAnswer !== true && mp.if !== undefined && !mp.if.isUsableAsAnswer()) { diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts index e0e3a2b73a..533017447f 100644 --- a/UI/Popup/TagRenderingQuestion.ts +++ b/UI/Popup/TagRenderingQuestion.ts @@ -49,7 +49,7 @@ export default class TagRenderingQuestion extends Combine { const applicableMappingsSrc = UIEventSource.ListStabilized(tags.map(tags => { - const applicableMappings: { if: TagsFilter, then: any, ifnot?: TagsFilter }[] = [] + const applicableMappings: { if: TagsFilter, then: any, ifnot?: TagsFilter, addExtraTags: Tag[] }[] = [] for (const mapping of configuration.mappings ?? []) { if (mapping.hideInAnswer === true) { continue @@ -110,7 +110,7 @@ export default class TagRenderingQuestion extends Combine { options.saveButtonConstr(inputElement.GetValue()), new Toggle(Translations.t.general.testing.SetClass("alert"), undefined, State.state.featureSwitchIsTesting) ]) - + let bottomTags: BaseUIElement; if (options.bottomText !== undefined) { bottomTags = options.bottomText(inputElement.GetValue()) @@ -147,7 +147,7 @@ export default class TagRenderingQuestion extends Combine { private static GenerateInputElement(configuration: TagRenderingConfig, - applicableMappings: { if: TagsFilter, then: any, ifnot?: TagsFilter }[], + applicableMappings: { if: TagsFilter, then: any, ifnot?: TagsFilter, addExtraTags: Tag[] }[], applicableUnit: Unit, tagsSource: UIEventSource) : InputElement { @@ -341,12 +341,16 @@ export default class TagRenderingQuestion extends Combine { mapping: { if: TagsFilter, then: Translation, + addExtraTags: Tag[] }, ifNot?: TagsFilter[]): InputElement { let tagging: TagsFilter = mapping.if; if (ifNot !== undefined) { tagging = new And([mapping.if, ...ifNot]) } + if (mapping.addExtraTags) { + tagging = new And([tagging, ...mapping.addExtraTags]) + } return new FixedInputElement( new SubstitutedTranslation(mapping.then, tagsSource), diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index 3ec94777e3..33dc363adc 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -1,2328 +1,2192 @@ { - "id": "charging_station", - "name": { - "en": "Charging stations", - "nl": "Oplaadpunten", - "de": "Ladestationen", - "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", - "nl": "Oplaadpunten", - "de": "Ladestation", - "it": "Stazione di ricarica", - "ja": "充電ステーション", - "nb_NO": "Ladestasjon", - "ru": "Зарядная станция", - "zh_Hant": "充電站" - } - }, - "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": "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": "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 ,... " - } - } - ] - }, - { - "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=" - ] - } - } - ], - "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 - } - ], - "width": { - "render": "8" - }, - "iconSize": { - "render": "50,50,bottom" - }, - "color": { - "render": "#00f" - }, - "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 + "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" + } + } + ] + }, + { + "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": [ + "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 ,... " + } + } + ] + }, + { + "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 connections are available here?", + "nl": "Welke aansluitingen zijn hier beschikbaar?" + }, + "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": "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" + }, + "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=" + ] + } + } + ], + "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 + } + ], + "width": { + "render": "8" + }, + "iconSize": { + "render": "50,50,bottom" + }, + "color": { + "render": "#00f" + }, + "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/cycleways_and_roads/cycleways_and_roads.json b/assets/layers/cycleways_and_roads/cycleways_and_roads.json index da3f5ba20f..fc33b6d409 100644 --- a/assets/layers/cycleways_and_roads/cycleways_and_roads.json +++ b/assets/layers/cycleways_and_roads/cycleways_and_roads.json @@ -83,7 +83,6 @@ } ] }, - "description": {}, "tagRenderings": [ { "question": { From 1dd0df2a7568761f6711f55863a21aa62f12721e Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 27 Oct 2021 01:18:05 +0200 Subject: [PATCH 4/8] Add slicer script --- scripts/slice.ts | 85 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 scripts/slice.ts diff --git a/scripts/slice.ts b/scripts/slice.ts new file mode 100644 index 0000000000..d7ae5e186e --- /dev/null +++ b/scripts/slice.ts @@ -0,0 +1,85 @@ +import * as fs from "fs"; +import TiledFeatureSource from "../Logic/FeatureSource/TiledFeatureSource/TiledFeatureSource"; +import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSource"; +import * as readline from "readline"; +import ScriptUtils from "./ScriptUtils"; + +async function main(args: string[]) { + + console.log("GeoJSON slicer") + if (args.length < 3) { + console.log("USAGE: ") + return + } + + const inputFile = args[0] + const zoomlevel = Number(args[1]) + const outputDirectory = args[2] + + if (!fs.existsSync(outputDirectory)) { + fs.mkdirSync(outputDirectory) + console.log("Directory created") + } + console.log("Using directory ", outputDirectory) + + + const fileStream = fs.createReadStream(inputFile); + + const rl = readline.createInterface({ + input: fileStream, + crlfDelay: Infinity + }); + // Note: we use the crlfDelay option to recognize all instances of CR LF + // ('\r\n') in input.txt as a single line break. + + const allFeatures = [] + // @ts-ignore + for await (const line of rl) { + // Each line in input.txt will be successively available here as `line`. + try{ + allFeatures.push(JSON.parse(line)) + }catch (e) { + console.error("Could not parse", line) + break + } + if(allFeatures.length % 10000 === 0){ + ScriptUtils.erasableLog("Loaded ", allFeatures.length, "features up till now") + } + } + + console.log("Loaded all", allFeatures.length, "points") + + const keysToRemove = ["ID","STRAATNMID","NISCODE","GEMEENTE","POSTCODE","HERKOMST","APPTNR"] + for (const f of allFeatures) { + for (const keyToRm of keysToRemove) { + delete f.properties[keyToRm] + } + } + + //const knownKeys = Utils.Dedup([].concat(...allFeatures.map(f => Object.keys(f.properties)))) + //console.log("Kept keys: ", knownKeys) + + TiledFeatureSource.createHierarchy( + new StaticFeatureSource(allFeatures, false), + { + minZoomLevel: zoomlevel, + maxZoomLevel: zoomlevel, + maxFeatureCount: Number.MAX_VALUE, + registerTile: tile => { + const path = `${outputDirectory}/tile_${tile.z}_${tile.x}_${tile.y}.geojson` + fs.writeFileSync(path, JSON.stringify({ + "type": "FeatureCollection", + "features": tile.features.data.map(ff => ff.feature) + }, null, " ")) + console.log("Written ", path, "which has ", tile.features.data.length, "features") + } + } + ) + +} + +let args = [...process.argv] +args.splice(0, 2) +main(args).then(_ => { + console.log("All done!") +}); From d5f4572e9a736fb79bfef2d467666035ffd5c7b0 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 27 Oct 2021 03:52:19 +0200 Subject: [PATCH 5/8] Add possibility to load external data as mercator tiles, add bbox tile possibilities, add CRAB and GRB as datasources in the GRB theme --- Logic/BBox.ts | 21 ++- Logic/FeatureSource/FeaturePipeline.ts | 10 +- Logic/FeatureSource/Sources/GeoJsonSource.ts | 16 ++- .../DynamicGeoJsonTileSource.ts | 42 +++--- Logic/GeoOperations.ts | 30 ++++- Models/ThemeConfig/Json/LayerConfigJson.ts | 4 +- Models/ThemeConfig/LayerConfig.ts | 1 + Models/ThemeConfig/SourceConfig.ts | 10 +- UI/SpecialVisualizations.ts | 2 +- assets/themes/grb_import/README.md | 20 +++ assets/themes/{ => grb_import}/grb.json | 39 +++++- scripts/slice.ts | 120 +++++++++++++----- 12 files changed, 254 insertions(+), 61 deletions(-) create mode 100644 assets/themes/grb_import/README.md rename assets/themes/{ => grb_import}/grb.json (80%) diff --git a/Logic/BBox.ts b/Logic/BBox.ts index 0205b15337..78634897b6 100644 --- a/Logic/BBox.ts +++ b/Logic/BBox.ts @@ -1,5 +1,6 @@ import * as turf from "@turf/turf"; import {TileRange, Tiles} from "../Models/TileRange"; +import {GeoOperations} from "./GeoOperations"; export class BBox { @@ -22,7 +23,7 @@ export class BBox { this.minLon = Math.min(this.minLon, coordinate[0]); this.minLat = Math.min(this.minLat, coordinate[1]); } - + this.maxLon = Math.min(this.maxLon, 180) this.maxLat = Math.min(this.maxLat, 90) this.minLon = Math.max(this.minLon, -180) @@ -117,12 +118,12 @@ export class BBox { } pad(factor: number, maxIncrease = 2): BBox { - + const latDiff = Math.min(maxIncrease / 2, Math.abs(this.maxLat - this.minLat) * factor) - const lonDiff =Math.min(maxIncrease / 2, Math.abs(this.maxLon - this.minLon) * factor) + const lonDiff = Math.min(maxIncrease / 2, Math.abs(this.maxLon - this.minLon) * factor) return new BBox([[ this.minLon - lonDiff, - this.minLat - latDiff + this.minLat - latDiff ], [this.maxLon + lonDiff, this.maxLat + latDiff]]) } @@ -161,4 +162,16 @@ export class BBox { const boundslr = Tiles.tile_bounds_lon_lat(lr.z, lr.x, lr.y) return new BBox([].concat(boundsul, boundslr)) } + + toMercator(): { minLat: number, maxLat: number, minLon: number, maxLon: number } { + const [minLon, minLat] = GeoOperations.ConvertWgs84To900913([this.minLon, this.minLat]) + const [maxLon, maxLat] = GeoOperations.ConvertWgs84To900913([this.maxLon, this.maxLat]) + + return { + minLon, maxLon, + minLat, maxLat + } + + + } } \ No newline at end of file diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index b5ea30bd3a..8cd9aae67c 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -98,7 +98,7 @@ export default class FeaturePipeline { this.osmSourceZoomLevel = state.osmApiTileSize.data; const useOsmApi = state.locationControl.map(l => l.zoom > (state.overpassMaxZoom.data ?? 12)) this.relationTracker = new RelationsTracker() - + state.changes.allChanges.addCallbackAndRun(allChanges => { allChanges.filter(ch => ch.id < 0 && ch.changes !== undefined) .map(ch => ch.changes) @@ -205,7 +205,9 @@ export default class FeaturePipeline { neededTiles: neededTilesFromOsm, handleTile: tile => { new RegisteringAllFromFeatureSourceActor(tile) - new SaveTileToLocalStorageActor(tile, tile.tileIndex) + if (tile.layer.layerDef.maxAgeOfCache > 0) { + new SaveTileToLocalStorageActor(tile, tile.tileIndex) + } perLayerHierarchy.get(tile.layer.layerDef.id).registerTile(tile) tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile)) @@ -213,7 +215,9 @@ export default class FeaturePipeline { state: state, markTileVisited: (tileId) => state.filteredLayers.data.forEach(flayer => { - SaveTileToLocalStorageActor.MarkVisited(flayer.layerDef.id, tileId, new Date()) + if (flayer.layerDef.maxAgeOfCache > 0) { + SaveTileToLocalStorageActor.MarkVisited(flayer.layerDef.id, tileId, new Date()) + } self.freshnesses.get(flayer.layerDef.id).addTileLoad(tileId, new Date()) }) }) diff --git a/Logic/FeatureSource/Sources/GeoJsonSource.ts b/Logic/FeatureSource/Sources/GeoJsonSource.ts index be93dedc7a..68f8fab823 100644 --- a/Logic/FeatureSource/Sources/GeoJsonSource.ts +++ b/Logic/FeatureSource/Sources/GeoJsonSource.ts @@ -7,6 +7,7 @@ import {Utils} from "../../../Utils"; import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; import {Tiles} from "../../../Models/TileRange"; import {BBox} from "../../BBox"; +import {GeoOperations} from "../../GeoOperations"; export default class GeoJsonSource implements FeatureSourceForLayer, Tiled { @@ -14,7 +15,6 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled { public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; public readonly name; public readonly isOsmCache: boolean - private onFail: ((errorMsg: any, url: string) => void) = undefined; private readonly seenids: Set = new Set() public readonly layer: FilteredLayer; @@ -44,10 +44,20 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled { let url = flayer.layerDef.source.geojsonSource.replace("{layer}", flayer.layerDef.id); if (zxy !== undefined) { const [z, x, y] = zxy; + let tile_bbox = BBox.fromTile(z, x, y) + let bounds : { minLat: number, maxLat: number, minLon: number, maxLon: number } = tile_bbox + if(this.layer.layerDef.source.mercatorCrs){ + bounds = tile_bbox.toMercator() + } url = url .replace('{z}', "" + z) .replace('{x}', "" + x) .replace('{y}', "" + y) + .replace('{y_min}',""+bounds.minLat) + .replace('{y_max}',""+bounds.maxLat) + .replace('{x_min}',""+bounds.minLon) + .replace('{x_max}',""+bounds.maxLon) + this.tileIndex = Tiles.tile_index(z, x, y) this.bbox = BBox.fromTile(z, x, y) } else { @@ -71,6 +81,10 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled { if(json.features === undefined || json.features === null){ return; } + + if(self.layer.layerDef.source.mercatorCrs){ + json = GeoOperations.GeoJsonToWGS84(json) + } const time = new Date(); const newFeatures: { feature: any, freshness: Date } [] = [] diff --git a/Logic/FeatureSource/TiledFeatureSource/DynamicGeoJsonTileSource.ts b/Logic/FeatureSource/TiledFeatureSource/DynamicGeoJsonTileSource.ts index 5032f53eaa..21aeec1c1d 100644 --- a/Logic/FeatureSource/TiledFeatureSource/DynamicGeoJsonTileSource.ts +++ b/Logic/FeatureSource/TiledFeatureSource/DynamicGeoJsonTileSource.ts @@ -20,24 +20,28 @@ export default class DynamicGeoJsonTileSource extends DynamicTileSource { if (source.geojsonSource === undefined) { throw "Invalid layer: geojsonSource expected" } - - const whitelistUrl = source.geojsonSource - .replace("{z}", ""+source.geojsonZoomLevel) - .replace("{x}_{y}.geojson", "overview.json") - .replace("{layer}",layer.layerDef.id) - + let whitelist = undefined - Utils.downloadJson(whitelistUrl).then( - json => { - const data = new Map>(); - for (const x in json) { - data.set(Number(x), new Set(json[x])) + if (source.geojsonSource.indexOf("{x}_{y}.geojson") > 0) { + + const whitelistUrl = source.geojsonSource + .replace("{z}", "" + source.geojsonZoomLevel) + .replace("{x}_{y}.geojson", "overview.json") + .replace("{layer}", layer.layerDef.id) + + Utils.downloadJson(whitelistUrl).then( + json => { + const data = new Map>(); + for (const x in json) { + data.set(Number(x), new Set(json[x])) + } + console.log("The whitelist is", data, "based on ", json, "from", whitelistUrl) + whitelist = data } - whitelist = data - } - ).catch(err => { - console.warn("No whitelist found for ", layer.layerDef.id, err) - }) + ).catch(err => { + console.warn("No whitelist found for ", layer.layerDef.id, err) + }) + } const seenIds = new Set(); const blackList = new UIEventSource(seenIds) @@ -45,14 +49,14 @@ export default class DynamicGeoJsonTileSource extends DynamicTileSource { layer, source.geojsonZoomLevel, (zxy) => { - if(whitelist !== undefined){ + if (whitelist !== undefined) { const isWhiteListed = whitelist.get(zxy[1])?.has(zxy[2]) - if(!isWhiteListed){ + if (!isWhiteListed) { console.log("Not downloading tile", ...zxy, "as it is not on the whitelist") return undefined; } } - + const src = new GeoJsonSource( layer, zxy, diff --git a/Logic/GeoOperations.ts b/Logic/GeoOperations.ts index 87716003ee..09a2881178 100644 --- a/Logic/GeoOperations.ts +++ b/Logic/GeoOperations.ts @@ -226,7 +226,7 @@ export class GeoOperations { /** * Generates the closest point on a way from a given point - * + * * The properties object will contain three values: // - `index`: closest point was found on nth line part, // - `dist`: distance between pt and the closest point (in kilometer), @@ -283,6 +283,34 @@ export class GeoOperations { return headerValuesOrdered.map(v => JSON.stringify(v)).join(",") + "\n" + lines.join("\n") } + + private static readonly _earthRadius = 6378137; + private static readonly _originShift = 2 * Math.PI * GeoOperations._earthRadius / 2; + + //Converts given lat/lon in WGS84 Datum to XY in Spherical Mercator EPSG:900913 + public static ConvertWgs84To900913(lonLat: [number, number]): [number, number] { + const lon = lonLat[0]; + const lat = lonLat[1]; + const x = lon * GeoOperations._originShift / 180; + let y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180); + y = y * GeoOperations._originShift / 180; + return [x, y]; + } + +//Converts XY point from (Spherical) Web Mercator EPSG:3785 (unofficially EPSG:900913) to lat/lon in WGS84 Datum + public static Convert900913ToWgs84(lonLat: [number, number]): [number, number] { + const lon = lonLat[0] + const lat = lonLat[1] + const x = 180 * lon / GeoOperations._originShift; + let y = 180 * lat / GeoOperations._originShift; + y = 180 / Math.PI * (2 * Math.atan(Math.exp(y * Math.PI / 180)) - Math.PI / 2); + return [x, y]; + } + + public static GeoJsonToWGS84(geojson){ + return turf.toWgs84(geojson) + } + /** * Calculates the intersection between two features. * Returns the length if intersecting a linestring and a (multi)polygon (in meters), returns a surface area (in m²) if intersecting two (multi)polygons diff --git a/Models/ThemeConfig/Json/LayerConfigJson.ts b/Models/ThemeConfig/Json/LayerConfigJson.ts index e13734c8a0..c15c90d4a1 100644 --- a/Models/ThemeConfig/Json/LayerConfigJson.ts +++ b/Models/ThemeConfig/Json/LayerConfigJson.ts @@ -53,6 +53,8 @@ export interface LayerConfigJson { * source: {geoJson: "https://my.source.net/some-tile-geojson-{layer}-{z}-{x}-{y}.geojson", geoJsonZoomLevel: 14} * to use a tiled geojson source. The web server must offer multiple geojsons. {z}, {x} and {y} are substituted by the location; {layer} is substituted with the id of the loaded layer * + * Some API's use a BBOX instead of a tile, this can be used by specifying {y_min}, {y_max}, {x_min} and {x_max} + * Some API's use a mercator-projection (EPSG:900913) instead of WGS84. Set the flag `mercatorCrs: true` in the source for this * * Note that both geojson-options might set a flag 'isOsmCache' indicating that the data originally comes from OSM too * @@ -61,7 +63,7 @@ export interface LayerConfigJson { * While still supported, this is considered deprecated */ source: ({ osmTags: AndOrTagConfigJson | string, overpassScript?: string } | - { osmTags: AndOrTagConfigJson | string, geoJson: string, geoJsonZoomLevel?: number, isOsmCache?: boolean }) & ({ + { osmTags: AndOrTagConfigJson | string, geoJson: string, geoJsonZoomLevel?: number, isOsmCache?: boolean, mercatorCrs?: boolean }) & ({ /** * The maximum amount of seconds that a tile is allowed to linger in the cache */ diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts index ba6ca94b8d..9f49a6c210 100644 --- a/Models/ThemeConfig/LayerConfig.ts +++ b/Models/ThemeConfig/LayerConfig.ts @@ -124,6 +124,7 @@ export default class LayerConfig { geojsonSourceLevel: json.source["geoJsonZoomLevel"], overpassScript: json.source["overpassScript"], isOsmCache: json.source["isOsmCache"], + mercatorCrs: json.source["mercatorCrs"] }, this.id ); diff --git a/Models/ThemeConfig/SourceConfig.ts b/Models/ThemeConfig/SourceConfig.ts index 1d223bd2bd..b378d0acd3 100644 --- a/Models/ThemeConfig/SourceConfig.ts +++ b/Models/ThemeConfig/SourceConfig.ts @@ -1,4 +1,5 @@ import {TagsFilter} from "../../Logic/Tags/TagsFilter"; +import {RegexTag} from "../../Logic/Tags/RegexTag"; export default class SourceConfig { @@ -7,8 +8,10 @@ export default class SourceConfig { public readonly geojsonSource?: string; public readonly geojsonZoomLevel?: number; public readonly isOsmCacheLayer: boolean; + public readonly mercatorCrs: boolean; constructor(params: { + mercatorCrs?: boolean; osmTags?: TagsFilter, overpassScript?: string, geojsonSource?: string, @@ -33,10 +36,15 @@ export default class SourceConfig { console.error(params) throw `Source said it is a OSM-cached layer, but didn't define the actual source of the cache (in context ${context})` } - this.osmTags = params.osmTags; + if(params.geojsonSource !== undefined && params.geojsonSourceLevel !== undefined){ + if(! ["x","y","x_min","x_max","y_min","Y_max"].some(toSearch => params.geojsonSource.indexOf(toSearch) > 0)){ + throw `Source defines a geojson-zoomLevel, but does not specify {x} nor {y} (or equivalent), this is probably a bug (in context ${context})` + }} + this.osmTags = params.osmTags ?? new RegexTag("id",/.*/); this.overpassScript = params.overpassScript; this.geojsonSource = params.geojsonSource; this.geojsonZoomLevel = params.geojsonSourceLevel; this.isOsmCacheLayer = params.isOsmCache ?? false; + this.mercatorCrs = params.mercatorCrs ?? false; } } \ No newline at end of file diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index 1882152965..680b3e571c 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -457,7 +457,7 @@ There are also some technicalities in your theme to keep in mind: 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.")]) } - const tgsSpec = args[0].split(",").map(spec => { + 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 diff --git a/assets/themes/grb_import/README.md b/assets/themes/grb_import/README.md new file mode 100644 index 0000000000..2ed7bfbbfa --- /dev/null +++ b/assets/themes/grb_import/README.md @@ -0,0 +1,20 @@ + GRB Import helper +=================== + + +Preparing the CRAB dataset +-------------------------- + +```` +# The original data is downloaded from https://download.vlaanderen.be/Producten/Detail?id=447&title=CRAB_Adressenlijst# (the GML-file here ) +wget https://downloadagiv.blob.core.windows.net/crab-adressenlijst/GML/CRAB_Adressenlijst_GML.zip + +# Extract the zip file +unzip CRAB_Adressenlijst_GML.zip + +# convert the pesky GML file into geojson +ogr2ogr -progress -t_srs WGS84 -f \"GeoJson\" CRAB.geojson CrabAdr.gml + +# When done, this big file is sliced into tiles with the slicer script +node --max_old_space_size=8000 $(which ts-node) ~/git/MapComplete/scripts/slice.ts CRAB.geojson 18 ~/git/pietervdvn.github.io/CRAB_2021_10_26 +```` \ No newline at end of file diff --git a/assets/themes/grb.json b/assets/themes/grb_import/grb.json similarity index 80% rename from assets/themes/grb.json rename to assets/themes/grb_import/grb.json index 9c62526175..4f0d99d9db 100644 --- a/assets/themes/grb.json +++ b/assets/themes/grb_import/grb.json @@ -22,7 +22,7 @@ "socialImage": "", "layers": [ { - "id": "grb-fixmes", + "id": "osm-fixmes", "name": { "nl": "Fixmes op gebouwen" }, @@ -198,6 +198,43 @@ }, "wayHandling": 2, "presets": [] + }, + { + "id": "crab-addresses 2021-10-26", + "source": { + "osmTags": "HUISNR~*", + "geoJson": "https://raw.githubusercontent.com/pietervdvn/pietervdvn.github.io/master/CRAB_2021_10_26/tile_{z}_{x}_{y}.geojson", + "#geoJson": "https://pietervdvn.github.io/CRAB_2021_10_26/tile_{z}_{x}_{y}.geojson", + "geoJsonZoomLevel": 18, + "maxCacheAge": 0 + }, + "minzoom": 19, + "name": "CRAB-addressen", + "title": "CRAB-adres", + "icon": "circle:#bb3322", + "iconSize": "15,15,center", + "tagRenderings": [ + "all_tags", + { + "id": "import-button", + "render": "{import_button(addr:street=$STRAATNM; addr:housenumber=$HUISNR)}" + } + ] + }, + { + "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": 19, + "tagRenderings": [ + "all_tags" + ] } ], "hideFromOverview": true, diff --git a/scripts/slice.ts b/scripts/slice.ts index d7ae5e186e..74673035b5 100644 --- a/scripts/slice.ts +++ b/scripts/slice.ts @@ -4,11 +4,84 @@ import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSou import * as readline from "readline"; import ScriptUtils from "./ScriptUtils"; +async function readFeaturesFromLineDelimitedJsonFile(inputFile: string): Promise { + const fileStream = fs.createReadStream(inputFile); + + const rl = readline.createInterface({ + input: fileStream, + crlfDelay: Infinity + }); + // Note: we use the crlfDelay option to recognize all instances of CR LF + // ('\r\n') in input.txt as a single line break. + + const allFeatures: any[] = [] + // @ts-ignore + for await (const line of rl) { + try { + allFeatures.push(JSON.parse(line)) + } catch (e) { + console.error("Could not parse", line) + break + } + if (allFeatures.length % 10000 === 0) { + ScriptUtils.erasableLog("Loaded ", allFeatures.length, "features up till now") + } + } + return allFeatures +} + +async function readGeojsonLineByLine(inputFile: string): Promise { + const fileStream = fs.createReadStream(inputFile); + + const rl = readline.createInterface({ + input: fileStream, + crlfDelay: Infinity + }); + // Note: we use the crlfDelay option to recognize all instances of CR LF + // ('\r\n') in input.txt as a single line break. + + const allFeatures: any[] = [] + let featuresSeen = false + // @ts-ignore + for await (let line: string of rl) { + if (!featuresSeen && line.startsWith("\"features\":")) { + featuresSeen = true; + continue; + } + if (!featuresSeen) { + continue + } + if (line.endsWith(",")) { + line = line.substring(0, line.length - 1) + } + + try { + allFeatures.push(JSON.parse(line)) + } catch (e) { + console.error("Could not parse", line) + break + } + if (allFeatures.length % 10000 === 0) { + ScriptUtils.erasableLog("Loaded ", allFeatures.length, "features up till now") + } + } + return allFeatures +} + +async function readFeaturesFromGeoJson(inputFile: string): Promise { + try { + return JSON.parse(fs.readFileSync(inputFile, "UTF-8")).features + } catch (e) { + // We retry, but with a line-by-line approach + return await readGeojsonLineByLine(inputFile) + } +} + async function main(args: string[]) { console.log("GeoJSON slicer") if (args.length < 3) { - console.log("USAGE: ") + console.log("USAGE: ") return } @@ -23,39 +96,24 @@ async function main(args: string[]) { console.log("Using directory ", outputDirectory) - const fileStream = fs.createReadStream(inputFile); - - const rl = readline.createInterface({ - input: fileStream, - crlfDelay: Infinity - }); - // Note: we use the crlfDelay option to recognize all instances of CR LF - // ('\r\n') in input.txt as a single line break. - - const allFeatures = [] - // @ts-ignore - for await (const line of rl) { - // Each line in input.txt will be successively available here as `line`. - try{ - allFeatures.push(JSON.parse(line)) - }catch (e) { - console.error("Could not parse", line) - break - } - if(allFeatures.length % 10000 === 0){ - ScriptUtils.erasableLog("Loaded ", allFeatures.length, "features up till now") - } + let allFeatures: any []; + if (inputFile.endsWith(".geojson")) { + allFeatures = await readFeaturesFromGeoJson(inputFile) + } else { + allFeatures = await readFeaturesFromLineDelimitedJsonFile(inputFile) } - + + console.log("Loaded all", allFeatures.length, "points") - - const keysToRemove = ["ID","STRAATNMID","NISCODE","GEMEENTE","POSTCODE","HERKOMST","APPTNR"] + + const keysToRemove = ["ID", "STRAATNMID", "NISCODE", "GEMEENTE", "POSTCODE", "HERKOMST"] for (const f of allFeatures) { for (const keyToRm of keysToRemove) { delete f.properties[keyToRm] } + delete f.bbox } - + //const knownKeys = Utils.Dedup([].concat(...allFeatures.map(f => Object.keys(f.properties)))) //console.log("Kept keys: ", knownKeys) @@ -67,11 +125,15 @@ async function main(args: string[]) { maxFeatureCount: Number.MAX_VALUE, registerTile: tile => { const path = `${outputDirectory}/tile_${tile.z}_${tile.x}_${tile.y}.geojson` + const features = tile.features.data.map(ff => ff.feature) + features.forEach(f => { + delete f.bbox + }) fs.writeFileSync(path, JSON.stringify({ "type": "FeatureCollection", - "features": tile.features.data.map(ff => ff.feature) + "features": features }, null, " ")) - console.log("Written ", path, "which has ", tile.features.data.length, "features") + ScriptUtils.erasableLog("Written ", path, "which has ", tile.features.data.length, "features") } } ) From bec7ed6da6e4e6077066a81163e76b547d2c1bae Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 27 Oct 2021 19:57:15 +0200 Subject: [PATCH 6/8] Fix loading of themes from external sources --- Logic/DetermineLayout.ts | 18 ++++++++---------- Models/ThemeConfig/LayoutConfig.ts | 1 - index.ts | 2 -- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/Logic/DetermineLayout.ts b/Logic/DetermineLayout.ts index 9af285952e..476131732d 100644 --- a/Logic/DetermineLayout.ts +++ b/Logic/DetermineLayout.ts @@ -18,7 +18,6 @@ export default class DetermineLayout { */ public static async GetLayout(): Promise<[LayoutConfig, string]> { - const loadCustomThemeParam = QueryParameters.GetQueryParameter("userlayout", "false", "If not 'false', a custom (non-official) theme is loaded. This custom layout can be done in multiple ways: \n\n- The hash of the URL contains a base64-encoded .json-file containing the theme definition\n- The hash of the URL contains a lz-compressed .json-file, as generated by the custom theme generator\n- The parameter itself is an URL, in which case that URL will be downloaded. It should point to a .json of a theme") const layoutFromBase64 = decodeURIComponent(loadCustomThemeParam.data); @@ -73,17 +72,13 @@ export default class DetermineLayout { try { - const data = await Utils.downloadJson(link) + const parsed = await Utils.downloadJson(link) + console.log("Got ", parsed) try { - let parsed = data; - if (typeof parsed == "string") { - parsed = JSON.parse(parsed); - } - // Overwrite the id to the url parsed.id = link; - return new LayoutConfig(parsed, false).patchImages(link, data); + return new LayoutConfig(parsed, false).patchImages(link, JSON.stringify(parsed)); } catch (e) { - + console.error(e) DetermineLayout.ShowErrorOnCustomTheme( `${link} is invalid:`, new FixedUiElement(e) @@ -92,6 +87,7 @@ export default class DetermineLayout { } } catch (e) { + console.erorr(e) DetermineLayout.ShowErrorOnCustomTheme( `${link} is invalid - probably not found or invalid JSON:`, new FixedUiElement(e) @@ -107,7 +103,7 @@ export default class DetermineLayout { try { // layoutFromBase64 contains the name of the theme. This is partly to do tracking with goat counter const dedicatedHashFromLocalStorage = LocalStorageSource.Get( - "user-layout-" + userLayoutParam.data.replace(" ", "_") + "user-layout-" + userLayoutParam.data?.replace(" ", "_") ); if (dedicatedHashFromLocalStorage.data?.length < 10) { dedicatedHashFromLocalStorage.setData(undefined); @@ -134,6 +130,7 @@ export default class DetermineLayout { try { json = JSON.parse(Utils.UnMinify(LZString.decompressFromBase64(hash))) } catch (e) { + console.error(e) DetermineLayout.ShowErrorOnCustomTheme("Could not decode the hash", new FixedUiElement("Not a valid (LZ-compressed) JSON")) return null; } @@ -143,6 +140,7 @@ export default class DetermineLayout { userLayoutParam.setData(layoutToUse.id); return [layoutToUse, btoa(Utils.MinifyJSON(JSON.stringify(json)))]; } catch (e) { + console.error(e) if (hash === undefined || hash.length < 10) { DetermineLayout.ShowErrorOnCustomTheme("Could not load a theme from the hash", new FixedUiElement("Hash does not contain data")) } diff --git a/Models/ThemeConfig/LayoutConfig.ts b/Models/ThemeConfig/LayoutConfig.ts index 1d89a104d7..54e2a64543 100644 --- a/Models/ThemeConfig/LayoutConfig.ts +++ b/Models/ThemeConfig/LayoutConfig.ts @@ -304,7 +304,6 @@ export default class LayoutConfig { } rewriting.forEach((value, key) => { console.log("Rewriting", key, "==>", value) - originalJson = originalJson.replace(new RegExp(key, "g"), value) }) return new LayoutConfig(JSON.parse(originalJson), false, "Layout rewriting") diff --git a/index.ts b/index.ts index 92ba9da66b..a383eaac26 100644 --- a/index.ts +++ b/index.ts @@ -33,8 +33,6 @@ if (location.href.startsWith("http://buurtnatuur.be")) { class Init { - - public static Init(layoutToUse: LayoutConfig, encoded: string) { if(layoutToUse === null){ From 8acf85cc552041eed1a225584feab8dac844cea0 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 27 Oct 2021 20:19:45 +0200 Subject: [PATCH 7/8] 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 7d431c7af5..44a2940d3d 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 476131732d..563b8c1a79 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 8f0265bb14..4fc1840c11 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 8cd9aae67c..858ad4283d 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 0c2c9d92ae..ec097accac 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 0a1c67f411..92138b9951 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 83b85f34a2..a6e3cc44bc 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 b9f134565b..248f9a8849 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 f911c0b41c..e151df6c5a 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 e259b4f2e3..f6c551663b 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 1192ff8ee6..5dfa062ead 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 2f957ed15a..e8cfb43dae 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 4f0d99d9db..ed5a02aac3 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 7d805cee45..4bcce8003e 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 aa0aeed0b4..88d6dec279 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 74673035b5..342dc22f90 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 8/8] 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 ed5a02aac3..af9672582d 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" }