diff --git a/android b/android
index b7b29d20e4..a48aaffec4 160000
--- a/android
+++ b/android
@@ -1 +1 @@
-Subproject commit b7b29d20e40bde9144c719a2b59484c04cc79b9f
+Subproject commit a48aaffec4ca59a2129834207e72ee3df85d2cd6
diff --git a/assets/layers/climbing/climbing.json b/assets/layers/climbing/climbing.json
index 7f1b7539b3..9960d2bffe 100644
--- a/assets/layers/climbing/climbing.json
+++ b/assets/layers/climbing/climbing.json
@@ -68,6 +68,7 @@
"ja": "ルートの長さは平均で{canonical(climbing:length)} です",
"nl": "De klimroutes zijn gemiddeld {canonical(climbing:length)} lang"
},
+ "icon": "./assets/themes/climbing/height.svg",
"freeform": {
"key": "climbing:length",
"type": "pfloat"
@@ -426,6 +427,7 @@
"it": "Le vie di arrampicata sportiva qui hanno al massimo {climbing:bolts:max} spit.
Questo è senza le soste e indica quanti rinvii servono a un arrampicatore.
",
"nl": "De sportklimroutes hebben maximum {climbing:bolts:max} haken. Hierbij worden standplaatsen niet meegeteld. Dit geeft aan hoeveel setjes een klimmer nodig heeft.
"
},
+ "icon": "./assets/themes/climbing/carabiner.svg",
"freeform": {
"key": "climbing:bolts:max",
"type": "pnat",
diff --git a/assets/layers/tree_node/Onroerend_Erfgoed_logo_without_text.svg b/assets/layers/tree/Onroerend_Erfgoed_logo_without_text.svg
similarity index 100%
rename from assets/layers/tree_node/Onroerend_Erfgoed_logo_without_text.svg
rename to assets/layers/tree/Onroerend_Erfgoed_logo_without_text.svg
diff --git a/assets/layers/tree_node/Onroerend_Erfgoed_logo_without_text.svg.license b/assets/layers/tree/Onroerend_Erfgoed_logo_without_text.svg.license
similarity index 100%
rename from assets/layers/tree_node/Onroerend_Erfgoed_logo_without_text.svg.license
rename to assets/layers/tree/Onroerend_Erfgoed_logo_without_text.svg.license
diff --git a/assets/layers/tree_node/broadleaved.svg b/assets/layers/tree/broadleaved.svg
similarity index 100%
rename from assets/layers/tree_node/broadleaved.svg
rename to assets/layers/tree/broadleaved.svg
diff --git a/assets/layers/tree_node/broadleaved.svg.license b/assets/layers/tree/broadleaved.svg.license
similarity index 100%
rename from assets/layers/tree_node/broadleaved.svg.license
rename to assets/layers/tree/broadleaved.svg.license
diff --git a/assets/layers/tree_node/leafless.svg b/assets/layers/tree/leafless.svg
similarity index 100%
rename from assets/layers/tree_node/leafless.svg
rename to assets/layers/tree/leafless.svg
diff --git a/assets/layers/tree_node/leafless.svg.license b/assets/layers/tree/leafless.svg.license
similarity index 100%
rename from assets/layers/tree_node/leafless.svg.license
rename to assets/layers/tree/leafless.svg.license
diff --git a/assets/layers/tree_node/license_info.json b/assets/layers/tree/license_info.json
similarity index 100%
rename from assets/layers/tree_node/license_info.json
rename to assets/layers/tree/license_info.json
diff --git a/assets/layers/tree_node/needleleaved.svg b/assets/layers/tree/needleleaved.svg
similarity index 100%
rename from assets/layers/tree_node/needleleaved.svg
rename to assets/layers/tree/needleleaved.svg
diff --git a/assets/layers/tree_node/needleleaved.svg.license b/assets/layers/tree/needleleaved.svg.license
similarity index 100%
rename from assets/layers/tree_node/needleleaved.svg.license
rename to assets/layers/tree/needleleaved.svg.license
diff --git a/assets/layers/tree_node/palm.svg b/assets/layers/tree/palm.svg
similarity index 100%
rename from assets/layers/tree_node/palm.svg
rename to assets/layers/tree/palm.svg
diff --git a/assets/layers/tree_node/palm.svg.license b/assets/layers/tree/palm.svg.license
similarity index 100%
rename from assets/layers/tree_node/palm.svg.license
rename to assets/layers/tree/palm.svg.license
diff --git a/assets/layers/tree_node/tree_node.json b/assets/layers/tree/tree.json
similarity index 86%
rename from assets/layers/tree_node/tree_node.json
rename to assets/layers/tree/tree.json
index 5131fb5973..7959b01b98 100644
--- a/assets/layers/tree_node/tree_node.json
+++ b/assets/layers/tree/tree.json
@@ -1,5 +1,5 @@
{
- "id": "tree_node",
+ "id": "tree",
"name": {
"en": "Tree",
"ca": "Arbre",
@@ -96,7 +96,7 @@
},
{
"icon": {
- "render": "./assets/layers/tree_node/unknown.svg",
+ "render": "./assets/layers/tree/unknown.svg",
"mappings": [
{
"if": {
@@ -104,7 +104,7 @@
"leaf_type=broadleaved"
]
},
- "then": "./assets/layers/tree_node/broadleaved.svg"
+ "then": "./assets/layers/tree/broadleaved.svg"
},
{
"if": {
@@ -112,7 +112,7 @@
"leaf_type=needleleaved"
]
},
- "then": "./assets/layers/tree_node/needleleaved.svg"
+ "then": "./assets/layers/tree/needleleaved.svg"
},
{
"if": {
@@ -120,7 +120,7 @@
"leaf_type=palm"
]
},
- "then": "./assets/layers/tree_node/palm.svg"
+ "then": "./assets/layers/tree/palm.svg"
}
]
}
@@ -272,6 +272,7 @@
"freeform": {
"key": "species:wikidata",
"type": "wikidata",
+ "inline": false,
"helperArgs": [
"species",
{
@@ -578,7 +579,7 @@
},
"icon": {
"class": "small",
- "path": "./assets/layers/tree_node/broadleaved.svg"
+ "path": "./assets/layers/tree/broadleaved.svg"
}
},
{
@@ -601,7 +602,7 @@
},
"icon": {
"class": "small",
- "path": "./assets/layers/tree_node/needleleaved.svg"
+ "path": "./assets/layers/tree/needleleaved.svg"
}
},
{
@@ -626,7 +627,7 @@
"hideInAnswer": true,
"icon": {
"class": "small",
- "path": "./assets/layers/tree_node/leafless.svg"
+ "path": "./assets/layers/tree/leafless.svg"
}
},
{
@@ -714,7 +715,7 @@
}
},
{
- "id": "tree_node-name",
+ "id": "tree-name",
"question": {
"en": "Does the tree have a name?",
"ca": "Té nom aquest arbre?",
@@ -819,7 +820,7 @@
},
"icon": {
"class": "small",
- "path": "./assets/layers/tree_node/Onroerend_Erfgoed_logo_without_text.svg"
+ "path": "./assets/layers/tree/Onroerend_Erfgoed_logo_without_text.svg"
}
},
{
@@ -915,7 +916,7 @@
}
},
{
- "id": "tree_node-ref:OnroerendErfgoed",
+ "id": "tree-ref:OnroerendErfgoed",
"question": {
"en": "What is the ID issued by Onroerend Erfgoed Flanders?",
"ca": "Quina és la identificació emesa per Onroerend Erfgoed Flanders?",
@@ -929,17 +930,25 @@
"pt": "Qual é o ID emitido por Onroerend Erfgoed Flanders?"
},
"render": {
- "en": " Onroerend Erfgoed ID: {ref:OnroerendErfgoed} ",
- "ca": " Identifiació Onroerend Erfgoed: {ref:OnroerendErfgoed} ",
- "cs": " Onroerend Erfgoed ID: {ref:OnroerendErfgoed} ",
- "da": " Onroerend Erfgoed ID: {ref:OnroerendErfgoed} ",
- "de": " Onroerend Erfgoed Kennung: {ref:OnroerendErfgoed} ",
- "es": " ID de Onroerend Erfgoed: {ref:OnroerendErfgoed} ",
- "fr": " Identifiant Onroerend Erfgoed : {ref:OnroerendErfgoed} ",
- "it": " ID Onroerend Erfgoed: {ref:OnroerendErfgoed} ",
- "nl": " Onroerend Erfgoed-ID: {ref:OnroerendErfgoed} ",
- "ru": " Onroerend Erfgoed ID: {ref:OnroerendErfgoed} "
+ "before": {
+ "en": "Onroerend Erfgoed ID: ",
+ "ca": "Identifiació Onroerend Erfgoed: ",
+ "cs": "Onroerend Erfgoed ID: ",
+ "da": "Onroerend Erfgoed ID: ",
+ "de": "Onroerend Erfgoed Kennung: ",
+ "es": "ID de Onroerend Erfgoed: ",
+ "fr": "Identifiant Onroerend Erfgoed : ",
+ "it": "ID Onroerend Erfgoed: ",
+ "nl": "Onroerend Erfgoed-ID: ",
+ "ru": "Onroerend Erfgoed ID:"
+ },
+ "special": {
+ "type": "link",
+ "href": "https://id.erfgoed.net/erfgoedobjecten/{ref:OnroerendErfgoed}",
+ "text": "{ref:OnroerendErfgoed}"
+ }
},
+ "icon": "./assets/layers/tree/Onroerend_Erfgoed_logo_without_text.svg",
"freeform": {
"key": "ref:OnroerendErfgoed",
"type": "nat"
@@ -966,16 +975,9 @@
"pt": "Qual é o ID do Wikidata para esta árvore?"
},
"render": {
- "en": " Wikidata: {wikidata} ",
- "ca": " Wikidata{wikidata} ",
- "cs": " Wikidata: {wikidata} ",
- "da": " Wikidata: {wikidata} ",
- "de": " Wikidata: {wikidata} ",
- "es": " Wikidata: {wikidata} ",
- "fr": " Wikidata : {wikidata} ",
- "it": " Wikidata: {wikidata} ",
- "nl": " Wikidata: {wikidata} ",
- "ru": " Wikidata: {wikidata} "
+ "special": {
+ "type": "wikipedia"
+ }
},
"freeform": {
"key": "wikidata",
@@ -990,9 +992,6 @@
}
}
],
- "deletion": {
- "minNeededChangesets": 5
- },
"allowMove": {
"enableRelocation": false,
"enableImproveAccuracy": true
diff --git a/assets/layers/tree_node/unknown.svg b/assets/layers/tree/unknown.svg
similarity index 100%
rename from assets/layers/tree_node/unknown.svg
rename to assets/layers/tree/unknown.svg
diff --git a/assets/layers/tree_node/unknown.svg.license b/assets/layers/tree/unknown.svg.license
similarity index 100%
rename from assets/layers/tree_node/unknown.svg.license
rename to assets/layers/tree/unknown.svg.license
diff --git a/assets/svg/direction_stroke.svg b/assets/svg/direction_stroke.svg
index 521b40b0d3..43a7a145f8 100644
--- a/assets/svg/direction_stroke.svg
+++ b/assets/svg/direction_stroke.svg
@@ -1,9 +1,59 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/assets/themes/nature/nature.json b/assets/themes/nature/nature.json
index 55031965dc..08571b3f80 100644
--- a/assets/themes/nature/nature.json
+++ b/assets/themes/nature/nature.json
@@ -78,7 +78,7 @@
}
},
{
- "builtin": "tree_node",
+ "builtin": "tree",
"override": {
"minzoom": 18,
"isCounted": false,
diff --git a/assets/themes/trees/trees.json b/assets/themes/trees/trees.json
index 3cc32641d9..25d5574e52 100644
--- a/assets/themes/trees/trees.json
+++ b/assets/themes/trees/trees.json
@@ -74,7 +74,7 @@
"Midgard"
],
"layers": [
- "tree_node"
+ "tree"
],
"osmApiTileSize": 18,
"widenFactor": 0.2
diff --git a/langs/layers/ca.json b/langs/layers/ca.json
index 54dd593b5c..e4ac8aee9e 100644
--- a/langs/layers/ca.json
+++ b/langs/layers/ca.json
@@ -12584,7 +12584,7 @@
"render": "Parada de transport públic"
}
},
- "tree_node": {
+ "tree": {
"description": "Una capa que mostra arbres",
"name": "Arbre",
"presets": {
@@ -12685,10 +12685,7 @@
},
"question": "És un arbre de fulla ampla o d'agulla?"
},
- "tree-species-wikidata": {
- "question": "De quina espècie és aquest arbre?"
- },
- "tree_node-name": {
+ "tree-name": {
"mappings": {
"0": {
"then": "L'arbre no té nom."
@@ -12697,13 +12694,17 @@
"question": "Té nom aquest arbre?",
"render": "Nom : {name}"
},
- "tree_node-ref:OnroerendErfgoed": {
+ "tree-ref:OnroerendErfgoed": {
"question": "Quina és la identificació emesa per Onroerend Erfgoed Flanders?",
- "render": " Identifiació Onroerend Erfgoed: {ref:OnroerendErfgoed} "
+ "render": {
+ "before": "Identifiació Onroerend Erfgoed: "
+ }
+ },
+ "tree-species-wikidata": {
+ "question": "De quina espècie és aquest arbre?"
},
"tree_node-wikidata": {
- "question": "Quin és l'identificador de Wikidata d'aquest arbre?",
- "render": " Wikidata{wikidata} "
+ "question": "Quin és l'identificador de Wikidata d'aquest arbre?"
}
},
"title": {
diff --git a/langs/layers/cs.json b/langs/layers/cs.json
index 319fb81956..f43c5248fc 100644
--- a/langs/layers/cs.json
+++ b/langs/layers/cs.json
@@ -13370,7 +13370,7 @@
"render": "Tranzitní zastávka"
}
},
- "tree_node": {
+ "tree": {
"description": "Vrstva zobrazující stromy",
"name": "Strom",
"presets": {
@@ -13477,10 +13477,7 @@
},
"question": "Jedná se o listnatý nebo jehličnatý strom?"
},
- "tree-species-wikidata": {
- "question": "Jaký druh je tento strom?"
- },
- "tree_node-name": {
+ "tree-name": {
"mappings": {
"0": {
"then": "Strom nemá jméno."
@@ -13489,13 +13486,17 @@
"question": "Má strom nějaké jméno?",
"render": "Jméno: {name}"
},
- "tree_node-ref:OnroerendErfgoed": {
+ "tree-ref:OnroerendErfgoed": {
"question": "Jaké je ID vydané společností Onroerend Erfgoed Flanders?",
- "render": " Onroerend Erfgoed ID: {ref:OnroerendErfgoed} "
+ "render": {
+ "before": "Onroerend Erfgoed ID: "
+ }
+ },
+ "tree-species-wikidata": {
+ "question": "Jaký druh je tento strom?"
},
"tree_node-wikidata": {
- "question": "Jaké je ID Wikidata pro tento strom?",
- "render": " Wikidata: {wikidata} "
+ "question": "Jaké je ID Wikidata pro tento strom?"
}
},
"title": {
diff --git a/langs/layers/cy.json b/langs/layers/cy.json
index 54dbfc5059..2f2908031b 100644
--- a/langs/layers/cy.json
+++ b/langs/layers/cy.json
@@ -835,7 +835,7 @@
}
}
},
- "tree_node": {
+ "tree": {
"name": "Coeden",
"tagRenderings": {
"tree-decidouous": {
diff --git a/langs/layers/da.json b/langs/layers/da.json
index d788ed1279..b704c09a8e 100644
--- a/langs/layers/da.json
+++ b/langs/layers/da.json
@@ -3510,7 +3510,7 @@
"render": "Stoppested"
}
},
- "tree_node": {
+ "tree": {
"description": "Et lag, der viser træer",
"name": "Træ",
"presets": {
@@ -3597,10 +3597,7 @@
}
}
},
- "tree-species-wikidata": {
- "question": "Hvilken art er dette træ?"
- },
- "tree_node-name": {
+ "tree-name": {
"mappings": {
"0": {
"then": "Træet har ikke et navn."
@@ -3609,13 +3606,17 @@
"question": "Har træet et navn?",
"render": "Navn: {name}"
},
- "tree_node-ref:OnroerendErfgoed": {
+ "tree-ref:OnroerendErfgoed": {
"question": "Hvad er ID udstedt af Onroerend Erfgoed Flanders?",
- "render": " Onroerend Erfgoed ID: {ref:OnroerendErfgoed} "
+ "render": {
+ "before": "Onroerend Erfgoed ID: "
+ }
+ },
+ "tree-species-wikidata": {
+ "question": "Hvilken art er dette træ?"
},
"tree_node-wikidata": {
- "question": "Hvad er Wikidata-id'et for dette træ?",
- "render": " Wikidata: {wikidata} "
+ "question": "Hvad er Wikidata-id'et for dette træ?"
}
},
"title": {
diff --git a/langs/layers/de.json b/langs/layers/de.json
index 7b8869eebf..27c384de17 100644
--- a/langs/layers/de.json
+++ b/langs/layers/de.json
@@ -12573,7 +12573,7 @@
"render": "Haltestelle"
}
},
- "tree_node": {
+ "tree": {
"description": "Eine Ebene, die Bäume zeigt",
"name": "Bäume",
"presets": {
@@ -12674,10 +12674,7 @@
},
"question": "Ist dies ein Laub- oder Nadelbaum?"
},
- "tree-species-wikidata": {
- "question": "Um welche Baumart handelt es sich?"
- },
- "tree_node-name": {
+ "tree-name": {
"mappings": {
"0": {
"then": "Der Baum hat keinen Namen."
@@ -12686,13 +12683,17 @@
"question": "Hat der Baum einen Namen?",
"render": "Name: {name}"
},
- "tree_node-ref:OnroerendErfgoed": {
+ "tree-ref:OnroerendErfgoed": {
"question": "Wie lautet die Kennung der Onroerend Erfgoed Flanders?",
- "render": " Onroerend Erfgoed Kennung: {ref:OnroerendErfgoed} "
+ "render": {
+ "before": "Onroerend Erfgoed Kennung: "
+ }
+ },
+ "tree-species-wikidata": {
+ "question": "Um welche Baumart handelt es sich?"
},
"tree_node-wikidata": {
- "question": "Was ist das passende Wikidata Element zu diesem Baum?",
- "render": " Wikidata: {wikidata} "
+ "question": "Was ist das passende Wikidata Element zu diesem Baum?"
}
},
"title": {
diff --git a/langs/layers/en.json b/langs/layers/en.json
index d5c81adcfa..c9632acd73 100644
--- a/langs/layers/en.json
+++ b/langs/layers/en.json
@@ -13690,7 +13690,7 @@
"render": "Transit Stop"
}
},
- "tree_node": {
+ "tree": {
"description": "A layer showing trees",
"name": "Tree",
"presets": {
@@ -13797,10 +13797,7 @@
},
"question": "Is this a broadleaved or needleleaved tree?"
},
- "tree-species-wikidata": {
- "question": "What species is this tree?"
- },
- "tree_node-name": {
+ "tree-name": {
"mappings": {
"0": {
"then": "The tree does not have a name."
@@ -13809,13 +13806,17 @@
"question": "Does the tree have a name?",
"render": "Name: {name}"
},
- "tree_node-ref:OnroerendErfgoed": {
+ "tree-ref:OnroerendErfgoed": {
"question": "What is the ID issued by Onroerend Erfgoed Flanders?",
- "render": " Onroerend Erfgoed ID: {ref:OnroerendErfgoed} "
+ "render": {
+ "before": "Onroerend Erfgoed ID: "
+ }
+ },
+ "tree-species-wikidata": {
+ "question": "What species is this tree?"
},
"tree_node-wikidata": {
- "question": "What is the Wikidata ID for this tree?",
- "render": " Wikidata: {wikidata} "
+ "question": "What is the Wikidata ID for this tree?"
}
},
"title": {
diff --git a/langs/layers/eo.json b/langs/layers/eo.json
index 65274ecbda..cbdf2e8d2f 100644
--- a/langs/layers/eo.json
+++ b/langs/layers/eo.json
@@ -211,9 +211,9 @@
}
}
},
- "tree_node": {
+ "tree": {
"tagRenderings": {
- "tree_node-name": {
+ "tree-name": {
"render": "Nomo: {name}"
}
},
diff --git a/langs/layers/es.json b/langs/layers/es.json
index bd188f450d..0baced4d3f 100644
--- a/langs/layers/es.json
+++ b/langs/layers/es.json
@@ -11452,7 +11452,7 @@
"render": "Parada de transporte"
}
},
- "tree_node": {
+ "tree": {
"description": "Una capa que muestra árboles",
"name": "Árbol",
"presets": {
@@ -11553,10 +11553,7 @@
},
"question": "¿Es este un árbol de hoja ancha o acicular?"
},
- "tree-species-wikidata": {
- "question": "¿De qué especie es este árbol?"
- },
- "tree_node-name": {
+ "tree-name": {
"mappings": {
"0": {
"then": "El árbol no tiene nombre."
@@ -11565,13 +11562,17 @@
"question": "¿Tiene el árbol un nombre?",
"render": "Nombre: {name}"
},
- "tree_node-ref:OnroerendErfgoed": {
+ "tree-ref:OnroerendErfgoed": {
"question": "¿Cuál es el ID emitido por Onroerend Erfgoed Flandes?",
- "render": " ID de Onroerend Erfgoed: {ref:OnroerendErfgoed} "
+ "render": {
+ "before": "ID de Onroerend Erfgoed: "
+ }
+ },
+ "tree-species-wikidata": {
+ "question": "¿De qué especie es este árbol?"
},
"tree_node-wikidata": {
- "question": "¿Cuál es el ID de Wikidata para este árbol?",
- "render": " Wikidata: {wikidata} "
+ "question": "¿Cuál es el ID de Wikidata para este árbol?"
}
},
"title": {
diff --git a/langs/layers/fr.json b/langs/layers/fr.json
index 7098d2a0e8..9321344af7 100644
--- a/langs/layers/fr.json
+++ b/langs/layers/fr.json
@@ -6930,7 +6930,7 @@
}
}
},
- "tree_node": {
+ "tree": {
"description": "Une couche montrant les arbres",
"name": "Arbre",
"presets": {
@@ -7031,10 +7031,7 @@
},
"question": "Cet arbre est-il un feuillu ou un résineux ?"
},
- "tree-species-wikidata": {
- "question": "Quelle est l'espèce de cet arbre ?"
- },
- "tree_node-name": {
+ "tree-name": {
"mappings": {
"0": {
"then": "L'arbre n'a pas de nom."
@@ -7043,13 +7040,17 @@
"question": "L'arbre a-t-il un nom ?",
"render": "Nom : {name}"
},
- "tree_node-ref:OnroerendErfgoed": {
+ "tree-ref:OnroerendErfgoed": {
"question": "Quel est son identifiant donné par Onroerend Erfgoed ?",
- "render": " Identifiant Onroerend Erfgoed : {ref:OnroerendErfgoed} "
+ "render": {
+ "before": "Identifiant Onroerend Erfgoed : "
+ }
+ },
+ "tree-species-wikidata": {
+ "question": "Quelle est l'espèce de cet arbre ?"
},
"tree_node-wikidata": {
- "question": "Quel est l'identifiant Wikidata de cet arbre ?",
- "render": " Wikidata : {wikidata} "
+ "question": "Quel est l'identifiant Wikidata de cet arbre ?"
}
},
"title": {
diff --git a/langs/layers/id.json b/langs/layers/id.json
index 465de72295..cc5cd707c6 100644
--- a/langs/layers/id.json
+++ b/langs/layers/id.json
@@ -764,7 +764,7 @@
}
}
},
- "tree_node": {
+ "tree": {
"presets": {
"2": {
"title": "Pohon"
@@ -804,10 +804,7 @@
}
}
},
- "tree-species-wikidata": {
- "question": "Spesies pohon apa ini?"
- },
- "tree_node-name": {
+ "tree-name": {
"mappings": {
"0": {
"then": "Pohon ini tidak memiliki nama."
@@ -815,6 +812,9 @@
},
"question": "Apakah pohon ini memiliki nama?",
"render": "Nama: {name}"
+ },
+ "tree-species-wikidata": {
+ "question": "Spesies pohon apa ini?"
}
}
},
diff --git a/langs/layers/it.json b/langs/layers/it.json
index d01597f118..5a210ede35 100644
--- a/langs/layers/it.json
+++ b/langs/layers/it.json
@@ -13269,7 +13269,7 @@
"render": "Fermata dei mezzi pubblici"
}
},
- "tree_node": {
+ "tree": {
"description": "Un livello che mostra gli alberi",
"name": "Albero",
"presets": {
@@ -13376,10 +13376,7 @@
},
"question": "Questo è un albero latifoglie o aghifoglie?"
},
- "tree-species-wikidata": {
- "question": "Di che specie è questo albero?"
- },
- "tree_node-name": {
+ "tree-name": {
"mappings": {
"0": {
"then": "L'albero non ha un nome."
@@ -13388,13 +13385,17 @@
"question": "L'albero ha un nome?",
"render": "Nome: {name}"
},
- "tree_node-ref:OnroerendErfgoed": {
+ "tree-ref:OnroerendErfgoed": {
"question": "Qual è l'ID rilasciato da Onroerend Erfgoed Fiandre?",
- "render": " ID Onroerend Erfgoed: {ref:OnroerendErfgoed} "
+ "render": {
+ "before": "ID Onroerend Erfgoed: "
+ }
+ },
+ "tree-species-wikidata": {
+ "question": "Di che specie è questo albero?"
},
"tree_node-wikidata": {
- "question": "Qual è l'ID Wikidata per questo albero?",
- "render": " Wikidata: {wikidata} "
+ "question": "Qual è l'ID Wikidata per questo albero?"
}
},
"title": {
diff --git a/langs/layers/nl.json b/langs/layers/nl.json
index cde21b3c44..0ae22478d3 100644
--- a/langs/layers/nl.json
+++ b/langs/layers/nl.json
@@ -10726,7 +10726,7 @@
"render": "OV-halte"
}
},
- "tree_node": {
+ "tree": {
"description": "Een laag die bomen toont",
"name": "Boom",
"presets": {
@@ -10833,10 +10833,7 @@
},
"question": "Is dit een naald- of loofboom?"
},
- "tree-species-wikidata": {
- "question": "Wat is de boomsoort?"
- },
- "tree_node-name": {
+ "tree-name": {
"mappings": {
"0": {
"then": "De boom heeft geen naam."
@@ -10845,13 +10842,17 @@
"question": "Heeft de boom een naam?",
"render": "Naam: {name}"
},
- "tree_node-ref:OnroerendErfgoed": {
+ "tree-ref:OnroerendErfgoed": {
"question": "Wat is het ID uitgegeven door Onroerend Erfgoed Vlaanderen?",
- "render": " Onroerend Erfgoed-ID: {ref:OnroerendErfgoed} "
+ "render": {
+ "before": "Onroerend Erfgoed-ID: "
+ }
+ },
+ "tree-species-wikidata": {
+ "question": "Wat is de boomsoort?"
},
"tree_node-wikidata": {
- "question": "Wat is het Wikidata-ID van deze boom?",
- "render": " Wikidata: {wikidata} "
+ "question": "Wat is het Wikidata-ID van deze boom?"
}
},
"title": {
diff --git a/langs/layers/pl.json b/langs/layers/pl.json
index 6622991a80..decd96ce4d 100644
--- a/langs/layers/pl.json
+++ b/langs/layers/pl.json
@@ -3583,7 +3583,7 @@
"render": "Ścieżka"
}
},
- "tree_node": {
+ "tree": {
"tagRenderings": {
"tree-leaf_type": {
"mappings": {
diff --git a/langs/layers/pt.json b/langs/layers/pt.json
index d3d1dace71..6e2b577c2b 100644
--- a/langs/layers/pt.json
+++ b/langs/layers/pt.json
@@ -1777,7 +1777,7 @@
}
}
},
- "tree_node": {
+ "tree": {
"name": "Árvore",
"presets": {
"0": {
@@ -1872,10 +1872,7 @@
},
"question": "Esta é uma árvore de folhas largas ou acículas?"
},
- "tree-species-wikidata": {
- "question": "Que espécie é esta árvore?"
- },
- "tree_node-name": {
+ "tree-name": {
"mappings": {
"0": {
"then": "A árvore não tem nome."
@@ -1884,9 +1881,12 @@
"question": "A árvore tem nome?",
"render": "Nome: {name}"
},
- "tree_node-ref:OnroerendErfgoed": {
+ "tree-ref:OnroerendErfgoed": {
"question": "Qual é o ID emitido por Onroerend Erfgoed Flanders?"
},
+ "tree-species-wikidata": {
+ "question": "Que espécie é esta árvore?"
+ },
"tree_node-wikidata": {
"question": "Qual é o ID do Wikidata para esta árvore?"
}
diff --git a/langs/layers/pt_BR.json b/langs/layers/pt_BR.json
index 1bb85c2151..2834d9a0c9 100644
--- a/langs/layers/pt_BR.json
+++ b/langs/layers/pt_BR.json
@@ -1611,7 +1611,7 @@
}
}
},
- "tree_node": {
+ "tree": {
"name": "Árvore",
"presets": {
"2": {
diff --git a/langs/layers/ru.json b/langs/layers/ru.json
index b5ed86b3b1..4cda3d5e15 100644
--- a/langs/layers/ru.json
+++ b/langs/layers/ru.json
@@ -1977,7 +1977,7 @@
}
}
},
- "tree_node": {
+ "tree": {
"name": "Дерево",
"presets": {
"0": {
@@ -2004,7 +2004,7 @@
},
"question": "Это дерево вечнозелёное или листопадное?"
},
- "tree_node-name": {
+ "tree-name": {
"mappings": {
"0": {
"then": "У этого дерева нет названия."
@@ -2013,11 +2013,10 @@
"question": "Есть ли у этого дерева название?",
"render": "Название: {name}"
},
- "tree_node-ref:OnroerendErfgoed": {
- "render": " Onroerend Erfgoed ID: {ref:OnroerendErfgoed} "
- },
- "tree_node-wikidata": {
- "render": " Wikidata: {wikidata} "
+ "tree-ref:OnroerendErfgoed": {
+ "render": {
+ "before": "Onroerend Erfgoed ID:"
+ }
}
},
"title": {
diff --git a/langs/layers/uk.json b/langs/layers/uk.json
index da167fd201..3df620e97c 100644
--- a/langs/layers/uk.json
+++ b/langs/layers/uk.json
@@ -2998,7 +2998,7 @@
}
}
},
- "tree_node": {
+ "tree": {
"tagRenderings": {
"tree-species-wikidata": {
"question": "Якого виду це дерево?"
diff --git a/public/css/index-tailwind-output.css b/public/css/index-tailwind-output.css
index 56c5498b44..6d641cbe3d 100644
--- a/public/css/index-tailwind-output.css
+++ b/public/css/index-tailwind-output.css
@@ -2209,6 +2209,10 @@ input[type="range"].range-lg::-moz-range-thumb {
min-width: 8rem;
}
+.min-w-48 {
+ min-width: 12rem;
+}
+
.min-w-6 {
min-width: 1.5rem;
}
diff --git a/src/Models/ThemeConfig/LayerConfig.ts b/src/Models/ThemeConfig/LayerConfig.ts
index 6ca6f23eb4..c4a1e4d581 100644
--- a/src/Models/ThemeConfig/LayerConfig.ts
+++ b/src/Models/ThemeConfig/LayerConfig.ts
@@ -22,6 +22,7 @@ import { QuestionableTagRenderingConfigJson } from "./Json/QuestionableTagRender
import MarkdownUtils from "../../Utils/MarkdownUtils"
import { And } from "../../Logic/Tags/And"
import OsmWiki from "../../Logic/Osm/OsmWiki"
+import { UnitUtils } from "../UnitUtils"
export default class LayerConfig extends WithContextLoader {
public static readonly syncSelectionAllowed = ["no", "local", "theme-only", "global"] as const
@@ -312,7 +313,7 @@ export default class LayerConfig extends WithContextLoader {
)
}
this.units = (json.units ?? []).flatMap((unitJson, i) =>
- Unit.fromJson(unitJson, this.tagRenderings, `${context}.unit[${i}]`)
+ UnitUtils.fromJson(unitJson, this.tagRenderings, `${context}.unit[${i}]`)
)
{
let filter = json.filter
diff --git a/src/Models/Unit.ts b/src/Models/Unit.ts
index 311e661336..75c8a7adbf 100644
--- a/src/Models/Unit.ts
+++ b/src/Models/Unit.ts
@@ -1,14 +1,9 @@
import BaseUIElement from "../UI/BaseUIElement"
import { Denomination } from "./Denomination"
-import UnitConfigJson from "./ThemeConfig/Json/UnitConfigJson"
-import unit from "../../assets/layers/unit/unit.json"
-import TagRenderingConfig from "./ThemeConfig/TagRenderingConfig"
-import Validators, { ValidatorType } from "../UI/InputElement/Validators"
import { Validator } from "../UI/InputElement/Validator"
import FloatValidator from "../UI/InputElement/Validators/FloatValidator"
export class Unit {
- private static allUnits = this.initUnits()
public readonly appliesToKeys: Set
public readonly denominations: Denomination[]
public readonly denominationsSorted: Denomination[]
@@ -85,226 +80,7 @@ export class Unit {
}
}
- static fromJson(
- json:
- | UnitConfigJson
- | Record<
- string,
- string | { quantity: string; denominations: string[]; inverted?: boolean }
- >,
- tagRenderings: TagRenderingConfig[],
- ctx: string
- ): Unit[] {
- const types: Record = {}
- for (const tagRendering of tagRenderings) {
- if (tagRendering.freeform?.type) {
- types[tagRendering.freeform.key] = tagRendering.freeform.type
- }
- }
- if (!json.appliesToKey && !json.quantity) {
- return this.loadFromLibrary(json, types, ctx)
- }
- return this.parse(json, types, ctx)
- }
-
- private static parseDenomination(
- json: UnitConfigJson,
- validator: Validator,
- appliesToKey: string,
- ctx: string
- ): Unit {
- const applicable = json.applicableUnits.map((u, i) =>
- Denomination.fromJson(u, validator, `${ctx}.units[${i}]`)
- )
-
- if (
- json.defaultInput &&
- !applicable.some((denom) => denom.canonical.trim() === json.defaultInput)
- ) {
- throw `${ctx}: no denomination has the specified default denomination. The default denomination is '${
- json.defaultInput
- }', but the available denominations are ${applicable
- .map((denom) => denom.canonical)
- .join(", ")}`
- }
-
- return new Unit(
- json.quantity ?? "",
- appliesToKey === undefined ? undefined : [appliesToKey],
- applicable,
- json.eraseInvalidValues ?? false,
- validator
- )
- }
-
- /**
- *
- * // Should detect invalid defaultInput
- * let threwError = false
- * try{
- * Unit.parse({
- * appliesToKey: ["length"],
- * defaultInput: "xcm",
- * applicableUnits: [
- * {
- * canonicalDenomination: "m",
- * useIfNoUnitGiven: true,
- * human: "meter"
- * }
- * ]
- * },"test")
- * }catch(e){
- * threwError = true
- * }
- * threwError // => true
- *
- * // Should work
- * Unit.parse({
- * appliesToKey: ["length"],
- * defaultInput: "xcm",
- * applicableUnits: [
- * {
- * canonicalDenomination: "m",
- * useIfNoUnitGiven: true,
- * humen: "meter"
- * },
- * {
- * canonicalDenomination: "cm",
- * human: "centimeter"
- * }
- * ]
- * }, "test")
- */
- private static parse(
- json: UnitConfigJson,
- types: Record,
- ctx: string
- ): Unit[] {
- const appliesTo = json.appliesToKey
- for (let i = 0; i < (appliesTo ?? []).length; i++) {
- const key = appliesTo[i]
- if (key.trim() !== key) {
- throw `${ctx}.appliesToKey[${i}] is invalid: it starts or ends with whitespace`
- }
- }
-
- if ((json.applicableUnits ?? []).length === 0) {
- throw `${ctx}: define at least one applicable unit`
- }
- // Some keys do have unit handling
-
- const units: Unit[] = []
- if (appliesTo === undefined) {
- units.push(this.parseDenomination(json, Validators.get("float"), undefined, ctx))
- }
- for (const key of appliesTo ?? []) {
- const validator = Validators.get(types[key] ?? "float")
- units.push(this.parseDenomination(json, validator, undefined, ctx))
- }
- return units
- }
-
- private static initUnits(): Map {
- const m = new Map()
- const units = (unit.units).flatMap((json, i) =>
- this.parse(json, {}, "unit.json.units." + i)
- )
-
- for (const unit of units) {
- m.set(unit.quantity, unit)
- }
- return m
- }
-
- private static getFromLibrary(name: string, ctx: string): Unit {
- const loaded = this.allUnits.get(name)
- if (loaded === undefined) {
- throw (
- "No unit with quantity name " +
- name +
- " found (at " +
- ctx +
- "). Try one of: " +
- Array.from(this.allUnits.keys()).join(", ")
- )
- }
- return loaded
- }
-
- private static loadFromLibrary(
- spec: Record<
- string,
- | string
- | { quantity: string; denominations: string[]; canonical?: string; inverted?: boolean }
- >,
- types: Record,
- ctx: string
- ): Unit[] {
- const units: Unit[] = []
- for (const key in spec) {
- const toLoad = spec[key]
- const validator = Validators.get(types[key] ?? "float")
- if (typeof toLoad === "string") {
- const loaded = this.getFromLibrary(toLoad, ctx)
- units.push(
- new Unit(
- loaded.quantity,
- [key],
- loaded.denominations,
- loaded.eraseInvalid,
- validator,
- toLoad["inverted"]
- )
- )
- continue
- }
-
- const loaded = this.getFromLibrary(toLoad.quantity, ctx)
- const quantity = toLoad.quantity
-
- const fetchDenom = (d: string): Denomination => {
- const found = loaded.denominations.find(
- (denom) => denom.canonical.toLowerCase() === d
- )
- if (!found) {
- throw (
- `Could not find a denomination \`${d}\`for quantity ${quantity} at ${ctx}. Perhaps you meant to use on of ` +
- loaded.denominations.map((d) => d.canonical).join(", ")
- )
- }
- return found
- }
-
- if (!Array.isArray(toLoad.denominations)) {
- throw (
- "toLoad is not an array. Did you forget the [ and ] around the denominations at " +
- ctx +
- "?"
- )
- }
- const denoms = toLoad.denominations
- .map((d) => d.toLowerCase())
- .map((d) => fetchDenom(d))
- .map((d) => d.withValidator(validator))
-
- if (toLoad.canonical) {
- const canonical = fetchDenom(toLoad.canonical).withValidator(validator)
- denoms.unshift(canonical.withBlankCanonical())
- }
- units.push(
- new Unit(
- loaded.quantity,
- [key],
- denoms,
- loaded.eraseInvalid,
- validator,
- toLoad["inverted"]
- )
- )
- }
- return units
- }
isApplicableToKey(key: string | undefined): boolean {
if (key === undefined) {
diff --git a/src/Models/UnitUtils.ts b/src/Models/UnitUtils.ts
new file mode 100644
index 0000000000..31c1f90d27
--- /dev/null
+++ b/src/Models/UnitUtils.ts
@@ -0,0 +1,232 @@
+import UnitConfigJson from "./ThemeConfig/Json/UnitConfigJson"
+import TagRenderingConfig from "./ThemeConfig/TagRenderingConfig"
+import Validators, { ValidatorType } from "../UI/InputElement/Validators"
+import { Validator } from "../UI/InputElement/Validator"
+import { Denomination } from "./Denomination"
+import unit from "../../assets/layers/unit/unit.json"
+import { Unit } from "./Unit"
+
+export class UnitUtils {
+ private static allUnits = this.initUnits()
+
+ static fromJson(
+ json:
+ | UnitConfigJson
+ | Record<
+ string,
+ string | { quantity: string; denominations: string[]; inverted?: boolean }
+ >,
+ tagRenderings: TagRenderingConfig[],
+ ctx: string,
+ ): Unit[] {
+ const types: Record = {}
+ for (const tagRendering of tagRenderings) {
+ if (tagRendering.freeform?.type) {
+ types[tagRendering.freeform.key] = tagRendering.freeform.type
+ }
+ }
+
+ if (!json.appliesToKey && !json.quantity) {
+ return this.loadFromLibrary(json, types, ctx)
+ }
+ return this.parse(json, types, ctx)
+ }
+
+ private static parseDenomination(
+ json: UnitConfigJson,
+ validator: Validator,
+ appliesToKey: string,
+ ctx: string,
+ ): Unit {
+ const applicable = json.applicableUnits.map((u, i) =>
+ Denomination.fromJson(u, validator, `${ctx}.units[${i}]`),
+ )
+
+ if (
+ json.defaultInput &&
+ !applicable.some((denom) => denom.canonical.trim() === json.defaultInput)
+ ) {
+ throw `${ctx}: no denomination has the specified default denomination. The default denomination is '${
+ json.defaultInput
+ }', but the available denominations are ${applicable
+ .map((denom) => denom.canonical)
+ .join(", ")}`
+ }
+
+ return new Unit(
+ json.quantity ?? "",
+ appliesToKey === undefined ? undefined : [appliesToKey],
+ applicable,
+ json.eraseInvalidValues ?? false,
+ validator,
+ )
+ }
+
+ /**
+ *
+ * // Should detect invalid defaultInput
+ * let threwError = false
+ * try{
+ * Unit.parse({
+ * appliesToKey: ["length"],
+ * defaultInput: "xcm",
+ * applicableUnits: [
+ * {
+ * canonicalDenomination: "m",
+ * useIfNoUnitGiven: true,
+ * human: "meter"
+ * }
+ * ]
+ * },"test")
+ * }catch(e){
+ * threwError = true
+ * }
+ * threwError // => true
+ *
+ * // Should work
+ * Unit.parse({
+ * appliesToKey: ["length"],
+ * defaultInput: "xcm",
+ * applicableUnits: [
+ * {
+ * canonicalDenomination: "m",
+ * useIfNoUnitGiven: true,
+ * humen: "meter"
+ * },
+ * {
+ * canonicalDenomination: "cm",
+ * human: "centimeter"
+ * }
+ * ]
+ * }, "test")
+ */
+ private static parse(
+ json: UnitConfigJson,
+ types: Record,
+ ctx: string,
+ ): Unit[] {
+ const appliesTo = json.appliesToKey
+ for (let i = 0; i < (appliesTo ?? []).length; i++) {
+ const key = appliesTo[i]
+ if (key.trim() !== key) {
+ throw `${ctx}.appliesToKey[${i}] is invalid: it starts or ends with whitespace`
+ }
+ }
+
+ if ((json.applicableUnits ?? []).length === 0) {
+ throw `${ctx}: define at least one applicable unit`
+ }
+ // Some keys do have unit handling
+
+ const units: Unit[] = []
+ if (appliesTo === undefined) {
+ units.push(this.parseDenomination(json, Validators.get("float"), undefined, ctx))
+ }
+ for (const key of appliesTo ?? []) {
+ const validator = Validators.get(types[key] ?? "float")
+ units.push(this.parseDenomination(json, validator, undefined, ctx))
+ }
+ return units
+ }
+
+ private static initUnits(): Map {
+ const m = new Map()
+ const units = (unit.units).flatMap((json, i) =>
+ this.parse(json, {}, "unit.json.units." + i),
+ )
+
+ for (const unit of units) {
+ m.set(unit.quantity, unit)
+ }
+ return m
+ }
+
+ private static getFromLibrary(name: string, ctx: string): Unit {
+ const loaded = this.allUnits.get(name)
+ if (loaded === undefined) {
+ throw (
+ "No unit with quantity name " +
+ name +
+ " found (at " +
+ ctx +
+ "). Try one of: " +
+ Array.from(this.allUnits.keys()).join(", ")
+ )
+ }
+ return loaded
+ }
+
+ private static loadFromLibrary(
+ spec: Record<
+ string,
+ | string
+ | { quantity: string; denominations: string[]; canonical?: string; inverted?: boolean }
+ >,
+ types: Record,
+ ctx: string,
+ ): Unit[] {
+ const units: Unit[] = []
+ for (const key in spec) {
+ const toLoad = spec[key]
+ const validator = Validators.get(types[key] ?? "float")
+ if (typeof toLoad === "string") {
+ const loaded = this.getFromLibrary(toLoad, ctx)
+ units.push(
+ new Unit(
+ loaded.quantity,
+ [key],
+ loaded.denominations,
+ loaded.eraseInvalid,
+ validator,
+ toLoad["inverted"],
+ ),
+ )
+ continue
+ }
+
+ const loaded = this.getFromLibrary(toLoad.quantity, ctx)
+ const quantity = toLoad.quantity
+
+ const fetchDenom = (d: string): Denomination => {
+ const found = loaded.denominations.find(
+ (denom) => denom.canonical.toLowerCase() === d,
+ )
+ if (!found) {
+ throw (
+ `Could not find a denomination \`${d}\`for quantity ${quantity} at ${ctx}. Perhaps you meant to use on of ` +
+ loaded.denominations.map((d) => d.canonical).join(", ")
+ )
+ }
+ return found
+ }
+
+ if (!Array.isArray(toLoad.denominations)) {
+ throw (
+ "toLoad is not an array. Did you forget the [ and ] around the denominations at " +
+ ctx +
+ "?"
+ )
+ }
+ const denoms = toLoad.denominations
+ .map((d) => d.toLowerCase())
+ .map((d) => fetchDenom(d))
+ .map((d) => d.withValidator(validator))
+
+ if (toLoad.canonical) {
+ const canonical = fetchDenom(toLoad.canonical).withValidator(validator)
+ denoms.unshift(canonical.withBlankCanonical())
+ }
+ units.push(
+ new Unit(
+ loaded.quantity,
+ [key],
+ denoms,
+ loaded.eraseInvalid,
+ validator,
+ toLoad["inverted"],
+ ),
+ )
+ }
+ return units
+ }
+}
diff --git a/src/UI/InputElement/Helpers/DirectionInput.svelte b/src/UI/InputElement/Helpers/DirectionInput.svelte
index 860fc1ba35..691f27b6cb 100644
--- a/src/UI/InputElement/Helpers/DirectionInput.svelte
+++ b/src/UI/InputElement/Helpers/DirectionInput.svelte
@@ -6,25 +6,35 @@
import MaplibreMap from "../../Map/MaplibreMap.svelte"
import Direction_stroke from "../../../assets/svg/Direction_stroke.svelte"
import type { SpecialVisualizationState } from "../../SpecialVisualization"
-
+ import type Feature from "geojson"
+ import { GeoOperations } from "../../../Logic/GeoOperations"
/**
* A visualisation to pick a direction on a map background.
*/
export let value: UIEventSource
export let state: SpecialVisualizationState = undefined
-
- export let mapProperties: Partial & {
- readonly location: UIEventSource<{ lon: number; lat: number }>
+ export let args: any[] = []
+ export let feature: Feature
+ let [lon, lat] = GeoOperations.centerpointCoordinates(feature)
+ let mapProperties: MapProperties = {
+ location: new UIEventSource({ lon, lat }),
+ zoom: new UIEventSource(args[0] !== undefined ? Number(args[0]) : 17),
+ rasterLayer: state.mapProperties.rasterLayer,
+ rotation: state.mapProperties.rotation,
}
let map: UIEventSource = new UIEventSource(undefined)
let mla = new MapLibreAdaptor(map, mapProperties)
mla.allowMoving.setData(false)
mla.allowZooming.setData(false)
- state?.mapProperties?.rasterLayer?.addCallbackAndRunD((l) => mla.rasterLayer.set(l))
-
+ let rotation = new UIEventSource(value.data)
+ rotation.addCallbackD(rotation => {
+ const r = (rotation + mapProperties.rotation.data + 360) % 360
+ console.log("Setting value to", r)
+ value.setData(""+Math.floor(r))
+ }, [mapProperties.rotation])
let directionElem: HTMLElement | undefined
- $: value.addCallbackAndRunD((degrees) => {
- if (directionElem === undefined) {
+ $: rotation.addCallbackAndRunD((degrees) => {
+ if (!directionElem?.style) {
return
}
directionElem.style.rotate = degrees + "deg"
@@ -32,13 +42,14 @@
let mainElem: HTMLElement
+
function onPosChange(x: number, y: number) {
const rect = mainElem.getBoundingClientRect()
const dx = -(rect.left + rect.right) / 2 + x
const dy = (rect.top + rect.bottom) / 2 - y
const angle = (180 * Math.atan2(dy, dx)) / Math.PI
const angleGeo = Math.floor((450 - angle) % 360)
- value.setData("" + angleGeo)
+ rotation.setData(angleGeo)
}
let isDown = false
@@ -46,7 +57,7 @@
onPosChange(e.x, e.y)}
on:mousedown={(e) => {
isDown = true
@@ -71,7 +82,7 @@
-
diff --git a/src/UI/InputElement/InputHelper.svelte b/src/UI/InputElement/InputHelper.svelte
index 200e2afc26..ed5cbb64dc 100644
--- a/src/UI/InputElement/InputHelper.svelte
+++ b/src/UI/InputElement/InputHelper.svelte
@@ -1,67 +1,25 @@
-{#if type === "translation"}
-
-{:else if type === "direction"}
-
-{:else if type === "date"}
-
-{:else if type === "time"}
-
-{:else if type === "points_in_time"}
-
-{:else if type === "color"}
-
-{:else if type === "image"}
-
-{:else if type === "tag"}
-
-{:else if type === "simple_tag"}
-
-{:else if type === "opening_hours"}
-
-{:else if type === "slope"}
-
-{:else if type === "wikidata"}
-
-{:else if type === "distance"}
-
-
-{:else}
-
+{#if type === "distance"}
+
+{:else if validatorHelper !== undefined}
+
{/if}
diff --git a/src/UI/InputElement/InputHelpers.ts b/src/UI/InputElement/InputHelpers.ts
deleted file mode 100644
index bae5aaec9e..0000000000
--- a/src/UI/InputElement/InputHelpers.ts
+++ /dev/null
@@ -1,67 +0,0 @@
-import { UIEventSource } from "../../Logic/UIEventSource"
-
-import { MapProperties } from "../../Models/MapProperties"
-import { Feature } from "geojson"
-import { GeoOperations } from "../../Logic/GeoOperations"
-import { ValidatorType } from "./Validators"
-
-export interface InputHelperProperties {
- /**
- * Extra arguments which might be used by the helper component
- */
- args?: (string | number | boolean)[]
-
- /**
- * Used for map-based helpers, such as 'direction'
- */
- mapProperties?: Partial & {
- readonly location: UIEventSource<{ lon: number; lat: number }>
- }
- /**
- * The feature that this question is about
- * Used by the wikidata-input to read properties, which in turn is used to read the name to pre-populate the text field.
- * Additionally, used for direction input to set the default location if no mapProperties with location are given
- */
- feature?: Feature
-}
-
-export default class InputHelpers {
- public static hideInputField: ValidatorType[] = ["translation", "simple_tag", "tag","time"]
-
- /**
- * Constructs a mapProperties-object for the given properties.
- * Assumes that the first helper-args contains the desired zoom-level
- * Used for the 'direction' input helper
- * @param properties
- * @private
- */
- public static constructMapProperties(
- properties: InputHelperProperties
- ): Partial {
- let location = properties?.mapProperties?.location
- if (!location) {
- const [lon, lat] = GeoOperations.centerpointCoordinates(properties.feature)
- location = new UIEventSource<{ lon: number; lat: number }>({ lon, lat })
- }
- let mapProperties: Partial = properties?.mapProperties ?? { location }
- if (!mapProperties.location) {
- mapProperties = { ...mapProperties, location }
- }
- let zoom = 17
- if (properties?.args?.[0] !== undefined) {
- zoom = Number(properties.args[0])
- if (isNaN(zoom)) {
- throw "Invalid zoom level for argument at 'length'-input"
- }
- }
- if (!mapProperties.zoom) {
- mapProperties = { ...mapProperties, zoom: new UIEventSource(zoom) }
- }
- if (!mapProperties.rasterLayer) {
- /* mapProperties = {
- ...mapProperties, rasterLayer: properties?.mapProperties?.rasterLayer
- }*/
- }
- return mapProperties
- }
-}
diff --git a/src/UI/InputElement/Validator.ts b/src/UI/InputElement/Validator.ts
index b6cc2f4d42..96703f838e 100644
--- a/src/UI/InputElement/Validator.ts
+++ b/src/UI/InputElement/Validator.ts
@@ -1,6 +1,7 @@
import { Translation } from "../i18n/Translation"
import Translations from "../i18n/Translations"
import { HTMLInputTypeAttribute } from "svelte/elements"
+import { ComponentType } from "svelte/types/runtime/internal/dev"
/**
* A 'TextFieldValidator' contains various methods to check and cleanup an entered value or to give feedback.
@@ -21,6 +22,8 @@ export abstract class Validator {
public readonly textArea: boolean
public readonly isMeta?: boolean
+ public readonly inputHelper : ComponentType = undefined
+ public readonly hideInputField: boolean = false
constructor(
name: string,
@@ -80,4 +83,5 @@ export abstract class Validator {
public validateArguments(args: string): undefined | string {
return undefined
}
+
}
diff --git a/src/UI/InputElement/Validators.ts b/src/UI/InputElement/Validators.ts
index 6c830c2135..50fa1e52c3 100644
--- a/src/UI/InputElement/Validators.ts
+++ b/src/UI/InputElement/Validators.ts
@@ -1,105 +1,114 @@
import { Validator } from "./Validator"
+import FloatValidator from "./Validators/FloatValidator"
import StringValidator from "./Validators/StringValidator"
+import PFloatValidator from "./Validators/PFloatValidator"
import TextValidator from "./Validators/TextValidator"
import DateValidator from "./Validators/DateValidator"
+import { TimeValidator } from "./Validators/TimeValidator"
import NatValidator from "./Validators/NatValidator"
import IntValidator from "./Validators/IntValidator"
-import DistanceValidator from "./Validators/DistanceValidator"
+import PNatValidator from "./Validators/PNatValidator"
import DirectionValidator from "./Validators/DirectionValidator"
import WikidataValidator from "./Validators/WikidataValidator"
-import PNatValidator from "./Validators/PNatValidator"
-import FloatValidator from "./Validators/FloatValidator"
-import PFloatValidator from "./Validators/PFloatValidator"
import EmailValidator from "./Validators/EmailValidator"
import UrlValidator from "./Validators/UrlValidator"
-import PhoneValidator from "./Validators/PhoneValidator"
import OpeningHoursValidator from "./Validators/OpeningHoursValidator"
+import PhoneValidator from "./Validators/PhoneValidator"
import ColorValidator from "./Validators/ColorValidator"
-import SimpleTagValidator from "./Validators/SimpleTagValidator"
import ImageUrlValidator from "./Validators/ImageUrlValidator"
import TagKeyValidator from "./Validators/TagKeyValidator"
-import TranslationValidator from "./Validators/TranslationValidator"
-import FediverseValidator from "./Validators/FediverseValidator"
import IconValidator from "./Validators/IconValidator"
-import TagValidator from "./Validators/TagValidator"
-import IdValidator from "./Validators/IdValidator"
import SlopeValidator from "./Validators/SlopeValidator"
+import CollectionTimesValidator from "./Validators/CollectionTimesValidator"
+import IdValidator from "./Validators/IdValidator"
+import FediverseValidator from "./Validators/FediverseValidator"
+import SimpleTagValidator from "./Validators/SimpleTagValidator"
import VeloparkValidator from "./Validators/VeloparkValidator"
import NameSuggestionIndexValidator from "./Validators/NameSuggestionIndexValidator"
-import CurrencyValidator from "./Validators/CurrencyValidator"
import RegexValidator from "./Validators/RegexValidator"
-import { TimeValidator } from "./Validators/TimeValidator"
-import CollectionTimesValidator from "./Validators/CollectionTimesValidator"
+import CurrencyValidator from "./Validators/CurrencyValidator"
+import TagValidator from "./Validators/TagValidator"
+import TranslationValidator from "./Validators/TranslationValidator"
+import DistanceValidator from "./Validators/DistanceValidator"
-export type ValidatorType = (typeof Validators.availableTypes)[number]
+export const availableValidators = [
+ "color",
+ "currency",
+ "date",
+ "time",
+ "direction",
+ "distance",
+ "email",
+ "fediverse",
+ "float",
+ "icon",
+ "id",
+ "image",
+ "int",
+ "key",
+ "nat",
+ "nsi",
+ "opening_hours",
+ "pfloat",
+ "phone",
+ "pnat",
+ "points_in_time",
+ "regex",
+ "simple_tag",
+ "slope",
+ "string",
+ "tag",
+ "text",
+ "translation",
+ "url",
+ "velopark",
+ "wikidata",
+] as const
+export type ValidatorType = (typeof availableValidators)[number]
export default class Validators {
- public static readonly availableTypes = [
- "color",
- "currency",
- "date",
- "time",
- "direction",
- "distance",
- "email",
- "fediverse",
- "float",
- "icon",
- "id",
- "image",
- "int",
- "key",
- "nat",
- "nsi",
- "opening_hours",
- "pfloat",
- "phone",
- "pnat",
- "points_in_time",
- "regex",
- "simple_tag",
- "slope",
- "string",
- "tag",
- "text",
- "translation",
- "url",
- "velopark",
- "wikidata",
- ] as const
-
+ public static readonly availableTypes = availableValidators
public static readonly AllValidators: ReadonlyArray = [
new StringValidator(),
- new TextValidator(),
- new DateValidator(),
- new TimeValidator(),
- new NatValidator(),
- new IntValidator(),
- new DistanceValidator(),
- new DirectionValidator(),
- new WikidataValidator(),
- new PNatValidator(),
new FloatValidator(),
new PFloatValidator(),
- new EmailValidator(),
- new UrlValidator(),
- new PhoneValidator(),
- new OpeningHoursValidator(),
+ new TextValidator(),
+ new NatValidator(),
+ new PNatValidator(),
+ new IntValidator(),
+ new DateValidator(),
+ new TimeValidator(),
new ColorValidator(),
- new ImageUrlValidator(),
- new SimpleTagValidator(),
- new TagValidator(),
- new TagKeyValidator(),
- new TranslationValidator(),
- new IconValidator(),
- new FediverseValidator(),
- new IdValidator(),
+
+ new DirectionValidator(),
new SlopeValidator(),
- new VeloparkValidator(),
- new NameSuggestionIndexValidator(),
+
+
+ new UrlValidator(),
+ new EmailValidator(),
+ new PhoneValidator(),
+ new FediverseValidator(),
+ new ImageUrlValidator(),
+
+ new OpeningHoursValidator(),
+ new CollectionTimesValidator(),
new CurrencyValidator(),
+
+ new WikidataValidator(),
+
+ new TagKeyValidator(),
+ new IconValidator(),
+ new VeloparkValidator(),
+
+ new IdValidator(),
new RegexValidator(),
- new CollectionTimesValidator()
+ new SimpleTagValidator(),
+ new TranslationValidator(),
+ new TagValidator(),
+
+ new NameSuggestionIndexValidator(),
+
+ new DistanceValidator(),
]
private static _byType = Validators._byTypeConstructor()
diff --git a/src/UI/InputElement/Validators/CollectionTimesValidator.ts b/src/UI/InputElement/Validators/CollectionTimesValidator.ts
index 5847657c83..3fc5ce13da 100644
--- a/src/UI/InputElement/Validators/CollectionTimesValidator.ts
+++ b/src/UI/InputElement/Validators/CollectionTimesValidator.ts
@@ -1,7 +1,12 @@
import StringValidator from "./StringValidator"
+import { ComponentType } from "svelte/types/runtime/internal/dev"
+import CollectionTimes from "../Helpers/CollectionTimes/CollectionTimes.svelte"
export default class CollectionTimesValidator extends StringValidator{
+ public readonly inputHelper: ComponentType = CollectionTimes
constructor() {
super("points_in_time", "'Points in time' are points according to a fixed schedule, e.g. 'every monday at 10:00'. They are typically used for postbox collection times or times of mass at a place of worship")
}
+
+
}
diff --git a/src/UI/InputElement/Validators/ColorValidator.ts b/src/UI/InputElement/Validators/ColorValidator.ts
index 88dd5a893c..df984e53e2 100644
--- a/src/UI/InputElement/Validators/ColorValidator.ts
+++ b/src/UI/InputElement/Validators/ColorValidator.ts
@@ -1,7 +1,11 @@
import { Validator } from "../Validator"
+import ColorInput from "../Helpers/ColorInput.svelte"
export default class ColorValidator extends Validator {
+ inputHelper = ColorInput
+
constructor() {
super("color", "Shows a color picker")
}
+
}
diff --git a/src/UI/InputElement/Validators/DateValidator.ts b/src/UI/InputElement/Validators/DateValidator.ts
index 340b8159a8..5cc02c7c79 100644
--- a/src/UI/InputElement/Validators/DateValidator.ts
+++ b/src/UI/InputElement/Validators/DateValidator.ts
@@ -1,6 +1,10 @@
import { Validator } from "../Validator"
+import DateInput from "../Helpers/DateInput.svelte"
export default class DateValidator extends Validator {
+ public readonly inputHelper = DateInput
+ public readonly hideInputField = true
+
constructor() {
super("date", "A date with date picker")
}
@@ -25,4 +29,5 @@ export default class DateValidator extends Validator {
return [year, month, day].join("-")
}
+
}
diff --git a/src/UI/InputElement/Validators/DirectionValidator.ts b/src/UI/InputElement/Validators/DirectionValidator.ts
index 4cf14d9ad4..24719d4913 100644
--- a/src/UI/InputElement/Validators/DirectionValidator.ts
+++ b/src/UI/InputElement/Validators/DirectionValidator.ts
@@ -1,6 +1,9 @@
import IntValidator from "./IntValidator"
+import DirectionInput from "../Helpers/DirectionInput.svelte"
export default class DirectionValidator extends IntValidator {
+
+ public readonly inputHelper = DirectionInput
constructor() {
super(
"direction",
diff --git a/src/UI/InputElement/Validators/DistanceValidator.ts b/src/UI/InputElement/Validators/DistanceValidator.ts
index b511301591..ddca39f092 100644
--- a/src/UI/InputElement/Validators/DistanceValidator.ts
+++ b/src/UI/InputElement/Validators/DistanceValidator.ts
@@ -3,6 +3,7 @@ import { Utils } from "../../../Utils"
import { eliCategory } from "../../../Models/RasterLayerProperties"
export default class DistanceValidator extends Validator {
+
private readonly docs: string = [
"#### Helper-arguments",
"Options are:",
@@ -58,4 +59,5 @@ export default class DistanceValidator extends Validator {
}
return undefined
}
+
}
diff --git a/src/UI/InputElement/Validators/ImageUrlValidator.ts b/src/UI/InputElement/Validators/ImageUrlValidator.ts
index 5d9e115070..2d27f1d740 100644
--- a/src/UI/InputElement/Validators/ImageUrlValidator.ts
+++ b/src/UI/InputElement/Validators/ImageUrlValidator.ts
@@ -1,9 +1,11 @@
import UrlValidator from "./UrlValidator"
import { Translation } from "../../i18n/Translation"
+import ImageHelper from "../Helpers/ImageHelper.svelte"
export default class ImageUrlValidator extends UrlValidator {
private static readonly allowedExtensions = ["jpg", "jpeg", "svg", "png"]
public readonly isMeta = true
+ public readonly inputHelper = ImageHelper
constructor() {
super(
@@ -37,4 +39,6 @@ export default class ImageUrlValidator extends UrlValidator {
}
return ImageUrlValidator.hasValidExternsion(str)
}
+
}
+
diff --git a/src/UI/InputElement/Validators/OpeningHoursValidator.ts b/src/UI/InputElement/Validators/OpeningHoursValidator.ts
index 66990aaeb5..a296f6cf00 100644
--- a/src/UI/InputElement/Validators/OpeningHoursValidator.ts
+++ b/src/UI/InputElement/Validators/OpeningHoursValidator.ts
@@ -1,7 +1,12 @@
import { Validator } from "../Validator"
import MarkdownUtils from "../../../Utils/MarkdownUtils"
+import { ComponentType } from "svelte/types/runtime/internal/dev"
+import OpeningHoursInput from "../Helpers/OpeningHoursInput.svelte"
export default class OpeningHoursValidator extends Validator {
+
+ public readonly inputHelper= OpeningHoursInput
+
constructor() {
super(
"opening_hours",
@@ -39,4 +44,6 @@ export default class OpeningHoursValidator extends Validator {
].join("\n")
)
}
+
+
}
diff --git a/src/UI/InputElement/Validators/SimpleTagValidator.ts b/src/UI/InputElement/Validators/SimpleTagValidator.ts
index f39ca9d1d3..732874f115 100644
--- a/src/UI/InputElement/Validators/SimpleTagValidator.ts
+++ b/src/UI/InputElement/Validators/SimpleTagValidator.ts
@@ -2,12 +2,15 @@ import { Validator } from "../Validator"
import { Translation } from "../../i18n/Translation"
import Translations from "../../i18n/Translations"
import TagKeyValidator from "./TagKeyValidator"
+import SimpleTagInput from "../Helpers/SimpleTagInput.svelte"
/**
* Checks that the input conforms `key=value`, where `key` and `value` don't have too much weird characters
*/
export default class SimpleTagValidator extends Validator {
private static readonly KeyValidator = new TagKeyValidator()
+ public readonly inputHelper = SimpleTagInput
+ public readonly hideInputField = true
public readonly isMeta = true
constructor() {
@@ -50,4 +53,6 @@ export default class SimpleTagValidator extends Validator {
isValid(tag: string, _): boolean {
return this.getFeedback(tag, _) === undefined
}
+
}
+
diff --git a/src/UI/InputElement/Validators/SlopeValidator.ts b/src/UI/InputElement/Validators/SlopeValidator.ts
index 8409fa49bc..79e0330902 100644
--- a/src/UI/InputElement/Validators/SlopeValidator.ts
+++ b/src/UI/InputElement/Validators/SlopeValidator.ts
@@ -1,6 +1,9 @@
import FloatValidator from "./FloatValidator"
+import SlopeInput from "../Helpers/SlopeInput.svelte"
export default class SlopeValidator extends FloatValidator {
+ public readonly inputHelper =SlopeInput
+
constructor() {
super(
"slope",
@@ -40,4 +43,5 @@ export default class SlopeValidator extends FloatValidator {
}
return super.reformat(str) + lastChar
}
+
}
diff --git a/src/UI/InputElement/Validators/TagValidator.ts b/src/UI/InputElement/Validators/TagValidator.ts
index 8c69dd71d8..7900f348f2 100644
--- a/src/UI/InputElement/Validators/TagValidator.ts
+++ b/src/UI/InputElement/Validators/TagValidator.ts
@@ -1,11 +1,14 @@
import { Validator } from "../Validator"
import { Translation } from "../../i18n/Translation"
+import TagInput from "../Helpers/TagInput.svelte"
/**
* Checks that the input conforms a JSON-encoded tag expression or a simpleTag`key=value`,
*/
export default class TagValidator extends Validator {
public readonly isMeta = true
+ public readonly inputHelper = TagInput
+ public readonly hideInputField = true
constructor() {
super("tag", "A simple tag of the format `key=value` OR a tagExpression")
@@ -18,4 +21,5 @@ export default class TagValidator extends Validator {
isValid(tag: string, _): boolean {
return this.getFeedback(tag, _) === undefined
}
+
}
diff --git a/src/UI/InputElement/Validators/TimeValidator.ts b/src/UI/InputElement/Validators/TimeValidator.ts
index 867d352265..2d32ada1db 100644
--- a/src/UI/InputElement/Validators/TimeValidator.ts
+++ b/src/UI/InputElement/Validators/TimeValidator.ts
@@ -1,11 +1,15 @@
import { Validator } from "../Validator"
+import TimeInput from "../Helpers/TimeInput.svelte"
export class TimeValidator extends Validator {
- inputmode = "time"
+ public readonly inputmode = "time"
+ public readonly inputHelper = TimeInput
+ public readonly hideInputField = true
constructor() {
super("time", "A time picker")
}
+
}
diff --git a/src/UI/InputElement/Validators/TranslationValidator.ts b/src/UI/InputElement/Validators/TranslationValidator.ts
index f0a602e722..ca51e38c20 100644
--- a/src/UI/InputElement/Validators/TranslationValidator.ts
+++ b/src/UI/InputElement/Validators/TranslationValidator.ts
@@ -1,7 +1,10 @@
import { Validator } from "../Validator"
+import TranslationInput from "../Helpers/TranslationInput.svelte"
export default class TranslationValidator extends Validator {
+ public readonly inputHelper = TranslationInput
public readonly isMeta = true
+ public readonly hideInputField = true
constructor() {
super("translation", "Makes sure the the string is of format `Record` ")
}
@@ -14,4 +17,5 @@ export default class TranslationValidator extends Validator {
return false
}
}
+
}
diff --git a/src/UI/InputElement/Validators/WikidataValidator.ts b/src/UI/InputElement/Validators/WikidataValidator.ts
index acbaf9ffc1..51b2ccf6d0 100644
--- a/src/UI/InputElement/Validators/WikidataValidator.ts
+++ b/src/UI/InputElement/Validators/WikidataValidator.ts
@@ -3,9 +3,11 @@ import { Validator } from "../Validator"
import { Translation } from "../../i18n/Translation"
import Translations from "../../i18n/Translations"
import MarkdownUtils from "../../../Utils/MarkdownUtils"
+import WikidataInputHelper from "../Helpers/WikidataInputHelper.svelte"
export default class WikidataValidator extends Validator {
public static readonly _searchCache = new Map>()
+ public readonly inputHelper = WikidataInputHelper
private static docs = [
"#### Helper arguments",
@@ -104,6 +106,10 @@ Another example is to search for species and trees:
if (str === undefined) {
return false
}
+ if (str.length === 0) {
+ // Don't show an exclamation mark if empty, the other code will prevent saving
+ return true
+ }
if (str.length == 1) {
return false
}
@@ -182,4 +188,5 @@ Another example is to search for species and trees:
}
return clipped
}
+
}
diff --git a/src/UI/Popup/TagRendering/FreeformInput.svelte b/src/UI/Popup/TagRendering/FreeformInput.svelte
index 23e7bacac0..850c5a8c22 100644
--- a/src/UI/Popup/TagRendering/FreeformInput.svelte
+++ b/src/UI/Popup/TagRendering/FreeformInput.svelte
@@ -8,8 +8,8 @@
import InputHelper from "../../InputElement/InputHelper.svelte"
import type { Feature } from "geojson"
import { Unit } from "../../../Models/Unit"
- import InputHelpers from "../../InputElement/InputHelpers"
import type { SpecialVisualizationState } from "../../SpecialVisualization"
+ import Validators from "../../InputElement/Validators"
export let value: UIEventSource
export let unvalidatedText: UIEventSource = new UIEventSource(value.data)
@@ -29,11 +29,12 @@
}
const dispatch = createEventDispatcher<{ selected }>()
+ let hideInput = Validators.get(config.freeform.type).hideInputField
export let feedback: UIEventSource
onDestroy(
value.addCallbackD(() => {
dispatch("selected")
- })
+ }),
)
function getCountry() {
@@ -44,19 +45,30 @@
{#if inline}
-
+ {#if hideInput}
+
+ {:else}
+
+ {/if}
- {:else if InputHelpers.hideInputField.indexOf(config.freeform.type) < 0}
+ {:else if !hideInput}
{/if}
-
+ {#if !(inline && hideInput)}
+
+ {/if}
diff --git a/src/assets/svg/Direction_stroke.svelte b/src/assets/svg/Direction_stroke.svelte
index c6e5a51af2..d1d0d8711c 100644
--- a/src/assets/svg/Direction_stroke.svelte
+++ b/src/assets/svg/Direction_stroke.svelte
@@ -1,4 +1,4 @@
-
\ No newline at end of file
+
\ No newline at end of file