From f15dfa905a1ccbb2a5910b719a3b3f02bd43f3ce Mon Sep 17 00:00:00 2001
From: pietervdvn
Date: Thu, 21 Apr 2022 20:39:09 +0200
Subject: [PATCH 01/27] Small fixes to themes
---
assets/layers/waste_basket/waste_basket.json | 3 ++-
assets/themes/grb_import/grb.json | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/assets/layers/waste_basket/waste_basket.json b/assets/layers/waste_basket/waste_basket.json
index f0cda12055..88a369908e 100644
--- a/assets/layers/waste_basket/waste_basket.json
+++ b/assets/layers/waste_basket/waste_basket.json
@@ -31,6 +31,7 @@
"de": "Dies ist ein öffentlicher Abfalleimer, in den Sie Ihren Müll entsorgen können."
},
"tagRenderings": [
+ "images",
{
"id": "waste-basket-waste-types",
"question": {
@@ -293,4 +294,4 @@
}
}
]
-}
\ No newline at end of file
+}
diff --git a/assets/themes/grb_import/grb.json b/assets/themes/grb_import/grb.json
index 067a00ba22..b8abc07595 100644
--- a/assets/themes/grb_import/grb.json
+++ b/assets/themes/grb_import/grb.json
@@ -469,7 +469,7 @@
"_overlaps_with=feat.get('_overlaps_with_buildings').find(f => f.overlap > 1 /* square meter */ )",
"_osm_obj:source:ref=feat.get('_overlaps_with')?.feat?.properties['source:geometry:ref']",
"_osm_obj:id=feat.get('_overlaps_with')?.feat?.properties?.id",
- "_osm_obj:source:date=feat.get('_overlaps_with')?.feat?.properties['source:geometry:date'].replace(/\\//g, '-')",
+ "_osm_obj:source:date=(feat.get('_overlaps_with')?.feat?.properties ?? {})['source:geometry:date']?.replace(/\\//g, '-')",
"_osm_obj:building=feat.get('_overlaps_with')?.feat?.properties?.building",
"_osm_obj:addr:street=(feat.get('_overlaps_with')?.feat?.properties ?? {})['addr:street']",
"_osm_obj:addr:housenumber=(feat.get('_overlaps_with')?.feat?.properties ?? {})['addr:housenumber']",
From 389e3f18a04cf27a0e71040ea822631a2bbd0e14 Mon Sep 17 00:00:00 2001
From: pietervdvn
Date: Fri, 22 Apr 2022 01:45:54 +0200
Subject: [PATCH 02/27] Improve wikidata search with filtering, add search box
for species to trees
---
Logic/Web/Wikidata.ts | 135 +++++++++++++++++--------
UI/Input/ValidatedTextField.ts | 40 ++++++--
UI/SpecialVisualizations.ts | 92 +++++++++++------
UI/Wikipedia/WikidataSearchBox.ts | 20 +++-
assets/layers/etymology/etymology.json | 5 +-
assets/layers/tree_node/tree_node.json | 28 +++++
test.ts | 42 +++-----
7 files changed, 249 insertions(+), 113 deletions(-)
diff --git a/Logic/Web/Wikidata.ts b/Logic/Web/Wikidata.ts
index 9364750012..d4049a24e6 100644
--- a/Logic/Web/Wikidata.ts
+++ b/Logic/Web/Wikidata.ts
@@ -1,6 +1,6 @@
import {Utils} from "../../Utils";
import {UIEventSource} from "../UIEventSource";
-import * as wds from "wikibase-sdk"
+import * as wds from "wikidata-sdk"
export class WikidataResponse {
public readonly id: string
@@ -126,13 +126,22 @@ export interface WikidataSearchoptions {
maxCount?: 20 | number
}
+export interface WikidataAdvancedSearchoptions extends WikidataSearchoptions {
+ instanceOf?: number[];
+ notInstanceOf?: number[]
+}
+
+
/**
* Utility functions around wikidata
*/
export default class Wikidata {
private static readonly _identifierPrefixes = ["Q", "L"].map(str => str.toLowerCase())
- private static readonly _prefixesToRemove = ["https://www.wikidata.org/wiki/Lexeme:", "https://www.wikidata.org/wiki/", "Lexeme:"].map(str => str.toLowerCase())
+ private static readonly _prefixesToRemove = ["https://www.wikidata.org/wiki/Lexeme:",
+ "https://www.wikidata.org/wiki/",
+ "http://www.wikidata.org/entity/",
+ "Lexeme:"].map(str => str.toLowerCase())
private static readonly _cache = new Map>()
@@ -147,25 +156,51 @@ export default class Wikidata {
Wikidata._cache.set(key, src)
return src;
}
-
- public static async searchAdvanced(text: string, options: WikidataSearchoptions & {
- instanceOf: number}){
+
+ /**
+ * Given a search text, searches for the relevant wikidata entries, excluding pages "outside of the main tree", e.g. disambiguation pages.
+ * Optionally, an 'instance of' can be given to limit the scope, e.g. instanceOf:5 (humans) will only search for humans
+ */
+ public static async searchAdvanced(text: string, options: WikidataAdvancedSearchoptions): Promise<{
+ id: string,
+ relevance?: number,
+ label: string,
+ description?: string
+ }[]> {
+ let instanceOf = ""
+ if (options?.instanceOf !== undefined && options.instanceOf.length > 0) {
+ const phrases = options.instanceOf.map(q => `{ ?item wdt:P31/wdt:P279* wd:Q${q}. }`)
+ instanceOf = "{"+ phrases.join(" UNION ") + "}"
+ }
+ const forbidden = (options?.notInstanceOf ?? [])
+ .concat([17379835]) // blacklist 'wikimedia pages outside of the main knowledge tree', e.g. disambiguation pages
+ const minusPhrases = forbidden.map(q => `MINUS {?item wdt:P31/wdt:P279* wd:Q${q} .}`)
const sparql = `SELECT * WHERE {
SERVICE wikibase:mwapi {
bd:serviceParam wikibase:api "EntitySearch" .
- bd:serviceParam wikibase:endpoint "www.wikidata.org" .
- bd:serviceParam mwapi:search "${text}" .
- bd:serviceParam mwapi:language "${options.lang}" .
- ?item wikibase:apiOutputItem mwapi:item .
- ?num wikibase:apiOrdinal true .
- }
- ?item (wdt:P279|wdt:P31) wd:Q${options.instanceOf}
- } ORDER BY ASC(?num) LIMIT ${options.maxCount}`
+ bd:serviceParam wikibase:endpoint "www.wikidata.org" .
+ bd:serviceParam mwapi:search "${text}" .
+ bd:serviceParam mwapi:language "${options.lang}" .
+ ?item wikibase:apiOutputItem mwapi:item .
+ ?num wikibase:apiOrdinal true .
+ bd:serviceParam wikibase:limit ${Math.round((options.maxCount ?? 20) * 1.5) /*Some padding for disambiguation pages */} .
+ ?label wikibase:apiOutput mwapi:label .
+ ?description wikibase:apiOutput "@description" .
+ }
+ ${instanceOf}
+ ${minusPhrases.join("\n ")}
+ } ORDER BY ASC(?num) LIMIT ${options.maxCount ?? 20}`
const url = wds.sparqlQuery(sparql)
- const result = await Utils.downloadJson(url, {"User-Agent": "MapComplete script"})
- return result.results.bindings
-
+ const result = await Utils.downloadJson(url)
+ /*The full uri of the wikidata-item*/
+
+ return result.results.bindings.map(({item, label, description, num}) => ({
+ relevance: num?.value,
+ id: item?.value,
+ label: label?.value,
+ description: description?.value
+ }))
}
public static async search(
@@ -215,39 +250,28 @@ export default class Wikidata {
public static async searchAndFetch(
search: string,
- options?: WikidataSearchoptions
+ options?: WikidataAdvancedSearchoptions
): Promise {
- const maxCount = options.maxCount
// We provide some padding to filter away invalid values
- options.maxCount = Math.ceil((options.maxCount ?? 20) * 1.5)
- const searchResults = await Wikidata.search(search, options)
- const maybeResponses = await Promise.all(searchResults.map(async r => {
- try {
- return await Wikidata.LoadWikidataEntry(r.id).AsPromise()
- } catch (e) {
- console.error(e)
- return undefined;
- }
- }))
- const responses = maybeResponses
- .map(r => r["success"])
- .filter(wd => {
- if (wd === undefined) {
- return false;
+ const searchResults = await Wikidata.searchAdvanced(search, options)
+ const maybeResponses = await Promise.all(
+ searchResults.map(async r => {
+ try {
+ console.log("Loading ", r.id)
+ return await Wikidata.LoadWikidataEntry(r.id).AsPromise()
+ } catch (e) {
+ console.error(e)
+ return undefined;
}
- if (wd.claims.get("P31" /*Instance of*/)?.has("Q4167410"/* Wikimedia Disambiguation page*/)) {
- return false;
- }
- return true;
- })
- responses.splice(maxCount, responses.length - maxCount)
- return responses
+ }))
+ return Utils.NoNull(maybeResponses.map(r => r["success"]))
}
/**
* Gets the 'key' segment from a URL
- *
+ *
* Wikidata.ExtractKey("https://www.wikidata.org/wiki/Lexeme:L614072") // => "L614072"
+ * Wikidata.ExtractKey("http://www.wikidata.org/entity/Q55008046") // => "QQ55008046"
*/
public static ExtractKey(value: string | number): string {
if (typeof value === "number") {
@@ -291,6 +315,35 @@ export default class Wikidata {
return undefined;
}
+ /**
+ * Converts 'Q123' into 123, returns undefined if invalid
+ *
+ * Wikidata.QIdToNumber("Q123") // => 123
+ * Wikidata.QIdToNumber(" Q123 ") // => 123
+ * Wikidata.QIdToNumber(" X123 ") // => undefined
+ * Wikidata.QIdToNumber(" Q123X ") // => undefined
+ * Wikidata.QIdToNumber(undefined) // => undefined
+ * Wikidata.QIdToNumber(123) // => 123
+ */
+ public static QIdToNumber(q: string | number): number | undefined {
+ if(q === undefined || q === null){
+ return
+ }
+ if(typeof q === "number"){
+ return q
+ }
+ q = q.trim()
+ if (!q.startsWith("Q")) {
+ return
+ }
+ q = q.substr(1)
+ const n = Number(q)
+ if (isNaN(n)) {
+ return
+ }
+ return n
+ }
+
public static IdToArticle(id: string) {
if (id.startsWith("Q")) {
return "https://wikidata.org/wiki/" + id
diff --git a/UI/Input/ValidatedTextField.ts b/UI/Input/ValidatedTextField.ts
index 5e8f21fbab..15fe25b7eb 100644
--- a/UI/Input/ValidatedTextField.ts
+++ b/UI/Input/ValidatedTextField.ts
@@ -250,13 +250,15 @@ class WikidataTextField extends TextFieldDef {
["subarg", "doc"],
[["removePrefixes", "remove these snippets of text from the start of the passed string to search"],
["removePostfixes", "remove these snippets of text from the end of the passed string to search"],
+ ["instanceOf","A list of Q-identifier which indicates that the search results _must_ be an entity of this type, e.g. [`Q5`](https://www.wikidata.org/wiki/Q5) for humans"],
+ ["notInstanceof","A list of Q-identifiers which indicates that the search results _must not_ be an entity of this type, e.g. [`Q79007`](https://www.wikidata.org/wiki/Q79007) to filter away all streets from the search results"]
]
)])
]]),
new Title("Example usage"),
`The following is the 'freeform'-part of a layer config which will trigger a search for the wikidata item corresponding with the name of the selected feature. It will also remove '-street', '-square', ... if found at the end of the name
-\`\`\`
+\`\`\`json
"freeform": {
"key": "name:etymology:wikidata",
"type": "wikidata",
@@ -269,11 +271,29 @@ class WikidataTextField extends TextFieldDef {
"path",
"square",
"plaza",
- ]
+ ],
+ "#": "Remove streets and parks from the search results:"
+ "notInstanceOf": ["Q79007","Q22698"]
}
+
]
}
-\`\`\``
+\`\`\`
+
+Another example is to search for species and trees:
+
+\`\`\`json
+ "freeform": {
+ "key": "species:wikidata",
+ "type": "wikidata",
+ "helperArgs": [
+ "species",
+ {
+ "instanceOf": [10884, 16521]
+ }]
+ }
+\`\`\`
+`
]));
}
@@ -304,9 +324,9 @@ class WikidataTextField extends TextFieldDef {
const args = inputHelperOptions.args ?? []
const searchKey = args[0] ?? "name"
- let searchFor = inputHelperOptions.feature?.properties[searchKey]?.toLowerCase()
+ let searchFor = (inputHelperOptions.feature?.properties[searchKey]?.toLowerCase() ?? "")
- const options = args[1]
+ const options: any = args[1]
if (searchFor !== undefined && options !== undefined) {
const prefixes = options["removePrefixes"]
const postfixes = options["removePostfixes"]
@@ -325,10 +345,18 @@ class WikidataTextField extends TextFieldDef {
}
}
+
+ let instanceOf : number[] = Utils.NoNull((options?.instanceOf ?? []).map(i => Wikidata.QIdToNumber(i)))
+ let notInstanceOf : number[] = Utils.NoNull((options?.notInstanceOf ?? []).map(i => Wikidata.QIdToNumber(i)))
+ console.log("Instance of", instanceOf)
+
+
return new WikidataSearchBox({
value: currentValue,
- searchText: new UIEventSource(searchFor)
+ searchText: new UIEventSource(searchFor),
+ instanceOf,
+ notInstanceOf
})
}
}
diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts
index 046bd28f60..3203553932 100644
--- a/UI/SpecialVisualizations.ts
+++ b/UI/SpecialVisualizations.ts
@@ -46,6 +46,8 @@ import {LoginToggle} from "./Popup/LoginButton";
import {start} from "repl";
import {SubstitutedTranslation} from "./SubstitutedTranslation";
import {TextField} from "./Input/TextField";
+import Wikidata, {WikidataResponse} from "../Logic/Web/Wikidata";
+import {Translation} from "./i18n/Translation";
export interface SpecialVisualization {
funcName: string,
@@ -159,19 +161,19 @@ class CloseNoteButton implements SpecialVisualization {
tags.ping()
})
})
-
- if((params.minZoom??"") !== "" && !isNaN(Number(params.minZoom))){
- closeButton = new Toggle(
+
+ if ((params.minZoom ?? "") !== "" && !isNaN(Number(params.minZoom))) {
+ closeButton = new Toggle(
closeButton,
params.zoomButton ?? "",
- state. locationControl.map(l => l.zoom >= Number(params.minZoom))
+ state.locationControl.map(l => l.zoom >= Number(params.minZoom))
)
}
-
+
return new LoginToggle(new Toggle(
t.isClosed.SetClass("thanks"),
closeButton,
-
+
isClosed
), t.loginToClose, state)
}
@@ -180,7 +182,7 @@ class CloseNoteButton implements SpecialVisualization {
export default class SpecialVisualizations {
- public static specialVisualizations : SpecialVisualization[] = SpecialVisualizations.init()
+ public static specialVisualizations: SpecialVisualization[] = SpecialVisualizations.init()
public static HelpMessage() {
@@ -207,28 +209,28 @@ export default class SpecialVisualizations {
));
return new Combine([
- new Combine([
-
- new Title("Special tag renderings", 1),
-
- "In a tagrendering, some special values are substituted by an advanced UI-element. This allows advanced features and visualizations to be reused by custom themes or even to query third-party API's.",
- "General usage is `{func_name()}`, `{func_name(arg, someotherarg)}` or `{func_name(args):cssStyle}`. Note that you _do not_ need to use quotes around your arguments, the comma is enough to separate them. This also implies you cannot use a comma in your args",
- new Title("Using expanded syntax",4),
- `Instead of using \`{"render": {"en": "{some_special_visualisation(some_arg, some other really long message, more args)} , "nl": "{some_special_visualisation(some_arg, een boodschap in een andere taal, more args)}}, one can also write`,
- new FixedUiElement(JSON.stringify({
- render: {
- special:{
- type: "some_special_visualisation",
- "argname": "some_arg",
- "message":{
- en:"some other really long message",
- nl: "een boodschap in een andere taal"
- },
- "other_arg_name":"more args"
+ new Combine([
+
+ new Title("Special tag renderings", 1),
+
+ "In a tagrendering, some special values are substituted by an advanced UI-element. This allows advanced features and visualizations to be reused by custom themes or even to query third-party API's.",
+ "General usage is `{func_name()}`, `{func_name(arg, someotherarg)}` or `{func_name(args):cssStyle}`. Note that you _do not_ need to use quotes around your arguments, the comma is enough to separate them. This also implies you cannot use a comma in your args",
+ new Title("Using expanded syntax", 4),
+ `Instead of using \`{"render": {"en": "{some_special_visualisation(some_arg, some other really long message, more args)} , "nl": "{some_special_visualisation(some_arg, een boodschap in een andere taal, more args)}}, one can also write`,
+ new FixedUiElement(JSON.stringify({
+ render: {
+ special: {
+ type: "some_special_visualisation",
+ "argname": "some_arg",
+ "message": {
+ en: "some other really long message",
+ nl: "een boodschap in een andere taal"
+ },
+ "other_arg_name": "more args"
+ }
}
- }
- })).SetClass("code")
- ]).SetClass("flex flex-col"),
+ })).SetClass("code")
+ ]).SetClass("flex flex-col"),
...helpTexts
]
).SetClass("flex flex-col");
@@ -297,6 +299,32 @@ export default class SpecialVisualizations {
)
},
+ {
+ funcName: "wikidata_label",
+ docs: "Shows the label of the corresponding wikidata-item",
+ args: [
+ {
+ name: "keyToShowWikidataFor",
+ doc: "Use the wikidata entry from this key to show the label",
+ defaultValue: "wikidata"
+ }
+ ],
+ example: "`{wikidata_label()}` is a basic example, `{wikipedia(name:etymology:wikidata)}` to show the label itself",
+ constr: (_, tagsSource, args) =>
+ new VariableUiElement(
+ tagsSource.map(tags => tags[args[0]])
+ .map(wikidata => {
+ wikidata = Utils.NoEmpty(wikidata?.split(";")?.map(wd => wd.trim()) ?? [])[0]
+ const entry = Wikidata.LoadWikidataEntry(wikidata)
+ return new VariableUiElement(entry.map(e => {
+ if (e === undefined || e["success"] === undefined) {
+ return wikidata
+ }
+ const response = e["success"]
+ return Translation.fromMap(response.labels)
+ }))
+ }))
+ },
{
funcName: "minimap",
docs: "A small map showing the selected feature.",
@@ -482,7 +510,7 @@ export default class SpecialVisualizations {
docs: "Downloads a JSON from the given URL, e.g. '{live(example.org/data.json, shorthand:x.y.z, other:a.b.c, shorthand)}' will download the given file, will create an object {shorthand: json[x][y][z], other: json[a][b][c] out of it and will return 'other' or 'json[a][b][c]. This is made to use in combination with tags, e.g. {live({url}, {url:format}, needed_value)}",
example: "{live({url},{url:format},hour)} {live(https://data.mobility.brussels/bike/api/counts/?request=live&featureID=CB2105,hour:data.hour_cnt;day:data.day_cnt;year:data.year_cnt,hour)}",
args: [{
- name: "Url",
+ name: "Url",
doc: "The URL to load",
required: true
}, {
@@ -783,7 +811,7 @@ export default class SpecialVisualizations {
const textField = new TextField(
{
placeholder: t.addCommentPlaceholder,
- inputStyle: "width: 100%; height: 6rem;",
+ inputStyle: "width: 100%; height: 6rem;",
textAreaRows: 3,
htmlType: "area"
}
@@ -846,7 +874,7 @@ export default class SpecialVisualizations {
textField,
new Combine([
stateButtons.SetClass("sm:mr-2"),
- new Toggle(addCommentButton,
+ new Toggle(addCommentButton,
new Combine([t.typeText]).SetClass("flex items-center h-full subtle"),
textField.GetValue().map(t => t !== undefined && t.length >= 1)).SetClass("sm:mr-2")
]).SetClass("sm:flex sm:justify-between sm:items-stretch")
@@ -947,7 +975,7 @@ export default class SpecialVisualizations {
]
specialVisualizations.push(new AutoApplyButton(specialVisualizations))
-
+
return specialVisualizations;
}
diff --git a/UI/Wikipedia/WikidataSearchBox.ts b/UI/Wikipedia/WikidataSearchBox.ts
index c3df838cdd..87afd99054 100644
--- a/UI/Wikipedia/WikidataSearchBox.ts
+++ b/UI/Wikipedia/WikidataSearchBox.ts
@@ -17,14 +17,20 @@ export default class WikidataSearchBox extends InputElement {
IsSelected: UIEventSource = new UIEventSource(false);
private readonly wikidataId: UIEventSource
private readonly searchText: UIEventSource
+ private readonly instanceOf?: number[];
+ private readonly notInstanceOf?: number[];
constructor(options?: {
searchText?: UIEventSource,
- value?: UIEventSource
+ value?: UIEventSource,
+ notInstanceOf?: number[],
+ instanceOf?: number[]
}) {
super();
this.searchText = options?.searchText
this.wikidataId = options?.value ?? new UIEventSource(undefined);
+ this.instanceOf = options?.instanceOf
+ this.notInstanceOf = options?.notInstanceOf
}
GetValue(): UIEventSource {
@@ -59,7 +65,9 @@ export default class WikidataSearchBox extends InputElement {
if (promise === undefined) {
promise = Wikidata.searchAndFetch(searchText, {
lang,
- maxCount: 5
+ maxCount: 5,
+ notInstanceOf: this.notInstanceOf,
+ instanceOf: this.instanceOf
}
)
WikidataSearchBox._searchCache.set(key, promise)
@@ -75,13 +83,15 @@ export default class WikidataSearchBox extends InputElement {
return new Combine([Translations.t.general.wikipedia.failed.Clone().SetClass("alert"), searchFailMessage.data])
}
+ if (searchField.GetValue().data.length === 0) {
+ return Translations.t.general.wikipedia.doSearch
+ }
+
if (searchResults.length === 0) {
return Translations.t.general.wikipedia.noResults.Subs({search: searchField.GetValue().data ?? ""})
}
- if (searchResults.length === 0) {
- return Translations.t.general.wikipedia.doSearch
- }
+
return new Combine(searchResults.map(wikidataresponse => {
const el = WikidataPreviewBox.WikidataResponsePreview(wikidataresponse).SetClass("rounded-xl p-1 sm:p-2 md:p-3 m-px border-2 sm:border-4 transition-colors")
diff --git a/assets/layers/etymology/etymology.json b/assets/layers/etymology/etymology.json
index cf45d7a5d6..1447c7d812 100644
--- a/assets/layers/etymology/etymology.json
+++ b/assets/layers/etymology/etymology.json
@@ -52,6 +52,7 @@
"helperArgs": [
"name",
{
+ "notInstanceOf": ["Q79007","Q22698"],
"removePostfixes": [
"steenweg",
"heirbaan",
@@ -70,7 +71,9 @@
"wegel",
"kerk",
"church",
- "kaai"
+ "kaai",
+ "park",
+ "parque"
]
}
]
diff --git a/assets/layers/tree_node/tree_node.json b/assets/layers/tree_node/tree_node.json
index 821d2650b1..3035cc7ef0 100644
--- a/assets/layers/tree_node/tree_node.json
+++ b/assets/layers/tree_node/tree_node.json
@@ -314,6 +314,34 @@
]
}
},
+ {
+ "id": "tree-species-wikidata",
+ "question": {
+ "en": "What species is this tree?"
+ },
+ "render": {
+ "*":"{wikipedia(species:wikidata):max-height: 25rem}"
+ },
+ "freeform": {
+ "key": "species:wikidata",
+ "type": "wikidata",
+ "helperArgs": [
+ "species",
+ {
+ "instanceOf": [10884, 16521]
+ }]
+ }
+ },
+ {
+ "id": "tree-wikipedia",
+ "#": "If this tree has a wikipedia article, show it. People can _only_ set the species though!",
+ "render": {
+ "*":"{wikipedia()}"
+ },
+ "condition": {
+ "or": ["wikipedia~*","wikidata~*"]
+ }
+ },
{
"render": {
"nl": "Naam: {name}",
diff --git a/test.ts b/test.ts
index 9a6b02f972..4640df0ca4 100644
--- a/test.ts
+++ b/test.ts
@@ -1,31 +1,17 @@
-import Combine from "./UI/Base/Combine";
-import ValidatedTextField from "./UI/Input/ValidatedTextField";
-import Title from "./UI/Base/Title";
-import {FixedUiElement} from "./UI/Base/FixedUiElement";
import {VariableUiElement} from "./UI/Base/VariableUIElement";
import {UIEventSource} from "./Logic/UIEventSource";
-import {Translation} from "./UI/i18n/Translation";
+import Wikidata from "./Logic/Web/Wikidata";
+import Combine from "./UI/Base/Combine";
+import {FixedUiElement} from "./UI/Base/FixedUiElement";
-new Combine(
- ValidatedTextField.AvailableTypes().map(key => {
- let inp;
- const feedback = new UIEventSource(undefined)
- try {
- inp = ValidatedTextField.ForType(key).ConstructInputElement({
- feedback,
- country: () => "be"
- });
- } catch (e) {
- console.error(e)
- inp = new FixedUiElement(e).SetClass("alert")
- }
-
- return new Combine([
- new Title(key),
- inp,
- new VariableUiElement(inp.GetValue()),
- new VariableUiElement(feedback.map(v => v?.SetClass("alert")))
- ]);
- }
- )
-).AttachTo("maindiv")
\ No newline at end of file
+const result = UIEventSource.FromPromise(
+ Wikidata.searchAdvanced("WOlf", {
+ lang: "nl",
+ maxCount: 100,
+ instanceOf: 5
+ })
+)
+result.addCallbackAndRunD(r => console.log(r))
+new VariableUiElement(result.map(items =>new Combine( (items??[])?.map(i =>
+ new FixedUiElement(JSON.stringify(i, null, " ")).SetClass("p-4 block")
+)) )).SetClass("flex flex-col").AttachTo("maindiv")
\ No newline at end of file
From 979041dacbc665829fc7f24a1183caeaf74c3272 Mon Sep 17 00:00:00 2001
From: pietervdvn
Date: Fri, 22 Apr 2022 03:17:40 +0200
Subject: [PATCH 03/27] Better error handling
---
Models/ThemeConfig/Conversion/Conversion.ts | 19 +++++++++------
Models/ThemeConfig/Conversion/PrepareTheme.ts | 1 +
Models/ThemeConfig/Conversion/Validation.ts | 11 ++++++---
Models/ThemeConfig/Json/UnitConfigJson.ts | 7 ++++--
Models/ThemeConfig/LayerConfig.ts | 3 +++
scripts/generateLayerOverview.ts | 24 ++++++++++++-------
6 files changed, 44 insertions(+), 21 deletions(-)
diff --git a/Models/ThemeConfig/Conversion/Conversion.ts b/Models/ThemeConfig/Conversion/Conversion.ts
index 3d9aa1183a..5b6b72c008 100644
--- a/Models/ThemeConfig/Conversion/Conversion.ts
+++ b/Models/ThemeConfig/Conversion/Conversion.ts
@@ -216,13 +216,18 @@ export class Fuse extends DesugaringStep {
const information = []
for (let i = 0; i < this.steps.length; i++) {
const step = this.steps[i];
- let r = step.convert(json, "While running step " + step.name + ": " + context)
- errors.push(...r.errors ?? [])
- warnings.push(...r.warnings ?? [])
- information.push(...r.information ?? [])
- json = r.result
- if (errors.length > 0) {
- break;
+ try{
+ let r = step.convert(json, "While running step " + step.name + ": " + context)
+ errors.push(...r.errors ?? [])
+ warnings.push(...r.warnings ?? [])
+ information.push(...r.information ?? [])
+ json = r.result
+ if (errors.length > 0) {
+ break;
+ }
+ }catch(e){
+ console.error("Step "+step.name+" failed due to "+e);
+ throw e
}
}
return {
diff --git a/Models/ThemeConfig/Conversion/PrepareTheme.ts b/Models/ThemeConfig/Conversion/PrepareTheme.ts
index 7c1aa17211..8a9877e108 100644
--- a/Models/ThemeConfig/Conversion/PrepareTheme.ts
+++ b/Models/ThemeConfig/Conversion/PrepareTheme.ts
@@ -479,6 +479,7 @@ export class PrepareTheme extends Fuse {
}) {
super(
"Fully prepares and expands a theme",
+
new AddContextToTransltionsInLayout(),
new PreparePersonalTheme(state),
new WarnForUnsubstitutedLayersInTheme(),
diff --git a/Models/ThemeConfig/Conversion/Validation.ts b/Models/ThemeConfig/Conversion/Validation.ts
index 912707877c..b0c121953b 100644
--- a/Models/ThemeConfig/Conversion/Validation.ts
+++ b/Models/ThemeConfig/Conversion/Validation.ts
@@ -217,12 +217,17 @@ class MiscThemeChecks extends DesugaringStep{
convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors?: string[]; warnings?: string[]; information?: string[] } {
const warnings = []
+ const errors = []
+ if(json.id !== "personal" && (json.layers === undefined || json.layers.length === 0)){
+ errors.push("The theme "+json.id+" has no 'layers' defined ("+context+")")
+ }
if(json.socialImage === ""){
warnings.push("Social image for theme "+json.id+" is the emtpy string")
}
return {
result :json,
- warnings
+ warnings,
+ errors
};
}
}
@@ -231,8 +236,8 @@ export class PrevalidateTheme extends Fuse {
constructor() {
super("Various consistency checks on the raw JSON",
- new OverrideShadowingCheck(),
- new MiscThemeChecks()
+ new MiscThemeChecks(),
+ new OverrideShadowingCheck()
);
}
diff --git a/Models/ThemeConfig/Json/UnitConfigJson.ts b/Models/ThemeConfig/Json/UnitConfigJson.ts
index 5eba5eaf49..bde2683b23 100644
--- a/Models/ThemeConfig/Json/UnitConfigJson.ts
+++ b/Models/ThemeConfig/Json/UnitConfigJson.ts
@@ -18,9 +18,12 @@ export default interface UnitConfigJson {
export interface ApplicableUnitJson {
/**
- * The canonical value which will be added to the text.
+ * The canonical value which will be added to the value in OSM.
* e.g. "m" for meters
- * If the user inputs '42', the canonical value will be added and it'll become '42m'
+ * If the user inputs '42', the canonical value will be added and it'll become '42m'.
+ *
+ * Important: often, _no_ canonical values are expected, e.g. in the case of 'maxspeed' where 'km/h' is the default.
+ * In this case, an empty string should be used
*/
canonicalDenomination: string,
/**
diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts
index b7863b4274..ade6d6b6df 100644
--- a/Models/ThemeConfig/LayerConfig.ts
+++ b/Models/ThemeConfig/LayerConfig.ts
@@ -134,6 +134,9 @@ export default class LayerConfig extends WithContextLoader {
this.allowSplit = json.allowSplit ?? false;
this.name = Translations.T(json.name, translationContext + ".name");
+ if(json.units!==undefined && !Array.isArray(json.units)){
+ throw "At "+context+".units: the 'units'-section should be a list; you probably have an object there"
+ }
this.units = (json.units ?? []).map(((unitJson, i) => Unit.fromJson(unitJson, `${context}.unit[${i}]`)))
if (json.description !== undefined) {
diff --git a/scripts/generateLayerOverview.ts b/scripts/generateLayerOverview.ts
index d9faf2d9ab..02cc138594 100644
--- a/scripts/generateLayerOverview.ts
+++ b/scripts/generateLayerOverview.ts
@@ -203,16 +203,22 @@ class LayerOverviewUtils {
const themePath = themeInfo.path
new PrevalidateTheme().convertStrict(themeFile, themePath)
- themeFile = new PrepareTheme(convertState).convertStrict(themeFile, themePath)
-
- if(knownImagePaths === undefined){
- throw "Could not load known images/licenses"
+ try{
+
+ themeFile = new PrepareTheme(convertState).convertStrict(themeFile, themePath)
+
+ if(knownImagePaths === undefined){
+ throw "Could not load known images/licenses"
+ }
+ new ValidateThemeAndLayers(knownImagePaths, themePath, true, convertState.tagRenderings)
+ .convertStrict(themeFile, themePath)
+
+ this.writeTheme(themeFile)
+ fixed.set(themeFile.id, themeFile)
+ }catch(e){
+ console.error("ERROR: could not prepare theme "+themePath+" due to "+e)
+ throw e;
}
- new ValidateThemeAndLayers(knownImagePaths, themePath, true, convertState.tagRenderings)
- .convertStrict(themeFile, themePath)
-
- this.writeTheme(themeFile)
- fixed.set(themeFile.id, themeFile)
}
this.writeSmallOverview(themeFiles.map(tf => {
From 426ba4a930b4fcc16a393c55cfb3105eb2dc7580 Mon Sep 17 00:00:00 2001
From: Pieter Vander Vennet
Date: Fri, 22 Apr 2022 10:59:03 +0200
Subject: [PATCH 04/27] Fix test
---
Logic/Web/Wikidata.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Logic/Web/Wikidata.ts b/Logic/Web/Wikidata.ts
index d4049a24e6..c32f6b6870 100644
--- a/Logic/Web/Wikidata.ts
+++ b/Logic/Web/Wikidata.ts
@@ -271,7 +271,7 @@ export default class Wikidata {
* Gets the 'key' segment from a URL
*
* Wikidata.ExtractKey("https://www.wikidata.org/wiki/Lexeme:L614072") // => "L614072"
- * Wikidata.ExtractKey("http://www.wikidata.org/entity/Q55008046") // => "QQ55008046"
+ * Wikidata.ExtractKey("http://www.wikidata.org/entity/Q55008046") // => "Q55008046"
*/
public static ExtractKey(value: string | number): string {
if (typeof value === "number") {
@@ -378,4 +378,4 @@ export default class Wikidata {
return WikidataResponse.fromJson(response)
}
-}
\ No newline at end of file
+}
From 0aae923187ee66c12f10a5e3d7b47d42644e806c Mon Sep 17 00:00:00 2001
From: pietervdvn
Date: Fri, 22 Apr 2022 16:09:55 +0200
Subject: [PATCH 05/27] Add projected_centerpoint as pointrenderingoption
---
.../RenderingMultiPlexerFeatureSource.ts | 46 +++++++++++++------
.../Json/PointRenderingConfigJson.ts | 5 +-
Models/ThemeConfig/PointRenderingConfig.ts | 4 +-
assets/layers/etymology/etymology.json | 28 ++++++++++-
assets/layers/tree_node/tree_node.json | 17 +++++--
assets/layers/waste_basket/waste_basket.json | 4 +-
.../mapcomplete-changes.json | 42 ++++++-----------
langs/layers/en.json | 3 ++
8 files changed, 96 insertions(+), 53 deletions(-)
diff --git a/Logic/FeatureSource/Sources/RenderingMultiPlexerFeatureSource.ts b/Logic/FeatureSource/Sources/RenderingMultiPlexerFeatureSource.ts
index 5349d8d431..1f5d8cbb41 100644
--- a/Logic/FeatureSource/Sources/RenderingMultiPlexerFeatureSource.ts
+++ b/Logic/FeatureSource/Sources/RenderingMultiPlexerFeatureSource.ts
@@ -1,5 +1,5 @@
/**
- * This feature source helps the ShowDataLayer class: it introduces the necessary extra features and indiciates with what renderConfig it should be rendered.
+ * This feature source helps the ShowDataLayer class: it introduces the necessary extra features and indicates with what renderConfig it should be rendered.
*/
import {UIEventSource} from "../../UIEventSource";
import {GeoOperations} from "../../GeoOperations";
@@ -11,22 +11,25 @@ export default class RenderingMultiPlexerFeatureSource {
public readonly features: UIEventSource<(any & { pointRenderingIndex: number | undefined, lineRenderingIndex: number | undefined })[]>;
constructor(upstream: FeatureSource, layer: LayerConfig) {
+
+ const pointRenderObjects: { rendering: PointRenderingConfig, index: number }[] = layer.mapRendering.map((r, i) => ({
+ rendering: r,
+ index: i
+ }))
+ const pointRenderings = pointRenderObjects.filter(r => r.rendering.location.has("point"))
+ const centroidRenderings = pointRenderObjects.filter(r => r.rendering.location.has("centroid"))
+ const projectedCentroidRenderings = pointRenderObjects.filter(r => r.rendering.location.has("projected_centerpoint"))
+ const startRenderings = pointRenderObjects.filter(r => r.rendering.location.has("start"))
+ const endRenderings = pointRenderObjects.filter(r => r.rendering.location.has("end"))
+ const hasCentroid = centroidRenderings.length > 0 || projectedCentroidRenderings.length > 0
+ const lineRenderObjects = layer.lineRendering
+
this.features = upstream.features.map(
features => {
if (features === undefined) {
return;
}
- const pointRenderObjects: { rendering: PointRenderingConfig, index: number }[] = layer.mapRendering.map((r, i) => ({
- rendering: r,
- index: i
- }))
- const pointRenderings = pointRenderObjects.filter(r => r.rendering.location.has("point"))
- const centroidRenderings = pointRenderObjects.filter(r => r.rendering.location.has("centroid"))
- const startRenderings = pointRenderObjects.filter(r => r.rendering.location.has("start"))
- const endRenderings = pointRenderObjects.filter(r => r.rendering.location.has("end"))
-
- const lineRenderObjects = layer.lineRendering
const withIndex: (any & { pointRenderingIndex: number | undefined, lineRenderingIndex: number | undefined, multiLineStringIndex: number | undefined })[] = [];
@@ -55,12 +58,25 @@ export default class RenderingMultiPlexerFeatureSource {
}
} else {
// This is a a line: add the centroids
- for (const rendering of centroidRenderings) {
- addAsPoint(feat, rendering, GeoOperations.centerpointCoordinates(feat))
+ let centerpoint: [number, number] = undefined;
+ let projectedCenterPoint : [number, number] = undefined
+ if(hasCentroid){
+ centerpoint = GeoOperations.centerpointCoordinates(feat)
+ if(projectedCentroidRenderings.length > 0){
+ projectedCenterPoint = <[number,number]> GeoOperations.nearestPoint(feat, centerpoint).geometry.coordinates
+ }
}
+ for (const rendering of centroidRenderings) {
+ addAsPoint(feat, rendering, centerpoint)
+ }
+
if (feat.geometry.type === "LineString") {
+ for (const rendering of projectedCentroidRenderings) {
+ addAsPoint(feat, rendering, projectedCenterPoint)
+ }
+
// Add start- and endpoints
const coordinates = feat.geometry.coordinates
for (const rendering of startRenderings) {
@@ -71,6 +87,10 @@ export default class RenderingMultiPlexerFeatureSource {
addAsPoint(feat, rendering, coordinate)
}
+ }else{
+ for (const rendering of projectedCentroidRenderings) {
+ addAsPoint(feat, rendering, centerpoint)
+ }
}
// AT last, add it 'as is' to what we should render
diff --git a/Models/ThemeConfig/Json/PointRenderingConfigJson.ts b/Models/ThemeConfig/Json/PointRenderingConfigJson.ts
index 5170f89534..c9a4abc9d1 100644
--- a/Models/ThemeConfig/Json/PointRenderingConfigJson.ts
+++ b/Models/ThemeConfig/Json/PointRenderingConfigJson.ts
@@ -13,9 +13,10 @@ export default interface PointRenderingConfigJson {
/**
* All the locations that this point should be rendered at.
- * Using `location: ["point", "centroid"] will always render centerpoint
+ * Using `location: ["point", "centroid"] will always render centerpoint.
+ * 'projected_centerpoint' will show an item on the line itself, near the middle of the line. (LineStrings only)
*/
- location: ("point" | "centroid" | "start" | "end" | string)[]
+ location: ("point" | "centroid" | "start" | "end" | "projected_centerpoint" | string)[]
/**
* The icon for an element.
diff --git a/Models/ThemeConfig/PointRenderingConfig.ts b/Models/ThemeConfig/PointRenderingConfig.ts
index 0a02149b84..39dcec1563 100644
--- a/Models/ThemeConfig/PointRenderingConfig.ts
+++ b/Models/ThemeConfig/PointRenderingConfig.ts
@@ -15,8 +15,8 @@ import {VariableUiElement} from "../../UI/Base/VariableUIElement";
export default class PointRenderingConfig extends WithContextLoader {
- private static readonly allowed_location_codes = new Set(["point", "centroid", "start", "end"])
- public readonly location: Set<"point" | "centroid" | "start" | "end" | string>
+ private static readonly allowed_location_codes = new Set(["point", "centroid", "start", "end","projected_centerpoint"])
+ public readonly location: Set<"point" | "centroid" | "start" | "end" | "projected_centerpoint" | string>
public readonly icon: TagRenderingConfig;
public readonly iconBadges: { if: TagsFilter; then: TagRenderingConfig }[];
diff --git a/assets/layers/etymology/etymology.json b/assets/layers/etymology/etymology.json
index 1447c7d812..de0b63aabb 100644
--- a/assets/layers/etymology/etymology.json
+++ b/assets/layers/etymology/etymology.json
@@ -52,7 +52,10 @@
"helperArgs": [
"name",
{
- "notInstanceOf": ["Q79007","Q22698"],
+ "notInstanceOf": [
+ "Q79007",
+ "Q22698"
+ ],
"removePostfixes": [
"steenweg",
"heirbaan",
@@ -205,6 +208,29 @@
}
]
}
+ },
+ {
+ "location": [
+ "projected_centerpoint"
+ ],
+ "label": "LABEL
",
+ "icon": {
+ "render": "pin:#05d7fcaa",
+ "mappings": [
+ {
+ "if": {
+ "and": [
+ "name:etymology=",
+ "name:etymology:wikidata="
+ ]
+ },
+ "then": "pin:#fcca05aa"
+ }
+ ]
+ },
+ "iconSize": {
+ "render": "40,40,center"
+ }
}
]
}
\ No newline at end of file
diff --git a/assets/layers/tree_node/tree_node.json b/assets/layers/tree_node/tree_node.json
index 3035cc7ef0..4dd324cf1f 100644
--- a/assets/layers/tree_node/tree_node.json
+++ b/assets/layers/tree_node/tree_node.json
@@ -320,7 +320,7 @@
"en": "What species is this tree?"
},
"render": {
- "*":"{wikipedia(species:wikidata):max-height: 25rem}"
+ "*": "{wikipedia(species:wikidata):max-height: 25rem}"
},
"freeform": {
"key": "species:wikidata",
@@ -328,18 +328,25 @@
"helperArgs": [
"species",
{
- "instanceOf": [10884, 16521]
- }]
+ "instanceOf": [
+ 10884,
+ 16521
+ ]
+ }
+ ]
}
},
{
"id": "tree-wikipedia",
"#": "If this tree has a wikipedia article, show it. People can _only_ set the species though!",
"render": {
- "*":"{wikipedia()}"
+ "*": "{wikipedia()}"
},
"condition": {
- "or": ["wikipedia~*","wikidata~*"]
+ "or": [
+ "wikipedia~*",
+ "wikidata~*"
+ ]
}
},
{
diff --git a/assets/layers/waste_basket/waste_basket.json b/assets/layers/waste_basket/waste_basket.json
index 88a369908e..67d9687021 100644
--- a/assets/layers/waste_basket/waste_basket.json
+++ b/assets/layers/waste_basket/waste_basket.json
@@ -31,7 +31,7 @@
"de": "Dies ist ein öffentlicher Abfalleimer, in den Sie Ihren Müll entsorgen können."
},
"tagRenderings": [
- "images",
+ "images",
{
"id": "waste-basket-waste-types",
"question": {
@@ -294,4 +294,4 @@
}
}
]
-}
+}
\ No newline at end of file
diff --git a/assets/themes/mapcomplete-changes/mapcomplete-changes.json b/assets/themes/mapcomplete-changes/mapcomplete-changes.json
index 2f479a092b..c90abd4c4d 100644
--- a/assets/themes/mapcomplete-changes/mapcomplete-changes.json
+++ b/assets/themes/mapcomplete-changes/mapcomplete-changes.json
@@ -1,16 +1,13 @@
{
"id": "mapcomplete-changes",
"title": {
- "en": "Changes made with MapComplete",
- "de": "Änderungen mit MapComplete"
+ "en": "Changes made with MapComplete"
},
"shortDescription": {
- "en": "Shows changes made by MapComplete",
- "de": "Zeigt Änderungen von MapComplete"
+ "en": "Shows changes made by MapComplete"
},
"description": {
- "en": "This maps shows all the changes made with MapComplete",
- "de": "Diese Karte zeigt alle Änderungen die mit MapComplete gemacht wurden"
+ "en": "This maps shows all the changes made with MapComplete"
},
"maintainer": "",
"icon": "./assets/svg/logo.svg",
@@ -25,8 +22,7 @@
{
"id": "mapcomplete-changes",
"name": {
- "en": "Changeset centers",
- "de": "Schwerpunkte von Änderungssätzen"
+ "en": "Changeset centers"
},
"minzoom": 0,
"source": {
@@ -40,41 +36,35 @@
],
"title": {
"render": {
- "en": "Changeset for {theme}",
- "de": "Änderungen für {theme}"
+ "en": "Changeset for {theme}"
}
},
"description": {
- "en": "Shows all MapComplete changes",
- "de": "Zeigt alle MapComplete Änderungen"
+ "en": "Shows all MapComplete changes"
},
"tagRenderings": [
{
"id": "render_id",
"render": {
- "en": "Changeset {id} ",
- "de": "Änderung {id} "
+ "en": "Changeset {id} "
}
},
{
"id": "contributor",
"render": {
- "en": "Change made by {_last_edit:contributor} ",
- "de": "Änderung wurde von {_last_edit:contributor} gemacht"
+ "en": "Change made by {_last_edit:contributor} "
}
},
{
"id": "theme",
"render": {
- "en": "Change with theme {theme} ",
- "de": "Änderung mit Thema {theme} "
+ "en": "Change with theme {theme} "
},
"mappings": [
{
"if": "theme~http.*",
"then": {
- "en": "Change with unofficial theme {theme} ",
- "de": "Änderung mit inoffiziellem Thema {theme} "
+ "en": "Change with unofficial theme {theme} "
}
}
]
@@ -338,8 +328,7 @@
}
],
"question": {
- "en": "Themename contains {search}",
- "de": "Themenname enthält {search}"
+ "en": "Themename contains {search}"
}
}
]
@@ -355,8 +344,7 @@
}
],
"question": {
- "en": "Made by contributor {search}",
- "de": "Erstellt von {search}"
+ "en": "Made by contributor {search}"
}
}
]
@@ -372,8 +360,7 @@
}
],
"question": {
- "en": "Not made by contributor {search}",
- "de": "Nicht erstellt von {search}"
+ "en": "Not made by contributor {search}"
}
}
]
@@ -388,8 +375,7 @@
{
"id": "link_to_more",
"render": {
- "en": "More statistics can be found here ",
- "de": "Weitere Statistiken finden Sie hier "
+ "en": "More statistics can be found here "
}
},
{
diff --git a/langs/layers/en.json b/langs/layers/en.json
index 574c793046..3810cd4645 100644
--- a/langs/layers/en.json
+++ b/langs/layers/en.json
@@ -5454,6 +5454,9 @@
},
"question": "Is this a broadleaved or needleleaved tree?"
},
+ "tree-species-wikidata": {
+ "question": "What species is this tree?"
+ },
"tree_node-name": {
"mappings": {
"0": {
From 849f95feecc1fc8986042bc88bca62f2529715e1 Mon Sep 17 00:00:00 2001
From: pietervdvn
Date: Fri, 22 Apr 2022 16:10:25 +0200
Subject: [PATCH 06/27] Remove test rendering from etymology theme
---
assets/layers/etymology/etymology.json | 23 -----------------------
1 file changed, 23 deletions(-)
diff --git a/assets/layers/etymology/etymology.json b/assets/layers/etymology/etymology.json
index de0b63aabb..3268eb41bd 100644
--- a/assets/layers/etymology/etymology.json
+++ b/assets/layers/etymology/etymology.json
@@ -208,29 +208,6 @@
}
]
}
- },
- {
- "location": [
- "projected_centerpoint"
- ],
- "label": "LABEL
",
- "icon": {
- "render": "pin:#05d7fcaa",
- "mappings": [
- {
- "if": {
- "and": [
- "name:etymology=",
- "name:etymology:wikidata="
- ]
- },
- "then": "pin:#fcca05aa"
- }
- ]
- },
- "iconSize": {
- "render": "40,40,center"
- }
}
]
}
\ No newline at end of file
From 1bf3be987b3198a5511f81448541f4be4371680a Mon Sep 17 00:00:00 2001
From: pietervdvn
Date: Fri, 22 Apr 2022 16:24:21 +0200
Subject: [PATCH 07/27] Add name and species to tree title (if applicable); fix
issue with dropdown in tagrenderingquestion
---
UI/Popup/TagRenderingQuestion.ts | 8 ++++----
assets/layers/tree_node/tree_node.json | 22 ++++++++++++++++++++++
2 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts
index 60521e2735..7e32b12638 100644
--- a/UI/Popup/TagRenderingQuestion.ts
+++ b/UI/Popup/TagRenderingQuestion.ts
@@ -11,7 +11,7 @@ import {SaveButton} from "./SaveButton";
import {VariableUiElement} from "../Base/VariableUIElement";
import Translations from "../i18n/Translations";
import {FixedUiElement} from "../Base/FixedUiElement";
-import {Translation} from "../i18n/Translation";
+import {Translation, TypedTranslation} from "../i18n/Translation";
import Constants from "../../Models/Constants";
import {SubstitutedTranslation} from "../SubstitutedTranslation";
import {TagsFilter} from "../../Logic/Tags/TagsFilter";
@@ -51,7 +51,7 @@ export default class TagRenderingQuestion extends Combine {
const applicableMappingsSrc =
UIEventSource.ListStabilized(tags.map(tags => {
- const applicableMappings: { if: TagsFilter, icon?: string, then: any, ifnot?: TagsFilter, addExtraTags: Tag[] }[] = []
+ const applicableMappings: { if: TagsFilter, icon?: string, then: TypedTranslation, ifnot?: TagsFilter, addExtraTags: Tag[] }[] = []
for (const mapping of configuration.mappings ?? []) {
if (mapping.hideInAnswer === true) {
continue
@@ -158,7 +158,7 @@ export default class TagRenderingQuestion extends Combine {
private static GenerateInputElement(
state,
configuration: TagRenderingConfig,
- applicableMappings: { if: TagsFilter, then: any, icon?: string, ifnot?: TagsFilter, addExtraTags: Tag[] }[],
+ applicableMappings: { if: TagsFilter, then: TypedTranslation, icon?: string, ifnot?: TagsFilter, addExtraTags: Tag[] }[],
applicableUnit: Unit,
tagsSource: UIEventSource,
feedback: UIEventSource
@@ -207,7 +207,7 @@ export default class TagRenderingQuestion extends Combine {
applicableMappings.map((mapping, i) => {
return {
value: new And([mapping.if, ...allIfNotsExcept(i)]),
- shown: Translations.T(mapping.then)
+ shown: mapping.then.Subs(tagsSource.data)
}
})
)
diff --git a/assets/layers/tree_node/tree_node.json b/assets/layers/tree_node/tree_node.json
index f3bd0cfca1..0931dde3f6 100644
--- a/assets/layers/tree_node/tree_node.json
+++ b/assets/layers/tree_node/tree_node.json
@@ -31,6 +31,28 @@
"es": "Árbol"
},
"mappings": [
+ {
+ "if": {
+ "and": ["name~*","species:wikidata~*"]
+ },
+ "then": {
+ "*": "{name} ({wikidata_label(species:wikidata)})"
+ }
+ },
+ {
+ "if": {
+ "and": ["name~*"]
+ },
+ "then": {
+ "*": "{name}"
+ }
+ },
+ {
+ "if": "species:wikidata~*",
+ "then": {
+ "*": "{wikidata_label(species:wikidata)}"
+ }
+ },
{
"if": "species~*",
"then": {
From c36bce8100af49f818b42fd8cda3123ce11e90cb Mon Sep 17 00:00:00 2001
From: pietervdvn
Date: Sat, 23 Apr 2022 02:14:31 +0200
Subject: [PATCH 08/27] Add statistics download button
---
UI/ImportFlow/ImportViewerGui.ts | 45 +++++++++++++++++++++++++++++++-
1 file changed, 44 insertions(+), 1 deletion(-)
diff --git a/UI/ImportFlow/ImportViewerGui.ts b/UI/ImportFlow/ImportViewerGui.ts
index 2c427bd9c5..779214cd41 100644
--- a/UI/ImportFlow/ImportViewerGui.ts
+++ b/UI/ImportFlow/ImportViewerGui.ts
@@ -44,6 +44,47 @@ interface NoteState {
status: "imported" | "already_mapped" | "invalid" | "closed" | "not_found" | "open" | "has_comments"
}
+class DownloadStatisticsButton extends SubtleButton {
+ constructor(states: NoteState[][]) {
+ super(Svg.statistics_svg(), "Download statistics");
+ this.onClick(() => {
+
+ const st: NoteState[] = [].concat(...states)
+
+ const fields = [
+ "id",
+ "status",
+ "theme",
+ "date_created",
+ "date_closed",
+ "days_open",
+ "intro",
+ "...comments"
+ ]
+ const values : string[][] = st.map(note => {
+
+
+ return [note.props.id+"",
+ note.status,
+ note.theme,
+ note.props.date_created?.substr(0, note.props.date_created.length - 3),
+ note.props.closed_at?.substr(0, note.props.closed_at.length - 3) ?? "",
+ JSON.stringify( note.intro),
+ ...note.props.comments.map(c => JSON.stringify(c.user)+": "+JSON.stringify(c.text))
+ ]
+ })
+
+ Utils.offerContentsAsDownloadableFile(
+ [fields, ...values].map(c => c.join(", ")).join("\n"),
+ "mapcomplete_import_notes_overview.csv",
+ {
+ mimetype: "text/csv"
+ }
+ )
+ })
+ }
+}
+
class MassAction extends Combine {
constructor(state: UserRelatedState, props: NoteProperties[]) {
const textField = ValidatedTextField.ForType("text").ConstructInputElement()
@@ -303,7 +344,9 @@ class ImportInspector extends VariableUiElement {
contents.push(accordeon)
const content = new Combine(contents)
return new LeftIndex(
- [new TableOfContents(content, {noTopLevel: true, maxDepth: 1}).SetClass("subtle")],
+ [new TableOfContents(content, {noTopLevel: true, maxDepth: 1}).SetClass("subtle"),
+ new DownloadStatisticsButton(perBatch)
+ ],
content
)
From bc953abe48078bb866a281835143a9bbf6f8c211 Mon Sep 17 00:00:00 2001
From: pietervdvn
Date: Sat, 23 Apr 2022 15:20:54 +0200
Subject: [PATCH 09/27] Refactoring of the climbing theme
---
Models/ThemeConfig/Conversion/PrepareTheme.ts | 50 +-
Models/ThemeConfig/DependencyCalculator.ts | 2 +-
Models/ThemeConfig/LayerConfig.ts | 6 +-
UI/BigComponents/FilterView.ts | 11 +-
UI/SpecialVisualizations.ts | 4 +
assets/layers/climbing/climbing.json | 621 +++++++-----
.../layers/climbing_area/climbing_area.json | 308 ++++++
assets/layers/climbing_gym/climbing_gym.json | 70 +-
.../climbing_opportunity.json | 12 +-
.../layers/climbing_route/climbing_route.json | 38 +-
assets/themes/climbing/climbing.json | 935 ++++++------------
assets/themes/trees/trees.json | 2 +-
css/index-tailwind-output.css | 18 +-
index.css | 20 +
scripts/generateLayerOverview.ts | 3 +
15 files changed, 1099 insertions(+), 1001 deletions(-)
create mode 100644 assets/layers/climbing_area/climbing_area.json
diff --git a/Models/ThemeConfig/Conversion/PrepareTheme.ts b/Models/ThemeConfig/Conversion/PrepareTheme.ts
index 8a9877e108..1d61f2a3b3 100644
--- a/Models/ThemeConfig/Conversion/PrepareTheme.ts
+++ b/Models/ThemeConfig/Conversion/PrepareTheme.ts
@@ -330,12 +330,19 @@ class AddDependencyLayersToTheme extends DesugaringStep {
private readonly _state: DesugaringContext;
constructor(state: DesugaringContext,) {
- super("If a layer has a dependency on another layer, these layers are added automatically on the theme. (For example: defibrillator depends on 'walls_and_buildings' to snap onto. This layer is added automatically)", ["layers"], "AddDependencyLayersToTheme");
+ super(
+ `If a layer has a dependency on another layer, these layers are added automatically on the theme. (For example: defibrillator depends on 'walls_and_buildings' to snap onto. This layer is added automatically)
+
+ Note that these layers are added _at the start_ of the layer list, meaning that they will see _every_ feature.
+ Furthermore, \`passAllFeatures\` will be set, so that they won't steal away features from further layers.
+ Some layers (e.g. \`all_buildings_and_walls\' or \'streets_with_a_name\') are invisible, so by default, \'force_load\' is set too.
+ `, ["layers"], "AddDependencyLayersToTheme");
this._state = state;
}
- private static CalculateDependencies(alreadyLoaded: LayerConfigJson[], allKnownLayers: Map, themeId: string): LayerConfigJson[] {
- const dependenciesToAdd: LayerConfigJson[] = []
+ private static CalculateDependencies(alreadyLoaded: LayerConfigJson[], allKnownLayers: Map, themeId: string):
+ {config: LayerConfigJson, reason: string}[] {
+ const dependenciesToAdd: {config: LayerConfigJson, reason: string}[] = []
const loadedLayerIds: Set = new Set(alreadyLoaded.map(l => l.id));
// Verify cross-dependencies
@@ -361,35 +368,39 @@ class AddDependencyLayersToTheme extends DesugaringStep {
}
// During the generate script, builtin layers are verified but not loaded - so we have to add them manually here
- // Their existance is checked elsewhere, so this is fine
+ // Their existence is checked elsewhere, so this is fine
unmetDependencies = dependencies.filter(dep => !loadedLayerIds.has(dep.neededLayer))
for (const unmetDependency of unmetDependencies) {
if (loadedLayerIds.has(unmetDependency.neededLayer)) {
continue
}
- const dep = allKnownLayers.get(unmetDependency.neededLayer)
+ const dep = Utils.Clone(allKnownLayers.get(unmetDependency.neededLayer))
+ const reason = "This layer is needed by " + unmetDependency.neededBy +" because " +
+ unmetDependency.reason + " (at " + unmetDependency.context + ")";
if (dep === undefined) {
const message =
["Loading a dependency failed: layer " + unmetDependency.neededLayer + " is not found, neither as layer of " + themeId + " nor as builtin layer.",
- "This layer is needed by " + unmetDependency.neededBy,
- unmetDependency.reason + " (at " + unmetDependency.context + ")",
+ reason,
"Loaded layers are: " + alreadyLoaded.map(l => l.id).join(",")
]
throw message.join("\n\t");
}
- dependenciesToAdd.unshift(dep)
+
+ dep.forceLoad = true;
+ dep.passAllFeatures = true;
+ dep.description = reason;
+ dependenciesToAdd.unshift({
+ config: dep,
+ reason
+ })
loadedLayerIds.add(dep.id);
unmetDependencies = unmetDependencies.filter(d => d.neededLayer !== unmetDependency.neededLayer)
}
} while (unmetDependencies.length > 0)
- return dependenciesToAdd.map(dep => {
- dep = Utils.Clone(dep);
- dep.forceLoad = true
- return dep
- });
+ return dependenciesToAdd
}
convert(theme: LayoutConfigJson, context: string): { result: LayoutConfigJson; information: string[] } {
@@ -404,11 +415,16 @@ class AddDependencyLayersToTheme extends DesugaringStep {
})
const dependencies = AddDependencyLayersToTheme.CalculateDependencies(layers, allKnownLayers, theme.id);
- if (dependencies.length > 0) {
-
- information.push(context + ": added " + dependencies.map(d => d.id).join(", ") + " to the theme as they are needed")
+ for (const dependency of dependencies) {
+
}
- layers.unshift(...dependencies);
+ if (dependencies.length > 0) {
+ for (const dependency of dependencies) {
+ information.push(context + ": added " + dependency.config.id + " to the theme. "+dependency.reason)
+
+ }
+ }
+ layers.unshift(...dependencies.map(l => l.config));
return {
result: {
diff --git a/Models/ThemeConfig/DependencyCalculator.ts b/Models/ThemeConfig/DependencyCalculator.ts
index c4d3663d3c..895f267792 100644
--- a/Models/ThemeConfig/DependencyCalculator.ts
+++ b/Models/ThemeConfig/DependencyCalculator.ts
@@ -81,7 +81,7 @@ export default class DependencyCalculator {
// The important line: steal the dependencies!
deps.push({
- neededLayer: layerId, reason: "A calculated tag loads features from this layer",
+ neededLayer: layerId, reason: "a calculated tag loads features from this layer",
context: "calculatedTag[" + currentLine + "] which calculates the value for " + currentKey,
neededBy: layer.id
})
diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts
index ade6d6b6df..5d95d3cb2c 100644
--- a/Models/ThemeConfig/LayerConfig.ts
+++ b/Models/ThemeConfig/LayerConfig.ts
@@ -161,7 +161,11 @@ export default class LayerConfig extends WithContextLoader {
this.calculatedTags = [];
for (const kv of json.calculatedTags) {
const index = kv.indexOf("=");
- let key = kv.substring(0, index);
+ let key = kv.substring(0, index).trim();
+ const r = "[a-z_][a-z0-9:]*"
+ if(key.match(r) === null){
+ throw "At "+context+" invalid key for calculated tag: "+key+"; it should match "+r
+ }
const isStrict = key.endsWith(':')
if (isStrict) {
key = key.substr(0, key.length - 1)
diff --git a/UI/BigComponents/FilterView.ts b/UI/BigComponents/FilterView.ts
index e707211a09..092cd31ec1 100644
--- a/UI/BigComponents/FilterView.ts
+++ b/UI/BigComponents/FilterView.ts
@@ -20,6 +20,7 @@ import {QueryParameters} from "../../Logic/Web/QueryParameters";
import {TagUtils} from "../../Logic/Tags/TagUtils";
import {InputElement} from "../Input/InputElement";
import {DropDown} from "../Input/DropDown";
+import {UIElement} from "../UIElement";
export default class FilterView extends VariableUiElement {
constructor(filteredLayer: UIEventSource,
@@ -33,7 +34,7 @@ export default class FilterView extends VariableUiElement {
filteredLayer.map((filteredLayers) => {
// Create the views which toggle layers (and filters them) ...
let elements = filteredLayers
- ?.map(l => FilterView.createOneFilteredLayerElement(l)?.SetClass("filter-panel"))
+ ?.map(l => FilterView.createOneFilteredLayerElement(l, State.state)?.SetClass("filter-panel"))
?.filter(l => l !== undefined)
elements[0].SetClass("first-filter-panel")
@@ -87,10 +88,14 @@ export default class FilterView extends VariableUiElement {
);
}
- private static createOneFilteredLayerElement(filteredLayer: FilteredLayer) {
+ private static createOneFilteredLayerElement(filteredLayer: FilteredLayer, state: {featureSwitchIsDebugging: UIEventSource}) {
if (filteredLayer.layerDef.name === undefined) {
// Name is not defined: we hide this one
- return undefined;
+ return new Toggle(
+ filteredLayer.layerDef.description.Clone().SetClass("subtle") ,
+ undefined,
+ state.featureSwitchIsDebugging
+ );
}
const iconStyle = "width:1.5rem;height:1.5rem;margin-left:1.25rem;flex-shrink: 0;";
diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts
index 3203553932..d9a802870f 100644
--- a/UI/SpecialVisualizations.ts
+++ b/UI/SpecialVisualizations.ts
@@ -85,6 +85,10 @@ export class AllTagsPanel extends VariableUiElement {
if (value === undefined) {
continue
}
+ let type = "";
+ if(typeof value !== "string"){
+ type = " "+(typeof value)+" "
+ }
parts.push(["" + key + " ", value])
}
diff --git a/assets/layers/climbing/climbing.json b/assets/layers/climbing/climbing.json
index 9c53a74a0a..36cd68b9d3 100644
--- a/assets/layers/climbing/climbing.json
+++ b/assets/layers/climbing/climbing.json
@@ -1,300 +1,375 @@
{
"id": "climbing",
- "name": {
- "nl": "Klimgelegenheden",
- "de": "Klettermöglichkeiten",
- "en": "Climbing opportunities",
- "ja": "登坂教室",
- "fr": "Opportunité d’escalade",
- "it": "Opportunità di arrampicata"
- },
- "minzoom": 10,
- "source": {
- "osmTags": {
- "and": [
- "sport=climbing",
- "climbing!~route",
- "leisure!~sports_centre",
- "climbing!=route_top",
- "climbing!=route_bottom"
- ]
- }
- },
- "title": {
- "render": {
- "en": "Climbing opportunity",
- "nl": "Klimgelegenheid",
- "de": "Klettermöglichkeit",
- "ja": "登坂教室",
- "nb_NO": "Klatremulighet",
- "fr": "Opportunité d’escalade",
- "it": "Opportunità di arrampicata"
- },
- "mappings": [
- {
- "if": "climbing=crag",
- "then": {
- "en": "Climbing crag {name} ",
- "fr": "Mur d’escalade {name} ",
- "it": "Muro da arrampicata {name} ",
- "de": "Klettergarten {name} "
- }
- },
- {
- "if": {
- "and": [
- {
- "or": [
- "climbing=area",
- "climbing=site"
- ]
- },
- "name~*"
- ]
- },
- "then": {
- "en": "Climbing area {name} ",
- "nl": "Klimsite {name} ",
- "fr": "Zone d’escalade {name} ",
- "de": "Klettergebiet {name} ",
- "it": "Area di arrampicata {name} "
- }
- },
- {
- "if": {
- "or": [
- "climbing=site",
- "climbing=area"
- ]
- },
- "then": {
- "en": "Climbing site",
- "nl": "Klimsite",
- "fr": "Site d’escalade",
- "de": "Klettergebiet",
- "it": "Sito di arrampicata",
- "ca": "Llocs d'escalada"
- }
- },
- {
- "if": "name~*",
- "then": {
- "nl": "Klimgelegenheid {name} ",
- "en": "Climbing opportunity {name} ",
- "fr": "Opportunité d’escalade {name} ",
- "de": "Klettermöglichkeit {name} ",
- "it": "Opportunità di arrampicata {name} "
- }
- }
- ]
- },
+ "title": null,
"description": {
- "nl": "Een klimgelegenheid",
- "de": "Eine Klettergelegenheit",
- "en": "A climbing opportunity",
- "ja": "登坂教室",
- "nb_NO": "En klatremulighet",
- "fr": "Opportunité d’escalade",
- "it": "Un’opportunità di arrampicata"
+ "en": "A dummy layer which contains tagrenderings, shared among the climbing layers"
+ },
+ "minzoom": 25,
+ "source": {
+ "osmTags": "sport=climbing"
},
"tagRenderings": [
- "images",
{
- "id": "minimap",
- "render": "{minimap(18, id, _contained_climbing_route_ids): height: 9rem; overflow: hidden; border-radius:3rem; }"
- },
- {
- "render": {
- "en": "Length overview {histogram(_length_hist)}",
- "fr": "Résumé de longueur {histogram(_length_hist)}",
- "de": "Längenübersicht {histogram(_length_hist)}",
- "it": "Riassunto della lunghezza {histogram(_length_hist)}"
+ "id": "website",
+ "question": {
+ "en": "Is there a (unofficial) website with more informations (e.g. topos)?",
+ "de": "Gibt es eine (inoffizielle) Website mit mehr Informationen (z.B. Topos)?",
+ "ja": "もっと情報のある(非公式の)ウェブサイトはありますか(例えば、topos)?",
+ "nl": "Is er een (onofficiële) website met meer informatie (b.v. met topos)?",
+ "ru": "Есть ли (неофициальный) веб-сайт с более подробной информацией (напр., topos)?",
+ "fr": "Existe-t’il un site avec plus d’informations (ex : topographie) ?",
+ "it": "C’è un sito web (anche non ufficiale) con qualche informazione in più (ad es. topografie)?"
},
- "condition": "_length_hist!~\\[\\]",
- "id": "Contained routes length hist"
- },
- {
- "render": {
- "en": "Grades overview {histogram(_difficulty_hist)}",
- "fr": "Résumé des difficultés {histogram(_difficulty_hist)}",
- "de": "Schwierigkeitsübersicht {histogram(_difficulty_hist)}",
- "it": "Riassunto delle difficoltà {histogram(_difficulty_hist)}"
+ "condition": {
+ "and": [
+ "leisure!~sports_centre",
+ "sport=climbing",
+ "office=",
+ "club="
+ ]
},
- "condition": "_difficulty_hist!~\\[\\]",
- "id": "Contained routes hist"
+ "render": "{url} ",
+ "freeform": {
+ "key": "url",
+ "type": "url"
+ }
},
{
+ "id": "average_length",
"render": {
- "en": "Contains {_contained_climbing_routes_count} routes {_contained_climbing_routes} ",
- "fr": "Contient {_contained_climbing_routes_count} voies {_contained_climbing_routes} ",
- "it": "Contiene {_contained_climbing_routes_count} vie {_contained_climbing_routes} ",
- "de": " Enthält {_contained_climbing_routes_count} Routen {_contained_climbing_routes} "
- },
- "condition": "_contained_climbing_routes~*",
- "id": "Contained_climbing_routes"
- },
- {
- "render": {
- "en": "{name} ",
- "nl": "{name} ",
- "de": "{name} ",
- "ca": "{name} ",
- "fr": "{name} ",
- "id": "{name} ",
- "ru": "{name} ",
- "ja": "{name} ",
- "it": "{name} "
+ "de": "Die Routen sind durchschnittlich {canonical(climbing:length)} lang",
+ "en": "The routes are {canonical(climbing:length)} long on average",
+ "nl": "De klimroutes zijn gemiddeld {canonical(climbing:length)} lang",
+ "ja": "ルートの長さは平均で{canonical(climbing:length)} です",
+ "fr": "Les voies font {canonical(climbing:length)} de long en moyenne",
+ "it": "Le vie sono lunghe mediamente {canonical(climbing:length)} "
},
"question": {
- "en": "What is the name of this climbing opportunity?",
- "nl": "Wat is de naam van dit Klimgelegenheid?",
- "de": "Wie heißt diese Klettergelegenheit?",
- "ja": "この登坂教室の名前は何ですか?",
- "fr": "Quel est le nom de ce site ?",
- "it": "Qual è il nome di questa opportunità di arrampicata?"
+ "de": "Wie lang sind die Routen (durchschnittlich) in Metern?",
+ "en": "What is the (average) length of the routes in meters?",
+ "nl": "Wat is de (gemiddelde) lengte van de klimroutes, in meter?",
+ "ja": "ルートの(平均)長さはメートル単位でいくつですか?",
+ "fr": "Quelle est la longueur moyenne des voies en mètres ?",
+ "it": "Quale è la lunghezza (media) delle vie in metri?"
},
"freeform": {
- "key": "name"
+ "key": "climbing:length",
+ "type": "pfloat"
+ }
+ },
+ {
+ "id": "min_difficulty",
+ "question": {
+ "de": "Welche Schwierigkeit hat hier die leichteste Route (französisch/belgisches System)?",
+ "en": "What is the grade of the easiest route here, according to the french classification system?",
+ "nl": "Wat is het niveau van de makkelijkste route, volgens het Franse classificatiesysteem?",
+ "ja": "ここで一番簡単なルートのレベルは、フランスのランク評価システムで何ですか?",
+ "fr": "Quel est le niveau de la voie la plus simple selon la classification franco-belge ?",
+ "it": "Qual è il livello della via più facile qua, secondo il sistema di classificazione francese?"
+ },
+ "render": {
+ "de": "Die leichteste Route hat hier die Schwierigkeit {climbing:grade:french:min} (französisch/belgisches System)",
+ "en": "The lowest grade is {climbing:grade:french:min} according to the french/belgian system",
+ "nl": "De minimale klimmoeilijkheid is {climbing:grade:french:min} volgens het Franse/Belgische systeem",
+ "ja": "フランス/ベルギーのランク評価システムでは、最小の難易度は{climbing:grade:french:min}です",
+ "fr": "La difficulté minimale est {climbing:grade:french:min} selon la classification franco-belge",
+ "it": "Il minimo livello di difficoltà è {climbing:grade:french:min} secondo il sistema francese/belga"
+ },
+ "freeform": {
+ "key": "climbing:grade:french:min"
+ }
+ },
+ {
+ "id": "max_difficulty",
+ "question": {
+ "de": "Welche Schwierigkeit hat hier die schwerste Route (französisch/belgisches System)?",
+ "en": "What is the highest grade route here, according to the french classification system?",
+ "nl": "Wat is het niveau van de moeilijkste route, volgens het Franse classificatiesysteem?",
+ "ja": "フランスのランク評価によると、ここで一番難しいルートのレベルはどれくらいですか?",
+ "fr": "Quel est le niveau de la voie la plus difficile selon la classification franco-belge ?",
+ "it": "Qual è il livello della via più difficile qua, secondo il sistema di classificazione francese?"
+ },
+ "render": {
+ "de": "Die schwierigste Route hat hier die Schwierigkeitsstufe {climbing:grade:french:max} (französisch/belgisches System)",
+ "en": "The highest grade is {climbing:grade:french:max} according to the french/belgian system",
+ "nl": "De maximale klimmoeilijkheid is {climbing:grade:french:max} volgens het Franse/Belgische systeem",
+ "ja": "フランス/ベルギーのランク評価システムでは、最大の難易度は{climbing:grade:french:max}です",
+ "fr": "La difficulté maximale est {climbing:grade:french:max} selon la classification franco-belge",
+ "it": "Il massimo livello di difficoltà è {climbing:grade:french:max} secondo il sistema francese/belga"
+ },
+ "freeform": {
+ "key": "climbing:grade:french:max"
+ },
+ "condition": {
+ "and": [
+ "climbing!~route",
+ "office=",
+ "club=",
+ {
+ "or": [
+ "climbing:sport=yes",
+ "sport=climbing"
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "id": "bouldering",
+ "question": {
+ "de": "Kann hier gebouldert werden?",
+ "en": "Is bouldering possible here?",
+ "nl": "Is het mogelijk om hier te bolderen?",
+ "ja": "ここでボルダリングはできますか?",
+ "nb_NO": "Er buldring mulig her?",
+ "fr": "L’escalade de bloc est-elle possible ici ?",
+ "it": "È possibile praticare ‘bouldering’ qua?"
},
"mappings": [
+ {
+ "if": "climbing:boulder=yes",
+ "then": {
+ "de": "Hier kann gebouldert werden",
+ "en": "Bouldering is possible here",
+ "nl": "Bolderen kan hier",
+ "ja": "ボルダリングはここで可能です",
+ "nb_NO": "Buldring er mulig her",
+ "fr": "L’escalade de bloc est possible",
+ "it": "L’arrampicata su massi è possibile qua"
+ }
+ },
+ {
+ "if": "climbing:boulder=no",
+ "then": {
+ "de": "Hier kann nicht gebouldert werden",
+ "en": "Bouldering is not possible here",
+ "nl": "Bolderen kan hier niet",
+ "ja": "ここではボルダリングはできません",
+ "nb_NO": "Buldring er ikke mulig her",
+ "fr": "L’escalade de bloc n’est pas possible",
+ "it": "L’arrampicata su massi non è possibile qua"
+ }
+ },
+ {
+ "if": "climbing:boulder=limited",
+ "then": {
+ "de": "Bouldern ist hier nur an wenigen Routen möglich",
+ "en": "Bouldering is possible, allthough there are only a few routes",
+ "nl": "Bolderen kan hier, maar er zijn niet zoveel routes",
+ "ja": "ボルダリングは可能ですが、少しのルートしかありません",
+ "fr": "L’escalade de bloc est possible sur des voies précises",
+ "it": "L’arrampicata su massi è possibile anche se su poche vie"
+ }
+ },
+ {
+ "if": "climbing:boulder~*",
+ "then": {
+ "de": "Hier gibt es {climbing:boulder} Boulder-Routen",
+ "en": "There are {climbing:boulder} boulder routes",
+ "nl": "Er zijn hier {climbing:boulder} bolderroutes",
+ "ja": "{climbing:boulder} ボルダールートがある",
+ "fr": "Il y a {climbing:boulder} voies d’escalade de bloc",
+ "it": "Sono presenti {climbing:boulder} vie di arrampicata su massi"
+ },
+ "hideInAnswer": true
+ }
+ ]
+ },
+ {
+ "id": "toprope",
+ "question": {
+ "de": "Ist Toprope-Klettern hier möglich?",
+ "en": "Is toprope climbing possible here?",
+ "nl": "Is het mogelijk om hier te toprope-klimmen?",
+ "ja": "ここでtoprope登坂はできますか?",
+ "fr": "Est-il possible d’escalader à la moulinette ?",
+ "it": "È possibile arrampicarsi con la corda dall’alto qua?"
+ },
+ "mappings": [
+ {
+ "if": "climbing:toprope=yes",
+ "then": {
+ "de": "Toprope-Klettern ist hier möglich",
+ "en": "Toprope climbing is possible here",
+ "nl": "Toprope-klimmen kan hier",
+ "ja": "ここでToprope登坂ができます",
+ "fr": "L’escalade à la moulinette est possible",
+ "it": "È possibile arrampicarsi con moulinette qua"
+ }
+ },
+ {
+ "if": "climbing:toprope=no",
+ "then": {
+ "de": "Toprope-Climbing ist hier nicht möglich",
+ "en": "Toprope climbing is not possible here",
+ "nl": "Toprope-klimmen kan hier niet",
+ "ja": "ここではToprope登坂はできません",
+ "fr": "L’escalade à la moulinette n’est pas possible",
+ "it": "Non è possibile arrampicarsi con moulinette qua"
+ }
+ },
+ {
+ "if": "climbing:toprope~*",
+ "then": {
+ "de": "Hier gibt es {climbing:toprope} Toprope-Routen",
+ "en": "There are {climbing:toprope} toprope routes",
+ "nl": "Er zijn hier {climbing:toprope} toprope routes",
+ "ja": "{climbing:toprope} 登坂ルートがある",
+ "fr": "{climbing:toprope} voies sont équipées de moulinettes",
+ "it": "Sono presenti {climbing:toprope} vie con moulinette"
+ },
+ "hideInAnswer": true
+ }
+ ]
+ },
+ {
+ "id": "sportclimbing",
+ "question": {
+ "de": "Ist hier Sportklettern möglich (feste Ankerpunkte)?",
+ "en": "Is sport climbing possible here on fixed anchors?",
+ "nl": "Is het mogelijk om hier te sportklimmen/voorklimmen op reeds aangebrachte haken?",
+ "ja": "ここでは固定アンカー式のスポーツクライミングはできますか?",
+ "it": "È possibile arrampicarsi qua con ancoraggi fissi?"
+ },
+ "mappings": [
+ {
+ "if": "climbing:sport=yes",
+ "then": {
+ "de": "Sportklettern ist hier möglich",
+ "en": "Sport climbing is possible here",
+ "nl": "Sportklimmen/voorklimmen kan hier",
+ "ru": "Здесь можно заняться спортивным скалолазанием",
+ "ja": "ここでスポーツクライミングができます",
+ "it": "L’arrampicata sportiva è possibile qua",
+ "hu": "Itt lehetőség van sportmászásra",
+ "fr": "De l’escalade est possible ici"
+ }
+ },
+ {
+ "if": "climbing:sport=no",
+ "then": {
+ "de": "Sportklettern ist hier nicht möglich",
+ "en": "Sport climbing is not possible here",
+ "nl": "Sportklimmen/voorklimmen kan hier niet",
+ "ru": "Спортивное скалолазание здесь невозможно",
+ "ja": "ここではスポーツクライミングはできません",
+ "it": "L’arrampicata sportiva non è possibile qua",
+ "hu": "Itt nincs lehetőség sportmászásra",
+ "fr": "L’escalade est impossible ici"
+ }
+ },
+ {
+ "if": "climbing:sport~*",
+ "then": {
+ "de": "Hier gibt es {climbing:sport} Sportkletter-Routen",
+ "en": "There are {climbing:sport} sport climbing routes",
+ "nl": "Er zijn hier {climbing:sport} sportklimroutes/voorklimroutes",
+ "ja": "スポーツクライミングの {climbing:sport} ルートがある",
+ "it": "Sono presenti {climbing:sport} vie di arrampicata sportiva"
+ },
+ "hideInAnswer": true
+ }
+ ]
+ },
+ {
+ "id": "trad_climbing",
+ "question": {
+ "de": "Ist hier traditionelles Klettern möglich (eigene Sicherung z.B. mit Klemmkleilen)?",
+ "en": "Is traditional climbing possible here (using own gear e.g. chocks)?",
+ "nl": "Is het mogelijk om hier traditioneel te klimmen? (Dit is klimmen met klemblokjes en friends) ",
+ "ja": "伝統的な登山はここで可能ですか(例えば、チョックのような独自のギアを使用して)?",
+ "it": "È possibile arrampicarsi in maniera tradizionale qua (usando attrezzi propri, ad es. dadi)?"
+ },
+ "mappings": [
+ {
+ "if": "climbing:traditional=yes",
+ "then": {
+ "de": "Traditionelles Klettern ist hier möglich",
+ "en": "Traditional climbing is possible here",
+ "nl": "Traditioneel klimmen kan hier",
+ "ja": "ここでは伝統的な登山が可能です",
+ "it": "L’arrampicata tradizionale è possibile qua"
+ }
+ },
+ {
+ "if": "climbing:traditional=no",
+ "then": {
+ "de": "Traditionelles Klettern ist hier nicht möglich",
+ "en": "Traditional climbing is not possible here",
+ "nl": "Traditioneel klimmen kan hier niet",
+ "ja": "伝統的な登山はここではできない",
+ "it": "L’arrampicata tradizionale non è possibile qua"
+ }
+ },
+ {
+ "if": "climbing:traditional~*",
+ "then": {
+ "de": "Hier gibt es {climbing:traditional} Routen für traditionelles Klettern",
+ "en": "There are {climbing:traditional} traditional climbing routes",
+ "nl": "Er zijn hier {climbing:traditional} traditionele klimroutes",
+ "ja": "{climbing:traditional} の伝統的な登山ルートがある",
+ "it": "Sono presenti {climbing:traditional} vie di arrampicata tradizionale"
+ },
+ "hideInAnswer": true
+ }
+ ]
+ },
+ {
+ "id": "max_bolts",
+ "question": {
+ "en": "How many bolts do routes in {title()} have at most?"
+ },
+ "render": {
+ "en": "The sport climbing routes here have at most {climbing:bolts:max} bolts.This is without relays and indicates how much quickdraws a climber needs
"
+ },
+ "freeform": {
+ "key": "climbing:bolts:max",
+ "type": "pnat",
+ "addExtraTag": [
+ "climbing:sport=yes"
+ ],
+ "inline": true
+ }
+ },
+ {
+ "id": "fee",
+ "question": {
+ "en": "Is a fee required to climb here?"
+ },
+ "render": {
+ "en": "A fee of {charge} should be paid for climbing here"
+ },
+ "freeform": {
+ "key": "charge",
+ "addExtraTags": [
+ "fee=yes"
+ ],
+ "inline": true
+ },
+ "mappings": [
+ {
+ "if": "fee=no",
+ "addExtraTags": [
+ "charge="
+ ],
+ "then": {
+ "en": "Climbing here is free of charge"
+ }
+ },
{
"if": {
"and": [
- "noname=yes",
- "name="
+ "fee=yes",
+ "charge="
]
},
"then": {
- "en": "This climbing opportunity doesn't have a name",
- "nl": "Dit Klimgelegenheid heeft geen naam",
- "de": "Diese Klettergelegenheit hat keinen Namen",
- "ja": "この登坂教室には名前がついていない",
- "fr": "Ce site n’a pas de nom",
- "it": "Questa opportunità di arrampicata non ha un nome"
- }
+ "en": "Paying a fee is required to climb here"
+ },
+ "hideInAnswer": "charge~*"
}
- ],
- "id": "name"
- },
- {
- "question": "What kind of climbing opportunity is this?",
- "mappings": [
- {
- "if": "climbing=boulder",
- "then": {
- "en": "A climbing boulder - a single rock or cliff with one or a few climbing routes which can be climbed safely without rope",
- "fr": "Rocher d’escalade, rocher avec une ou peu de voie permettant d’escalader sans corde",
- "de": "Ein Kletterfelsen - ein einzelner Felsen oder eine Klippe mit einer oder wenigen Kletterrouten, die ohne Seil sicher bestiegen werden können",
- "it": "Un masso per arrampicata (una singola roccia o falesia con una o poche vie di arrampicata che possono essere scalate in sicurezza senza una corda)"
- }
- },
- {
- "if": "climbing=crag",
- "then": {
- "en": "A climbing crag - a single rock or cliff with at least a few climbing routes",
- "fr": "Mur d’escalade, rocher avec plusieurs voies d’escalades",
- "it": "Un muro da arrampicata (un singolo masso o falesia con almeno qualche via per arrampicata)",
- "de": "Ein Kletterfelsen - ein einzelner Fels oder eine Klippe mit mindestens einigen Kletterrouten"
- }
- },
- {
- "if": "climbing=area",
- "then": "A climbing area with one or more climbing crags and/or boulders"
- }
- ],
- "id": "Type"
- },
- {
- "question": {
- "en": "What is the rock type here?",
- "fr": "Quel est le type de roche ?",
- "de": "Welchen Gesteinstyp gibt es hier?",
- "it": "Qual è il tipo di roccia qua?"
- },
- "render": {
- "en": "The rock type is {rock}",
- "fr": "La roche est du {rock}",
- "de": "Der Gesteinstyp ist {rock}",
- "it": "Il tipo di roccia è {rock}"
- },
- "freeform": {
- "key": "rock"
- },
- "mappings": [
- {
- "if": "rock=limestone",
- "then": {
- "en": "Limestone",
- "nl": "Kalksteen",
- "fr": "Calcaire",
- "de": "Kalkstein",
- "it": "Calcare"
- }
- }
- ],
- "condition": {
- "or": [
- "climbing=crag",
- "natural=cliff",
- "natural=bare_rock"
- ]
- },
- "id": "Rock type (crag/rock/cliff only)"
- }
- ],
- "presets": [
- {
- "tags": [
- "sport=climbing"
- ],
- "title": {
- "en": "a climbing opportunity",
- "nl": "een klimgelegenheid",
- "de": "eine klettermöglichkeit",
- "ja": "登坂教室",
- "nb_NO": "en klatremulighet",
- "fr": "une opportunité d’escalade",
- "it": "una opportunità di arrampicata"
- },
- "description": {
- "nl": "Een klimgelegenheid",
- "de": "Eine Klettergelegenheit",
- "en": "A climbing opportunity",
- "ja": "登坂教室",
- "nb_NO": "En klatremulighet",
- "fr": "Opportunité d’escalade",
- "it": "Un’opportunità di arrampicata"
- }
- }
- ],
- "calculatedTags": [
- "_contained_climbing_routes_properties=feat.overlapWith('climbing_route').map(f => f.feat.properties).map(p => {return {id: p.id, name: p.name, 'climbing:grade:french': p['climbing:grade:french'], 'climbing:length': p['climbing:length']} })",
- "_contained_climbing_routes=JSON.parse(feat.properties._contained_climbing_routes_properties ?? '[]').map(p => `${p.name ?? 'climbing route'} (${p['climbing:grade:french'] ?? 'unknown difficulty'} , ${p['climbing:length'] ?? 'unkown length'} meter) `).join('')",
- "_contained_climbing_route_ids=JSON.parse(feat.properties._contained_climbing_routes_properties ?? '[]').map(p => p.id)",
- "_difficulty_hist=JSON.parse(feat.properties._contained_climbing_routes_properties ?? '[]').map(p => p['climbing:grade:french'])",
- "_length_hist=JSON.parse(feat.properties._contained_climbing_routes_properties ?? '[]').map(p => p['climbing:length'])",
- "_contained_climbing_routes_count=JSON.parse(feat.properties._contained_climbing_routes_properties ?? '[]').length"
- ],
- "mapRendering": [
- {
- "icon": {
- "render": "./assets/themes/climbing/climbing_no_rope.svg"
- },
- "iconSize": {
- "render": "40,40,center"
- },
- "location": [
- "point",
- "centroid"
]
- },
- {
- "color": {
- "render": "#d38d5fAA"
- },
- "width": {
- "render": "8"
- }
}
- ]
+ ],
+ "mapRendering": null
}
\ No newline at end of file
diff --git a/assets/layers/climbing_area/climbing_area.json b/assets/layers/climbing_area/climbing_area.json
new file mode 100644
index 0000000000..22c0d28844
--- /dev/null
+++ b/assets/layers/climbing_area/climbing_area.json
@@ -0,0 +1,308 @@
+{
+ "id": "climbing_area",
+ "name": {
+ "nl": "Klimgelegenheden",
+ "de": "Klettermöglichkeiten",
+ "en": "Climbing opportunities",
+ "ja": "登坂教室",
+ "fr": "Opportunité d’escalade",
+ "it": "Opportunità di arrampicata"
+ },
+ "description": {
+ "en": "An area where climbing is possible, e.g. a crag, site, boulder, ... Contains aggregation of routes"
+ },
+ "minzoom": 10,
+ "source": {
+ "osmTags": {
+ "and": [
+ "sport=climbing",
+ "climbing!~route",
+ "leisure!~sports_centre",
+ "climbing!=route_top",
+ "climbing!=route_bottom"
+ ]
+ }
+ },
+ "title": {
+ "render": {
+ "en": "Climbing opportunity",
+ "nl": "Klimgelegenheid",
+ "de": "Klettermöglichkeit",
+ "ja": "登坂教室",
+ "nb_NO": "Klatremulighet",
+ "fr": "Opportunité d’escalade",
+ "it": "Opportunità di arrampicata"
+ },
+ "mappings": [
+ {
+ "if": "climbing=crag",
+ "then": {
+ "en": "Climbing crag {name} ",
+ "fr": "Mur d’escalade {name} ",
+ "it": "Muro da arrampicata {name} ",
+ "de": "Klettergarten {name} "
+ }
+ },
+ {
+ "if": {
+ "and": [
+ {
+ "or": [
+ "climbing=area",
+ "climbing=site"
+ ]
+ },
+ "name~*"
+ ]
+ },
+ "then": {
+ "en": "Climbing area {name} ",
+ "nl": "Klimsite {name} ",
+ "fr": "Zone d’escalade {name} ",
+ "de": "Klettergebiet {name} ",
+ "it": "Area di arrampicata {name} "
+ }
+ },
+ {
+ "if": {
+ "or": [
+ "climbing=site",
+ "climbing=area"
+ ]
+ },
+ "then": {
+ "en": "Climbing site",
+ "nl": "Klimsite",
+ "fr": "Site d’escalade",
+ "de": "Klettergebiet",
+ "it": "Sito di arrampicata",
+ "ca": "Llocs d'escalada"
+ }
+ },
+ {
+ "if": "name~*",
+ "then": {
+ "nl": "Klimgelegenheid {name} ",
+ "en": "Climbing opportunity {name} ",
+ "fr": "Opportunité d’escalade {name} ",
+ "de": "Klettermöglichkeit {name} ",
+ "it": "Opportunità di arrampicata {name} "
+ }
+ }
+ ]
+ },
+ "calculatedTags": [
+ "_contained_climbing_routes_properties=feat.overlapWith('climbing_route').map(f => f.feat.properties).map(p => {return {id: p.id, name: p.name, 'climbing:grade:french': p['climbing:grade:french'], 'climbing:length': p['climbing:length']} })",
+ "_contained_climbing_routes=feat.get('_contained_climbing_routes_properties')?.map(p => `${p.name ?? 'climbing route'} (${p['climbing:grade:french'] ?? 'unknown difficulty'} , ${p['climbing:length'] ?? 'unkown length'} meter) `).join('')",
+ "_contained_climbing_route_ids=feat.get('_contained_climbing_routes_properties')?.map(p => p.id)",
+ "_difficulty_hist=feat.get('_contained_climbing_routes_properties')?.map(p => p['climbing:grade:french'])?.filter(p => (p ?? null) !== null)?.sort()",
+ "_difficulty_max=feat.get('_difficulty_hist')?.at(-1)",
+ "_difficulty_min=feat.get('_difficulty_hist')?.at(0)",
+ "_length_hist=feat.get('_contained_climbing_routes_properties')?.map(p => p['climbing:length'])?.filter(p => (p ?? null) !== null)?.sort()",
+ "_length_max=feat.get('_length_hist')?.at(-1)",
+ "_length_min=feat.get('_length_hist')?.at(0)",
+ "_bolts_hist=feat.get('_contained_climbing_routes_properties')?.map(p => p['climbing:bolts'])?.filter(p => (p ?? null) !== null)?.sort()",
+ "_bolts_max=feat.get('_bolts_hist')?.at(-1)",
+ "_bolts_min=feat.get('_bolts_hist')?.at(0)",
+ "_contained_climbing_routes_count=feat.get('_contained_climbing_routes_properties')?.length"
+ ],
+ "tagRenderings": [
+ "images",
+ {
+ "id": "minimap",
+ "render": "{minimap(18, id, _contained_climbing_route_ids): height: 9rem; overflow: hidden; border-radius:3rem; }"
+ },
+ {
+ "render": {
+ "en": "Length overview {histogram(_length_hist)}",
+ "fr": "Résumé de longueur {histogram(_length_hist)}",
+ "de": "Längenübersicht {histogram(_length_hist)}",
+ "it": "Riassunto della lunghezza {histogram(_length_hist)}"
+ },
+ "condition": "_length_hist!~\\[\\]",
+ "id": "Contained routes length hist"
+ },
+ {
+ "render": {
+ "en": "Grades overview {histogram(_difficulty_hist)}",
+ "fr": "Résumé des difficultés {histogram(_difficulty_hist)}",
+ "de": "Schwierigkeitsübersicht {histogram(_difficulty_hist)}",
+ "it": "Riassunto delle difficoltà {histogram(_difficulty_hist)}"
+ },
+ "condition": "_difficulty_hist!~\\[\\]",
+ "id": "Contained routes hist"
+ },
+ {
+ "render": {
+ "en": "Contains {_contained_climbing_routes_count} routes {_contained_climbing_routes} ",
+ "fr": "Contient {_contained_climbing_routes_count} voies {_contained_climbing_routes} ",
+ "it": "Contiene {_contained_climbing_routes_count} vie {_contained_climbing_routes} ",
+ "de": " Enthält {_contained_climbing_routes_count} Routen {_contained_climbing_routes} "
+ },
+ "condition": "_contained_climbing_routes~*",
+ "id": "Contained_climbing_routes"
+ },
+ {
+ "render": {
+ "en": "{name} ",
+ "nl": "{name} ",
+ "de": "{name} ",
+ "ca": "{name} ",
+ "fr": "{name} ",
+ "id": "{name} ",
+ "ru": "{name} ",
+ "ja": "{name} ",
+ "it": "{name} "
+ },
+ "question": {
+ "en": "What is the name of this climbing opportunity?",
+ "nl": "Wat is de naam van dit Klimgelegenheid?",
+ "de": "Wie heißt diese Klettergelegenheit?",
+ "ja": "この登坂教室の名前は何ですか?",
+ "fr": "Quel est le nom de ce site ?",
+ "it": "Qual è il nome di questa opportunità di arrampicata?"
+ },
+ "freeform": {
+ "key": "name"
+ },
+ "mappings": [
+ {
+ "if": {
+ "and": [
+ "noname=yes",
+ "name="
+ ]
+ },
+ "then": {
+ "en": "This climbing opportunity doesn't have a name",
+ "nl": "Dit Klimgelegenheid heeft geen naam",
+ "de": "Diese Klettergelegenheit hat keinen Namen",
+ "ja": "この登坂教室には名前がついていない",
+ "fr": "Ce site n’a pas de nom",
+ "it": "Questa opportunità di arrampicata non ha un nome"
+ }
+ }
+ ],
+ "id": "name"
+ },
+ {
+ "question": "What kind of climbing opportunity is this?",
+ "mappings": [
+ {
+ "if": "climbing=boulder",
+ "then": {
+ "en": "A climbing boulder - a single rock or cliff with one or a few climbing routes which can be climbed safely without rope",
+ "fr": "Rocher d’escalade, rocher avec une ou peu de voie permettant d’escalader sans corde",
+ "de": "Ein Kletterfelsen - ein einzelner Felsen oder eine Klippe mit einer oder wenigen Kletterrouten, die ohne Seil sicher bestiegen werden können",
+ "it": "Un masso per arrampicata (una singola roccia o falesia con una o poche vie di arrampicata che possono essere scalate in sicurezza senza una corda)"
+ }
+ },
+ {
+ "if": "climbing=crag",
+ "then": {
+ "en": "A climbing crag - a single rock or cliff with at least a few climbing routes",
+ "fr": "Mur d’escalade, rocher avec plusieurs voies d’escalades",
+ "it": "Un muro da arrampicata (un singolo masso o falesia con almeno qualche via per arrampicata)",
+ "de": "Ein Kletterfelsen - ein einzelner Fels oder eine Klippe mit mindestens einigen Kletterrouten"
+ }
+ },
+ {
+ "if": "climbing=area",
+ "then": "A climbing area with one or more climbing crags and/or boulders"
+ }
+ ],
+ "id": "Type"
+ },
+ {
+ "question": {
+ "en": "What is the rock type here?",
+ "fr": "Quel est le type de roche ?",
+ "de": "Welchen Gesteinstyp gibt es hier?",
+ "it": "Qual è il tipo di roccia qua?"
+ },
+ "render": {
+ "en": "The rock type is {rock}",
+ "fr": "La roche est du {rock}",
+ "de": "Der Gesteinstyp ist {rock}",
+ "it": "Il tipo di roccia è {rock}"
+ },
+ "freeform": {
+ "key": "rock"
+ },
+ "mappings": [
+ {
+ "if": "rock=limestone",
+ "then": {
+ "en": "Limestone",
+ "nl": "Kalksteen",
+ "fr": "Calcaire",
+ "de": "Kalkstein",
+ "it": "Calcare"
+ }
+ }
+ ],
+ "condition": {
+ "or": [
+ "climbing=crag",
+ "natural=cliff",
+ "natural=bare_rock"
+ ]
+ },
+ "id": "Rock type (crag/rock/cliff only)"
+ },
+ {
+ "builtin": [
+ "climbing.website",
+ "climbing.fee",
+ "climbing.bouldering"
+ ]
+ }
+ ],
+ "presets": [
+ {
+ "tags": [
+ "sport=climbing"
+ ],
+ "title": {
+ "en": "a climbing opportunity",
+ "nl": "een klimgelegenheid",
+ "de": "eine klettermöglichkeit",
+ "ja": "登坂教室",
+ "nb_NO": "en klatremulighet",
+ "fr": "une opportunité d’escalade",
+ "it": "una opportunità di arrampicata"
+ },
+ "description": {
+ "nl": "Een klimgelegenheid",
+ "de": "Eine Klettergelegenheit",
+ "en": "A climbing opportunity",
+ "ja": "登坂教室",
+ "nb_NO": "En klatremulighet",
+ "fr": "Opportunité d’escalade",
+ "it": "Un’opportunità di arrampicata"
+ }
+ }
+ ],
+ "mapRendering": [
+ {
+ "icon": {
+ "render": "./assets/themes/climbing/climbing_no_rope.svg"
+ },
+ "iconSize": {
+ "render": "40,40,center"
+ },
+ "location": [
+ "point",
+ "centroid"
+ ]
+ },
+ {
+ "color": {
+ "render": "#d38d5fAA"
+ },
+ "width": {
+ "render": "8"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/assets/layers/climbing_gym/climbing_gym.json b/assets/layers/climbing_gym/climbing_gym.json
index d93037ae67..afa06676b0 100644
--- a/assets/layers/climbing_gym/climbing_gym.json
+++ b/assets/layers/climbing_gym/climbing_gym.json
@@ -55,15 +55,7 @@
"images",
{
"render": {
- "en": "{name} ",
- "nl": "{name} ",
- "de": "{name} ",
- "ca": "{name} ",
- "fr": "{name} ",
- "id": "{name} ",
- "ru": "{name} ",
- "ja": "{name} ",
- "it": "{name} "
+ "*": "{name} "
},
"question": {
"en": "What is the name of this climbing gym?",
@@ -81,7 +73,65 @@
"website",
"phone",
"email",
- "opening_hours"
+ {
+ "builtin": ["climbing.fee"]
+ },
+ "opening_hours",
+ {
+ "builtin":
+ ["climbing.average_length","climbing.min_difficulty","climbing.max_difficulty",
+ "climbing.bouldering",
+ "climbing.sportclimbing"]
+ },
+ {
+ "builtin": "climbing.max_bolts",
+ "override": {
+ "condition": "climbing:sport=yes"
+ }
+ },
+ {
+ "id": "Speed climbing?",
+ "question": {
+ "de": "Gibt es hier eine Speedkletter-Wand?",
+ "en": "Is there a speed climbing wall?",
+ "nl": "Is er een snelklimmuur (speed climbing)?",
+ "ja": "スピードクライミングウォールはありますか?",
+ "it": "È presente una prete per l’arrampicata di velocità?"
+ },
+ "mappings": [
+ {
+ "if": "climbing:speed=yes",
+ "then": {
+ "de": "Hier gibt es eine Speedkletter-Wand",
+ "en": "There is a speed climbing wall",
+ "nl": "Er is een snelklimmuur voor speed climbing",
+ "ja": "スピードクライミングウォールがある",
+ "it": "È presente una parete per l’arrampicata di velocità"
+ }
+ },
+ {
+ "if": "climbing:speed=no",
+ "then": {
+ "de": "Hier gibt es keine Speedkletter-Wand",
+ "en": "There is no speed climbing wall",
+ "nl": "Er is geen snelklimmuur voor speed climbing",
+ "ja": "スピードクライミングウォールがない",
+ "it": "Non è presente una parete per l’arrampicata di velocità"
+ }
+ },
+ {
+ "if": "climbing:speed~*",
+ "then": {
+ "de": "Hier gibt es {climbing:speed} Speedkletter-Routen",
+ "en": "There are {climbing:speed} speed climbing walls",
+ "nl": "Er zijn hier {climbing:speed} snelklimmuren",
+ "ja": "{climbing:speed} のスピードクライミングウォールがある",
+ "it": "Sono presenti {climbing:speed} pareti per l’arrampicata di velocità"
+ },
+ "hideInAnswer": true
+ }
+ ]
+ }
],
"mapRendering": [
{
diff --git a/assets/layers/climbing_opportunity/climbing_opportunity.json b/assets/layers/climbing_opportunity/climbing_opportunity.json
index c9afa69f06..3a5e851064 100644
--- a/assets/layers/climbing_opportunity/climbing_opportunity.json
+++ b/assets/layers/climbing_opportunity/climbing_opportunity.json
@@ -9,6 +9,9 @@
"fr": "Opportunités d’escalade ?",
"it": "Opportunità di arrampicata?"
},
+ "description": {
+ "en": "Fallback layer with items on which climbing _might_ be possible. It is loaded when zoomed in a lot, to prevent duplicate items to be added"
+ },
"minzoom": 19,
"source": {
"osmTags": {
@@ -38,15 +41,6 @@
"it": "Opportunità di arrampicata?"
}
},
- "description": {
- "nl": "Een klimgelegenheid?",
- "de": "Eine Klettergelegenheit?",
- "en": "A climbing opportunity?",
- "ja": "登坂教室?",
- "nb_NO": "En klatremulighet?",
- "fr": "Opportunité d’escalade ?",
- "it": "Un’opportunità di arrampicata?"
- },
"tagRenderings": [
{
"id": "climbing-opportunity-name",
diff --git a/assets/layers/climbing_route/climbing_route.json b/assets/layers/climbing_route/climbing_route.json
index faba960320..a562c753c1 100644
--- a/assets/layers/climbing_route/climbing_route.json
+++ b/assets/layers/climbing_route/climbing_route.json
@@ -9,6 +9,9 @@
"fr": "Voies d’escalade",
"it": "Vie di arrampicata"
},
+ "description": {
+ "en": "A single climbing route and its properties. Some properties are derived from the containing features"
+ },
"minzoom": 18,
"source": {
"osmTags": {
@@ -135,6 +138,7 @@
"id": "Difficulty"
},
{
+ "id": "bolts",
"question": {
"en": "How many bolts does this route have before reaching the anchor?",
"fr": "Combien de prises cette voie possède avant d’atteindre la moulinette ?",
@@ -142,7 +146,7 @@
"it": "Quanti bulloni sono presenti in questo percorso prima di arrivare alla moulinette?"
},
"render": {
- "en": "This route has {climbing:bolts} bolts",
+ "en": "This route has {climbing:bolts} bolts This is without relays and indicates how much quickdraws a climber needs
",
"fr": "Cette voie a {climbing:bolts} prises",
"de": "Diese Kletterroute hat {climbing:bolts} Haken",
"it": "Questo percorso ha {climbing:bolts} bulloni"
@@ -152,7 +156,8 @@
"type": "pnat",
"addExtraTag": [
"climbing:bolted=yes"
- ]
+ ],
+ "inline": true
},
"mappings": [
{
@@ -163,29 +168,15 @@
"de": "Auf dieser Kletterroute sind keine Haken vorhanden",
"it": "In questo percorso non sono presenti bulloni"
},
- "hideInAnswer": true
- },
- {
- "if": "climbing:bolted=no&climbing:bolts=",
- "then": {
- "en": "This route is not bolted",
- "fr": "Cette voie n’a pas de prises",
- "de": "Auf dieser Kletterroute sind keine Haken vorhanden",
- "it": "In questo percorso non sono presenti bulloni"
- }
+ "addExtraTags": [
+ "climbing:bolts="
+ ]
}
- ],
- "id": "Bolts"
- },
- {
- "question": "Is there other relevant info?",
- "render": "Description {description}",
- "freeform": {
- "key": "description"
- },
- "id": "Description"
+ ]
},
+ "description",
{
+ "id": "Rock type via embedded feature",
"render": {
"en": "The rock type is {_embedding_features_with_rock:rock} as stated on the surrounding crag ",
"fr": "Le type de roche est {_embedding_features_with_rock:rock} selon le mur ",
@@ -194,8 +185,7 @@
},
"freeform": {
"key": "_embedding_features_with_rock:rock"
- },
- "id": "Rock type"
+ }
}
],
"presets": [
diff --git a/assets/themes/climbing/climbing.json b/assets/themes/climbing/climbing.json
index 3723d6b518..043485b54d 100644
--- a/assets/themes/climbing/climbing.json
+++ b/assets/themes/climbing/climbing.json
@@ -43,688 +43,307 @@
"startZoom": 1,
"widenFactor": 1.5,
"layers": [
+ {
+ "builtin": [
"climbing_club",
"climbing_gym",
"climbing_route",
- "climbing",
+ "climbing_area",
"climbing_opportunity"
- ],
- "overrideAll": {
- "allowMove": {
- "enableRelocation": false,
- "enableImproveAccuracy": true
- },
- "+titleIcons": [
- {
- "render": "{climbing:length}m
",
- "condition": "climbing:length~*"
- },
- {
- "mappings": [
+ ],
+ "override": {
+ "allowMove": {
+ "enableRelocation": false,
+ "enableImproveAccuracy": true
+ },
+ "+titleIcons": [
{
- "if": "climbing:bolts~*",
- "then": "{climbing:bolts}
"
+ "render": "{climbing:length}m
",
+ "condition": "climbing:length~*"
},
{
- "if": "climbing:bolted=yes",
- "then": " "
+ "mappings": [
+ {
+ "if": "__bolts_max~*",
+ "then": "{__bolts_max}
"
+ },
+ {
+ "if": "climbing:bolted=yes",
+ "then": " "
+ }
+ ]
+ },
+ {
+ "id": "Min difficulty",
+ "condition": "__difficulty_min~*",
+ "then": " {__difficulty_min}
",
+
+ "mappings": [
+ {
+ "if": "__difficulty_min~(2|3).*",
+ "then": " {__difficulty_min}
"
+ },
+ {
+ "if": "__difficulty_min~4.*",
+ "then": " {__difficulty_min}
"
+ },
+ {
+ "if": "__difficulty_min~5.*",
+ "then": " {__difficulty_min}
"
+ },
+ {
+ "if": "__difficulty_min~6.*",
+ "then": " {__difficulty_min}
"
+ },
+ {
+ "if": "__difficulty_min~(7|8).*",
+ "then": " {__difficulty_min}
"
+ },
+ {
+ "if": "__difficulty_min~*",
+ "then": " {__difficulty_min}
"
+ }
+ ]
+ },
+ {
+ "id": "max difficulty",
+ "condition": "__difficulty_max~*",
+ "then": " {__difficulty_max}
",
+ "mappings": [
+ {
+ "if": "__difficulty_max~(2|3).*",
+ "then": " {__difficulty_max}
"
+ },
+ {
+ "if": "__difficulty_max~4.*",
+ "then": " {__difficulty_max}
"
+ },
+ {
+ "if": "__difficulty_max~5.*",
+ "then": " {__difficulty_max}
"
+ },
+ {
+ "if": "__difficulty_max~6.*",
+ "then": " {__difficulty_max}
"
+ },
+ {
+ "if": "__difficulty_max~(7|8).*",
+ "then": " {__difficulty_max}
"
+ },
+ {
+ "if": "__difficulty_max~*",
+ "then": " {__difficulty_max}
"
+ }
+ ]
+ },
+ {
+ "mappings": [
+ {
+ "if": "climbing:grade:french~3.*",
+ "then": " {climbing:grade:french}
"
+ },
+ {
+ "if": "climbing:grade:french~4.*",
+ "then": " {climbing:grade:french}
"
+ },
+ {
+ "if": "climbing:grade:french~5.*",
+ "then": " {climbing:grade:french}
"
+ },
+ {
+ "if": "climbing:grade:french~6.*",
+ "then": " {climbing:grade:french}
"
+ },
+ {
+ "if": "climbing:grade:french~7.*",
+ "then": " {climbing:grade:french}
"
+ },
+ {
+ "if": "climbing:grade:french~*",
+ "then": " {climbing:grade:french}
"
+ }
+ ]
}
- ]
- },
- {
- "mappings": [
- {
- "if": "climbing:grade:french~3.*",
- "then": " {climbing:grade:french}
"
- },
- {
- "if": "climbing:grade:french~4.*",
- "then": " {climbing:grade:french}
"
- },
- {
- "if": "climbing:grade:french~5.*",
- "then": " {climbing:grade:french}
"
- },
- {
- "if": "climbing:grade:french~6.*",
- "then": " {climbing:grade:french}
"
- },
- {
- "if": "climbing:grade:french~7.*",
- "then": " {climbing:grade:french}
"
- },
- {
- "if": "climbing:grade:french~*",
- "then": " {climbing:grade:french}
"
- }
- ]
- }
- ],
- "+calculatedTags": [
- "_embedding_feature_properties=feat.overlapWith('climbing').map(f => f.feat.properties).filter(p => p !== undefined).map(p => {return{access: p.access, id: p.id, name: p.name, climbing: p.climbing, 'access:description': p['access:description']}})",
- "_embedding_features_with_access=JSON.parse(feat.properties._embedding_feature_properties ?? '[]').filter(p => p.access !== undefined)[0]",
- "_embedding_feature_with_rock=JSON.parse(feat.properties._embedding_feature_properties ?? '[]').filter(p => p.rock !== undefined)[0] ?? '{}'",
- "_embedding_features_with_rock:rock=JSON.parse(feat.properties._embedding_feature_with_rock ?? '{}')?.rock",
- "_embedding_features_with_rock:id=JSON.parse(feat.properties._embedding_feature_with_rock ?? '{}')?.id",
- "_embedding_feature:access=JSON.parse(feat.properties._embedding_features_with_access ?? '{}').access",
- "_embedding_feature:access:description=JSON.parse(feat.properties._embedding_features_with_access ?? '{}')['access:description']",
- "_embedding_feature:id=JSON.parse(feat.properties._embedding_features_with_access ?? '{}').id"
- ],
- "units+": [
- {
- "appliesToKey": [
- "climbing:length",
- "climbing:length:min",
- "climbing:length:max"
],
- "applicableUnits": [
+ "+calculatedTags": [
+ "_embedding_feature_properties=feat.overlapWith('climbing_area').map(f => f.feat.properties).filter(p => p !== undefined).map(p => {return{access: p.access, id: p.id, name: p.name, climbing: p.climbing, 'access:description': p['access:description']}})",
+ "_embedding_features_with_access=feat.get('_embedding_feature_properties')?.filter(p => p.access !== undefined)?.at(0)",
+ "_embedding_feature_with_rock=feat.get('_embedding_feature_properties')?.filter(p => p.rock !== undefined)?.at(0)",
+ "_embedding_features_with_rock:rock=feat.get('_embedding_feature_with_rock')?.rock",
+ "_embedding_features_with_rock:id=feat.get('_embedding_feature_with_rock')?.id",
+ "_embedding_feature:access=feat.get('_embedding_features_with_access')?.access",
+ "_embedding_feature:access:description=(feat.get('_embedding_features_with_access')??{})['access:description']",
+ "_embedding_feature:id=feat.get('_embedding_features_with_access')?.id",
+ "__difficulty_max= feat.properties['climbing:grade:french:max'] ?? feat.properties['_difficulty_max']",
+ "__difficulty_min= feat.properties['climbing:grade:french:min'] ?? feat.properties['_difficulty_min']",
+ "__bolts_max= feat.get('climbing:bolts:max') ?? feat.get('climbing:bolts') ?? feat.get('_bolts_max')"
+ ],
+ "units+": [
{
- "canonicalDenomination": "",
- "alternativeDenomination": [
- "m",
- "meter",
- "meters"
+ "appliesToKey": [
+ "climbing:length",
+ "climbing:length:min",
+ "climbing:length:max"
],
- "human": {
- "en": " meter",
- "nl": " meter",
- "fr": " mètres",
- "de": " Meter",
- "eo": " metro",
- "it": " metri",
- "ru": " метр",
- "ca": " metre"
- },
- "default": true
- },
+ "applicableUnits": [
+ {
+ "canonicalDenomination": "",
+ "alternativeDenomination": [
+ "m",
+ "meter",
+ "meters"
+ ],
+ "human": {
+ "en": " meter",
+ "nl": " meter",
+ "fr": " mètres",
+ "de": " Meter",
+ "eo": " metro",
+ "it": " metri",
+ "ru": " метр",
+ "ca": " metre"
+ },
+ "default": true
+ },
+ {
+ "canonicalDenomination": "ft",
+ "alternativeDenomination": [
+ "feet",
+ "voet"
+ ],
+ "human": {
+ "en": " feet",
+ "nl": " voet",
+ "fr": " pieds",
+ "de": " Fuß",
+ "eo": " futo",
+ "it": " piedi",
+ "ca": " peus"
+ }
+ }
+ ]
+ }
+ ],
+ "tagRenderings+": [
{
- "canonicalDenomination": "ft",
- "alternativeDenomination": [
- "feet",
- "voet"
+ "id": "Access from containing feature",
+ "mappings": [
+ {
+ "if": "_embedding_feature:access=yes",
+ "then": {
+ "en": "The containing feature states that this is publicly accessible {_embedding_feature:access:description}",
+ "nl": "Een omvattend element geeft aan dat dit publiek toegangkelijk is {_embedding_feature:access:description}",
+ "fr": "L’élément englobant indique un accès libre {_embedding_feature:access:description}",
+ "it": "L’ elemento in cui è contenuto indica che è pubblicamente accessibile {_embedding_feature:access:description}",
+ "de": "Das enthaltende Objekt gibt an, dass es öffentlich zugänglich ist {_embedding_feature:access:description}"
+ }
+ },
+ {
+ "if": "_embedding_feature:access=permit",
+ "then": {
+ "en": "The containing feature states that a permit is needed to access {_embedding_feature:access:description}",
+ "nl": "Een omvattend element geeft aan dat een toelating nodig is om hier te klimmen {_embedding_feature:access:description}",
+ "fr": "L’élément englobant indique qu’ une autorisation d’accès est nécessaire {_embedding_feature:access:description}",
+ "it": "L’elemento che lo contiene indica che è richiesto un’autorizzazione per accedervi {_embedding_feature:access:description}",
+ "de": "Das enthaltende Objekt besagt, dass eine Genehmigung erforderlich ist für den Zugang zu {_embedding_feature:access:description}"
+ }
+ },
+ {
+ "if": "_embedding_feature:access=customers",
+ "then": {
+ "en": "The containing feature states that this is only accessible to customers {_embedding_feature:access:description}",
+ "fr": "L’élément englobant indique que l’accès est réservés aux clients {_embedding_feature:access:description}",
+ "it": "L’ elemento che lo contiene indica che è accessibile solo ai clienti {_embedding_feature:access:description}",
+ "de": "Das enthaltende Objekt besagt, dass es nur für Kunden zugänglich ist {_embedding_feature:access:description}"
+ }
+ },
+ {
+ "if": "_embedding_feature:access=members",
+ "then": {
+ "en": "The containing feature states that this is only accessible to club members {_embedding_feature:access:description}",
+ "fr": "L’élément englobant indique que l’accès est réservé aux membres {_embedding_feature:access:description}",
+ "it": "L’ elemento che lo contiene indica che è accessibile solamente ai membri del club {_embedding_feature:access:description}",
+ "de": "Das enthaltende Objekt besagt, dass es nur für Mitglieder zugänglich ist {_embedding_feature:access:description}"
+ }
+ },
+ {
+ "if": "_embedding_feature:access=no",
+ "then": "Not accessible as stated by the containing feature "
+ }
],
- "human": {
- "en": " feet",
- "nl": " voet",
- "fr": " pieds",
- "de": " Fuß",
- "eo": " futo",
- "it": " piedi",
- "ca": " peus"
- }
- }
- ]
- }
- ],
- "tagRenderings+": [
- {
- "id": "Website",
- "question": {
- "en": "Is there a (unofficial) website with more informations (e.g. topos)?",
- "de": "Gibt es eine (inoffizielle) Website mit mehr Informationen (z.B. Topos)?",
- "ja": "もっと情報のある(非公式の)ウェブサイトはありますか(例えば、topos)?",
- "nl": "Is er een (onofficiële) website met meer informatie (b.v. met topos)?",
- "ru": "Есть ли (неофициальный) веб-сайт с более подробной информацией (напр., topos)?",
- "fr": "Existe-t’il un site avec plus d’informations (ex : topographie) ?",
- "it": "C’è un sito web (anche non ufficiale) con qualche informazione in più (ad es. topografie)?"
- },
- "condition": {
- "and": [
- "leisure!~sports_centre",
- "sport=climbing",
- "office=",
- "club="
- ]
- },
- "render": "{url} ",
- "freeform": {
- "key": "url",
- "type": "url"
- }
- },
- {
- "id": "Access from containing feature",
- "mappings": [
- {
- "if": "_embedding_feature:access=yes",
- "then": {
- "en": "The containing feature states that this is publicly accessible {_embedding_feature:access:description}",
- "nl": "Een omvattend element geeft aan dat dit publiek toegangkelijk is {_embedding_feature:access:description}",
- "fr": "L’élément englobant indique un accès libre {_embedding_feature:access:description}",
- "it": "L’ elemento in cui è contenuto indica che è pubblicamente accessibile {_embedding_feature:access:description}",
- "de": "Das enthaltende Objekt gibt an, dass es öffentlich zugänglich ist {_embedding_feature:access:description}"
- }
+ "condition": "_embedding_feature:access~*"
},
{
- "if": "_embedding_feature:access=permit",
- "then": {
- "en": "The containing feature states that a permit is needed to access {_embedding_feature:access:description}",
- "nl": "Een omvattend element geeft aan dat een toelating nodig is om hier te klimmen {_embedding_feature:access:description}",
- "fr": "L’élément englobant indique qu’ une autorisation d’accès est nécessaire {_embedding_feature:access:description}",
- "it": "L’elemento che lo contiene indica che è richiesto un’autorizzazione per accedervi {_embedding_feature:access:description}",
- "de": "Das enthaltende Objekt besagt, dass eine Genehmigung erforderlich ist für den Zugang zu {_embedding_feature:access:description}"
- }
- },
- {
- "if": "_embedding_feature:access=customers",
- "then": {
- "en": "The containing feature states that this is only accessible to customers {_embedding_feature:access:description}",
- "fr": "L’élément englobant indique que l’accès est réservés aux clients {_embedding_feature:access:description}",
- "it": "L’ elemento che lo contiene indica che è accessibile solo ai clienti {_embedding_feature:access:description}",
- "de": "Das enthaltende Objekt besagt, dass es nur für Kunden zugänglich ist {_embedding_feature:access:description}"
- }
- },
- {
- "if": "_embedding_feature:access=members",
- "then": {
- "en": "The containing feature states that this is only accessible to club members {_embedding_feature:access:description}",
- "fr": "L’élément englobant indique que l’accès est réservé aux membres {_embedding_feature:access:description}",
- "it": "L’ elemento che lo contiene indica che è accessibile solamente ai membri del club {_embedding_feature:access:description}",
- "de": "Das enthaltende Objekt besagt, dass es nur für Mitglieder zugänglich ist {_embedding_feature:access:description}"
- }
- },
- {
- "if": "_embedding_feature:access=no",
- "then": "Not accessible as stated by the containing feature "
- }
- ],
- "condition": "_embedding_feature:access~*"
- },
- {
- "id": "Access",
- "question": {
- "en": "Who can access here?",
- "fr": "Qui peut y accéder ?",
- "de": "Wer hat hier Zugang?",
- "it": "Chi può accedervi?"
- },
- "mappings": [
- {
- "if": "access=yes",
- "then": {
- "en": "Publicly accessible to anyone",
- "fr": "Libre d’accès",
- "de": "Öffentlich zugänglich für jedermann",
- "it": "Pubblicamente accessibile a chiunque"
- }
- },
- {
- "if": "access=permit",
- "then": {
- "en": "You need a permit to access here",
- "fr": "Une autorisation est nécessaire",
- "de": "Zugang nur mit Genehmigung",
- "it": "È necessario avere un’autorizzazione per entrare"
- }
- },
- {
- "if": "access=customers",
- "then": {
- "en": "Only customers",
- "fr": "Réservé aux clients",
- "de": "Nur für Kunden",
- "it": "Riservato ai clienti",
- "ca": "Només clients"
- }
- },
- {
- "if": "access=members",
- "then": {
- "en": "Only club members",
- "ru": "Только членам клуба",
- "fr": "Réservé aux membres",
- "de": "Nur für Vereinsmitglieder",
- "it": "Riservato ai membri del club",
- "ca": "Només membres del club"
- }
- },
- {
- "if": "access=no",
- "then": "Not accessible"
- }
- ],
- "condition": {
- "and": [
- "climbing!=no",
- "office=",
- "club=",
- {
- "or": [
- "sport=climbing",
- "climbing:sport=yes"
- ]
+ "id": "access",
+ "question": {
+ "en": "Who can access here?",
+ "fr": "Qui peut y accéder ?",
+ "de": "Wer hat hier Zugang?",
+ "it": "Chi può accedervi?"
},
- {
+ "mappings": [
+ {
+ "if": "access=yes",
+ "then": {
+ "en": "Publicly accessible to anyone",
+ "fr": "Libre d’accès",
+ "de": "Öffentlich zugänglich für jedermann",
+ "it": "Pubblicamente accessibile a chiunque"
+ }
+ },
+ {
+ "if": "access=permit",
+ "then": {
+ "en": "You need a permit to access here",
+ "fr": "Une autorisation est nécessaire",
+ "de": "Zugang nur mit Genehmigung",
+ "it": "È necessario avere un’autorizzazione per entrare"
+ }
+ },
+ {
+ "if": "access=customers",
+ "then": {
+ "en": "Only customers",
+ "fr": "Réservé aux clients",
+ "de": "Nur für Kunden",
+ "it": "Riservato ai clienti",
+ "ca": "Només clients"
+ }
+ },
+ {
+ "if": "access=members",
+ "then": {
+ "en": "Only club members",
+ "ru": "Только членам клуба",
+ "fr": "Réservé aux membres",
+ "de": "Nur für Vereinsmitglieder",
+ "it": "Riservato ai membri del club",
+ "ca": "Només membres del club"
+ }
+ },
+ {
+ "if": "access=no",
+ "then": "Not accessible"
+ }
+ ],
+ "condition": {
"or": [
"access~*",
"_embedding_feature:access="
]
}
- ]
- }
- },
- {
- "id": "Access description (without _embedding_feature:access:description)",
- "render": "{access:description}",
- "freeform": {
- "key": "access:description"
- }
- },
- {
- "id": "Avg length?",
- "render": {
- "de": "Die Routen sind durchschnittlich {canonical(climbing:length)} lang",
- "en": "The routes are {canonical(climbing:length)} long on average",
- "nl": "De klimroutes zijn gemiddeld {canonical(climbing:length)} lang",
- "ja": "ルートの長さは平均で{canonical(climbing:length)} です",
- "fr": "Les voies font {canonical(climbing:length)} de long en moyenne",
- "it": "Le vie sono lunghe mediamente {canonical(climbing:length)} "
- },
- "condition": {
- "and": [
- "climbing!~route",
- "office=",
- "club=",
- "climbing:toprope!=no",
- {
- "or": [
- "sport=climbing",
- "climbing:sport=yes",
- "climbing=traditional",
- "climbing=gym"
- ]
- }
- ]
- },
- "question": {
- "de": "Wie lang sind die Routen (durchschnittlich) in Metern?",
- "en": "What is the (average) length of the routes in meters?",
- "nl": "Wat is de (gemiddelde) lengte van de klimroutes, in meter?",
- "ja": "ルートの(平均)長さはメートル単位でいくつですか?",
- "fr": "Quelle est la longueur moyenne des voies en mètres ?",
- "it": "Quale è la lunghezza (media) delle vie in metri?"
- },
- "freeform": {
- "key": "climbing:length",
- "type": "pnat"
- }
- },
- {
- "id": "Difficulty-min",
- "question": {
- "de": "Welche Schwierigkeit hat hier die leichteste Route (französisch/belgisches System)?",
- "en": "What is the grade of the easiest route here, according to the french classification system?",
- "nl": "Wat is het niveau van de makkelijkste route, volgens het Franse classificatiesysteem?",
- "ja": "ここで一番簡単なルートのレベルは、フランスのランク評価システムで何ですか?",
- "fr": "Quel est le niveau de la voie la plus simple selon la classification franco-belge ?",
- "it": "Qual è il livello della via più facile qua, secondo il sistema di classificazione francese?"
- },
- "render": {
- "de": "Die leichteste Route hat hier die Schwierigkeit {climbing:grade:french:min} (französisch/belgisches System)",
- "en": "The lowest grade is {climbing:grade:french:min} according to the french/belgian system",
- "nl": "De minimale klimmoeilijkheid is {climbing:grade:french:min} volgens het Franse/Belgische systeem",
- "ja": "フランス/ベルギーのランク評価システムでは、最小の難易度は{climbing:grade:french:min}です",
- "fr": "La difficulté minimale est {climbing:grade:french:min} selon la classification franco-belge",
- "it": "Il minimo livello di difficoltà è {climbing:grade:french:min} secondo il sistema francese/belga"
- },
- "freeform": {
- "key": "climbing:grade:french:min"
- },
- "condition": {
- "and": [
- "climbing!~route",
- "office=",
- "club=",
- {
- "or": [
- "climbing:sport=yes",
- "sport=climbing"
- ]
- }
- ]
- }
- },
- {
- "id": "Difficulty-max",
- "question": {
- "de": "Welche Schwierigkeit hat hier die schwerste Route (französisch/belgisches System)?",
- "en": "What is the highest grade route here, according to the french classification system?",
- "nl": "Wat is het niveau van de moeilijkste route, volgens het Franse classificatiesysteem?",
- "ja": "フランスのランク評価によると、ここで一番難しいルートのレベルはどれくらいですか?",
- "fr": "Quel est le niveau de la voie la plus difficile selon la classification franco-belge ?",
- "it": "Qual è il livello della via più difficile qua, secondo il sistema di classificazione francese?"
- },
- "render": {
- "de": "Die schwierigste Route hat hier die Schwierigkeitsstufe {climbing:grade:french:max} (französisch/belgisches System)",
- "en": "The highest grade is {climbing:grade:french:max} according to the french/belgian system",
- "nl": "De maximale klimmoeilijkheid is {climbing:grade:french:max} volgens het Franse/Belgische systeem",
- "ja": "フランス/ベルギーのランク評価システムでは、最大の難易度は{climbing:grade:french:max}です",
- "fr": "La difficulté maximale est {climbing:grade:french:max} selon la classification franco-belge",
- "it": "Il massimo livello di difficoltà è {climbing:grade:french:max} secondo il sistema francese/belga"
- },
- "freeform": {
- "key": "climbing:grade:french:max"
- },
- "condition": {
- "and": [
- "climbing!~route",
- "office=",
- "club=",
- {
- "or": [
- "climbing:sport=yes",
- "sport=climbing"
- ]
- }
- ]
- }
- },
- {
- "id": "Boldering?",
- "question": {
- "de": "Kann hier gebouldert werden?",
- "en": "Is bouldering possible here?",
- "nl": "Is het mogelijk om hier te bolderen?",
- "ja": "ここでボルダリングはできますか?",
- "nb_NO": "Er buldring mulig her?",
- "fr": "L’escalade de bloc est-elle possible ici ?",
- "it": "È possibile praticare ‘bouldering’ qua?"
- },
- "mappings": [
- {
- "if": "climbing:boulder=yes",
- "then": {
- "de": "Hier kann gebouldert werden",
- "en": "Bouldering is possible here",
- "nl": "Bolderen kan hier",
- "ja": "ボルダリングはここで可能です",
- "nb_NO": "Buldring er mulig her",
- "fr": "L’escalade de bloc est possible",
- "it": "L’arrampicata su massi è possibile qua"
- }
},
{
- "if": "climbing:boulder=no",
- "then": {
- "de": "Hier kann nicht gebouldert werden",
- "en": "Bouldering is not possible here",
- "nl": "Bolderen kan hier niet",
- "ja": "ここではボルダリングはできません",
- "nb_NO": "Buldring er ikke mulig her",
- "fr": "L’escalade de bloc n’est pas possible",
- "it": "L’arrampicata su massi non è possibile qua"
+ "id": "Access description (without _embedding_feature:access:description)",
+ "render": "{access:description}",
+ "freeform": {
+ "key": "access:description"
}
},
- {
- "if": "climbing:boulder=limited",
- "then": {
- "de": "Bouldern ist hier nur an wenigen Routen möglich",
- "en": "Bouldering is possible, allthough there are only a few routes",
- "nl": "Bolderen kan hier, maar er zijn niet zoveel routes",
- "ja": "ボルダリングは可能ですが、少しのルートしかありません",
- "fr": "L’escalade de bloc est possible sur des voies précises",
- "it": "L’arrampicata su massi è possibile anche se su poche vie"
- }
- },
- {
- "if": "climbing:boulder~*",
- "then": {
- "de": "Hier gibt es {climbing:boulder} Boulder-Routen",
- "en": "There are {climbing:boulder} boulder routes",
- "nl": "Er zijn hier {climbing:boulder} bolderroutes",
- "ja": "{climbing:boulder} ボルダールートがある",
- "fr": "Il y a {climbing:boulder} voies d’escalade de bloc",
- "it": "Sono presenti {climbing:boulder} vie di arrampicata su massi"
- },
- "hideInAnswer": true
- }
- ],
- "condition": {
- "and": [
- {
- "or": [
- "climbing:sport=yes",
- "sport=climbing"
- ]
- },
- "office=",
- "club="
- ]
- }
- },
- {
- "id": "Toproping?",
- "question": {
- "de": "Ist Toprope-Klettern hier möglich?",
- "en": "Is toprope climbing possible here?",
- "nl": "Is het mogelijk om hier te toprope-klimmen?",
- "ja": "ここでtoprope登坂はできますか?",
- "fr": "Est-il possible d’escalader à la moulinette ?",
- "it": "È possibile arrampicarsi con la corda dall’alto qua?"
- },
- "mappings": [
- {
- "if": "climbing:toprope=yes",
- "then": {
- "de": "Toprope-Klettern ist hier möglich",
- "en": "Toprope climbing is possible here",
- "nl": "Toprope-klimmen kan hier",
- "ja": "ここでToprope登坂ができます",
- "fr": "L’escalade à la moulinette est possible",
- "it": "È possibile arrampicarsi con moulinette qua"
- }
- },
- {
- "if": "climbing:toprope=no",
- "then": {
- "de": "Toprope-Climbing ist hier nicht möglich",
- "en": "Toprope climbing is not possible here",
- "nl": "Toprope-klimmen kan hier niet",
- "ja": "ここではToprope登坂はできません",
- "fr": "L’escalade à la moulinette n’est pas possible",
- "it": "Non è possibile arrampicarsi con moulinette qua"
- }
- },
- {
- "if": "climbing:toprope~*",
- "then": {
- "de": "Hier gibt es {climbing:toprope} Toprope-Routen",
- "en": "There are {climbing:toprope} toprope routes",
- "nl": "Er zijn hier {climbing:toprope} toprope routes",
- "ja": "{climbing:toprope} 登坂ルートがある",
- "fr": "{climbing:toprope} voies sont équipées de moulinettes",
- "it": "Sono presenti {climbing:toprope} vie con moulinette"
- },
- "hideInAnswer": true
- }
- ],
- "condition": {
- "and": [
- {
- "or": [
- "climbing:sport=yes",
- "sport=climbing"
- ]
- },
- "office=",
- "club="
- ]
- }
- },
- {
- "id": "Sportclimbing?",
- "question": {
- "de": "Ist hier Sportklettern möglich (feste Ankerpunkte)?",
- "en": "Is sport climbing possible here on fixed anchors?",
- "nl": "Is het mogelijk om hier te sportklimmen/voorklimmen op reeds aangebrachte haken?",
- "ja": "ここでは固定アンカー式のスポーツクライミングはできますか?",
- "it": "È possibile arrampicarsi qua con ancoraggi fissi?"
- },
- "mappings": [
- {
- "if": "climbing:sport=yes",
- "then": {
- "de": "Sportklettern ist hier möglich",
- "en": "Sport climbing is possible here",
- "nl": "Sportklimmen/voorklimmen kan hier",
- "ru": "Здесь можно заняться спортивным скалолазанием",
- "ja": "ここでスポーツクライミングができます",
- "it": "L’arrampicata sportiva è possibile qua",
- "hu": "Itt lehetőség van sportmászásra",
- "fr": "De l’escalade est possible ici"
- }
- },
- {
- "if": "climbing:sport=no",
- "then": {
- "de": "Sportklettern ist hier nicht möglich",
- "en": "Sport climbing is not possible here",
- "nl": "Sportklimmen/voorklimmen kan hier niet",
- "ru": "Спортивное скалолазание здесь невозможно",
- "ja": "ここではスポーツクライミングはできません",
- "it": "L’arrampicata sportiva non è possibile qua",
- "hu": "Itt nincs lehetőség sportmászásra",
- "fr": "L’escalade est impossible ici"
- }
- },
- {
- "if": "climbing:sport~*",
- "then": {
- "de": "Hier gibt es {climbing:sport} Sportkletter-Routen",
- "en": "There are {climbing:sport} sport climbing routes",
- "nl": "Er zijn hier {climbing:sport} sportklimroutes/voorklimroutes",
- "ja": "スポーツクライミングの {climbing:sport} ルートがある",
- "it": "Sono presenti {climbing:sport} vie di arrampicata sportiva"
- },
- "hideInAnswer": true
- }
- ],
- "condition": {
- "and": [
- {
- "or": [
- "climbing:sport=yes",
- "sport=climbing"
- ]
- },
- "office=",
- "club="
- ]
- }
- },
- {
- "id": "Traditional climbing?",
- "question": {
- "de": "Ist hier traditionelles Klettern möglich (eigene Sicherung z.B. mit Klemmkleilen)?",
- "en": "Is traditional climbing possible here (using own gear e.g. chocks)?",
- "nl": "Is het mogelijk om hier traditioneel te klimmen? (Dit is klimmen met klemblokjes en friends) ",
- "ja": "伝統的な登山はここで可能ですか(例えば、チョックのような独自のギアを使用して)?",
- "it": "È possibile arrampicarsi in maniera tradizionale qua (usando attrezzi propri, ad es. dadi)?"
- },
- "mappings": [
- {
- "if": "climbing:traditional=yes",
- "then": {
- "de": "Traditionelles Klettern ist hier möglich",
- "en": "Traditional climbing is possible here",
- "nl": "Traditioneel klimmen kan hier",
- "ja": "ここでは伝統的な登山が可能です",
- "it": "L’arrampicata tradizionale è possibile qua"
- }
- },
- {
- "if": "climbing:traditional=no",
- "then": {
- "de": "Traditionelles Klettern ist hier nicht möglich",
- "en": "Traditional climbing is not possible here",
- "nl": "Traditioneel klimmen kan hier niet",
- "ja": "伝統的な登山はここではできない",
- "it": "L’arrampicata tradizionale non è possibile qua"
- }
- },
- {
- "if": "climbing:traditional~*",
- "then": {
- "de": "Hier gibt es {climbing:traditional} Routen für traditionelles Klettern",
- "en": "There are {climbing:traditional} traditional climbing routes",
- "nl": "Er zijn hier {climbing:traditional} traditionele klimroutes",
- "ja": "{climbing:traditional} の伝統的な登山ルートがある",
- "it": "Sono presenti {climbing:traditional} vie di arrampicata tradizionale"
- },
- "hideInAnswer": true
- }
- ],
- "condition": {
- "and": [
- {
- "or": [
- "climbing:sport=yes",
- "sport=climbing"
- ]
- },
- "office=",
- "club="
- ]
- }
- },
- {
- "id": "Speed climbing?",
- "question": {
- "de": "Gibt es hier eine Speedkletter-Wand?",
- "en": "Is there a speed climbing wall?",
- "nl": "Is er een snelklimmuur (speed climbing)?",
- "ja": "スピードクライミングウォールはありますか?",
- "it": "È presente una prete per l’arrampicata di velocità?"
- },
- "condition": {
- "and": [
- "leisure=sports_centre",
- {
- "or": [
- "climbing:sport=yes",
- "sport=climbing"
- ]
- },
- "office=",
- "club="
- ]
- },
- "mappings": [
- {
- "if": "climbing:speed=yes",
- "then": {
- "de": "Hier gibt es eine Speedkletter-Wand",
- "en": "There is a speed climbing wall",
- "nl": "Er is een snelklimmuur voor speed climbing",
- "ja": "スピードクライミングウォールがある",
- "it": "È presente una parete per l’arrampicata di velocità"
- }
- },
- {
- "if": "climbing:speed=no",
- "then": {
- "de": "Hier gibt es keine Speedkletter-Wand",
- "en": "There is no speed climbing wall",
- "nl": "Er is geen snelklimmuur voor speed climbing",
- "ja": "スピードクライミングウォールがない",
- "it": "Non è presente una parete per l’arrampicata di velocità"
- }
- },
- {
- "if": "climbing:speed~*",
- "then": {
- "de": "Hier gibt es {climbing:speed} Speedkletter-Routen",
- "en": "There are {climbing:speed} speed climbing walls",
- "nl": "Er zijn hier {climbing:speed} snelklimmuren",
- "ja": "{climbing:speed} のスピードクライミングウォールがある",
- "it": "Sono presenti {climbing:speed} pareti per l’arrampicata di velocità"
- },
- "hideInAnswer": true
- }
+ "questions",
+ "reviews"
]
- },
- "questions",
- "reviews"
- ]
- }
+ }
+ }
+ ]
}
\ No newline at end of file
diff --git a/assets/themes/trees/trees.json b/assets/themes/trees/trees.json
index 910160fa58..393e0cadb1 100644
--- a/assets/themes/trees/trees.json
+++ b/assets/themes/trees/trees.json
@@ -17,8 +17,8 @@
"es": "Árboles"
},
"shortDescription": {
- "nl": "Breng bomen in kaart",
"en": "Map all the trees",
+ "nl": "Breng bomen in kaart",
"fr": "Carte des arbres",
"it": "Mappa tutti gli alberi",
"ja": "すべての樹木をマッピングする",
diff --git a/css/index-tailwind-output.css b/css/index-tailwind-output.css
index a41659488f..c517a81047 100644
--- a/css/index-tailwind-output.css
+++ b/css/index-tailwind-output.css
@@ -1144,16 +1144,16 @@ video {
width: 2.75rem;
}
-.w-16 {
- width: 4rem;
-}
-
.w-min {
width: -webkit-min-content;
width: -moz-min-content;
width: min-content;
}
+.w-16 {
+ width: 4rem;
+}
+
.w-auto {
width: auto;
}
@@ -1987,6 +1987,16 @@ a {
width: min-content;
}
+.rounded-left-full {
+ border-bottom-left-radius: 999rem;
+ border-top-left-radius: 999rem;
+}
+
+.rounded-right-full {
+ border-bottom-right-radius: 999rem;
+ border-top-right-radius: 999rem;
+}
+
.w-16-imp {
width: 4rem !important;
}
diff --git a/index.css b/index.css
index 77c7b01600..e70d93b25f 100644
--- a/index.css
+++ b/index.css
@@ -39,6 +39,16 @@
background-color: var(--catch-detail-color);
color: var(--catch-detail-color-contrast);
}
+
+ .rounded-left-full {
+ border-bottom-left-radius: 999rem;
+ border-top-left-radius: 999rem;
+ }
+
+ .rounded-right-full {
+ border-bottom-right-radius: 999rem;
+ border-top-right-radius: 999rem;
+ }
}
}
@@ -223,6 +233,16 @@ a {
width: min-content;
}
+.rounded-left-full {
+ border-bottom-left-radius: 999rem;
+ border-top-left-radius: 999rem;
+}
+
+.rounded-right-full {
+ border-bottom-right-radius: 999rem;
+ border-top-right-radius: 999rem;
+}
+
.w-16-imp {
width: 4rem !important;
}
diff --git a/scripts/generateLayerOverview.ts b/scripts/generateLayerOverview.ts
index 02cc138594..66fb28e883 100644
--- a/scripts/generateLayerOverview.ts
+++ b/scripts/generateLayerOverview.ts
@@ -156,6 +156,9 @@ class LayerOverviewUtils {
}
this.checkAllSvgs()
+
+ const green = s => '\x1b[92m' + s + '\x1b[0m'
+ console.log(green("All done!"))
}
private buildLayerIndex(knownImagePaths: Set): Map {
From 8b444156c626b4022da8deff986b6891bff11a5c Mon Sep 17 00:00:00 2001
From: pietervdvn
Date: Sat, 23 Apr 2022 17:01:53 +0200
Subject: [PATCH 10/27] Reset translations
---
langs/layers/ca.json | 9 +-
langs/layers/de.json | 120 ++++++++++++++++++---
langs/layers/en.json | 140 +++++++++++++++++++++---
langs/layers/fr.json | 88 ++++++++++++---
langs/layers/hu.json | 14 +++
langs/layers/id.json | 9 +-
langs/layers/it.json | 120 ++++++++++++++++++---
langs/layers/ja.json | 97 ++++++++++++++++-
langs/layers/nb_NO.json | 17 ++-
langs/layers/nl.json | 97 ++++++++++++++++-
langs/layers/ru.json | 22 +++-
langs/themes/ca.json | 44 ++++----
langs/themes/de.json | 230 ++++++++--------------------------------
langs/themes/en.json | 168 ++++++++---------------------
langs/themes/eo.json | 20 ++--
langs/themes/fr.json | 136 ++++++++----------------
langs/themes/hu.json | 14 ---
langs/themes/it.json | 168 ++++++++---------------------
langs/themes/ja.json | 92 ----------------
langs/themes/nb_NO.json | 15 ---
langs/themes/nl.json | 126 ++++------------------
langs/themes/ru.json | 41 +++----
22 files changed, 875 insertions(+), 912 deletions(-)
diff --git a/langs/layers/ca.json b/langs/layers/ca.json
index 15daa3a587..7e1050a67d 100644
--- a/langs/layers/ca.json
+++ b/langs/layers/ca.json
@@ -420,7 +420,7 @@
}
}
},
- "climbing": {
+ "climbing_area": {
"tagRenderings": {
"name": {
"render": "{name} "
@@ -441,13 +441,6 @@
}
}
},
- "climbing_gym": {
- "tagRenderings": {
- "name": {
- "render": "{name} "
- }
- }
- },
"climbing_opportunity": {
"tagRenderings": {
"climbing-opportunity-name": {
diff --git a/langs/layers/de.json b/langs/layers/de.json
index 0220e91464..a936c6148f 100644
--- a/langs/layers/de.json
+++ b/langs/layers/de.json
@@ -1616,7 +1616,84 @@
}
},
"climbing": {
- "description": "Eine Klettergelegenheit",
+ "tagRenderings": {
+ "average_length": {
+ "question": "Wie lang sind die Routen (durchschnittlich) in Metern?",
+ "render": "Die Routen sind durchschnittlich {canonical(climbing:length)} lang"
+ },
+ "bouldering": {
+ "mappings": {
+ "0": {
+ "then": "Hier kann gebouldert werden"
+ },
+ "1": {
+ "then": "Hier kann nicht gebouldert werden"
+ },
+ "2": {
+ "then": "Bouldern ist hier nur an wenigen Routen möglich"
+ },
+ "3": {
+ "then": "Hier gibt es {climbing:boulder} Boulder-Routen"
+ }
+ },
+ "question": "Kann hier gebouldert werden?"
+ },
+ "max_difficulty": {
+ "question": "Welche Schwierigkeit hat hier die schwerste Route (französisch/belgisches System)?",
+ "render": "Die schwierigste Route hat hier die Schwierigkeitsstufe {climbing:grade:french:max} (französisch/belgisches System)"
+ },
+ "min_difficulty": {
+ "question": "Welche Schwierigkeit hat hier die leichteste Route (französisch/belgisches System)?",
+ "render": "Die leichteste Route hat hier die Schwierigkeit {climbing:grade:french:min} (französisch/belgisches System)"
+ },
+ "sportclimbing": {
+ "mappings": {
+ "0": {
+ "then": "Sportklettern ist hier möglich"
+ },
+ "1": {
+ "then": "Sportklettern ist hier nicht möglich"
+ },
+ "2": {
+ "then": "Hier gibt es {climbing:sport} Sportkletter-Routen"
+ }
+ },
+ "question": "Ist hier Sportklettern möglich (feste Ankerpunkte)?"
+ },
+ "toprope": {
+ "mappings": {
+ "0": {
+ "then": "Toprope-Klettern ist hier möglich"
+ },
+ "1": {
+ "then": "Toprope-Climbing ist hier nicht möglich"
+ },
+ "2": {
+ "then": "Hier gibt es {climbing:toprope} Toprope-Routen"
+ }
+ },
+ "question": "Ist Toprope-Klettern hier möglich?"
+ },
+ "trad_climbing": {
+ "mappings": {
+ "0": {
+ "then": "Traditionelles Klettern ist hier möglich"
+ },
+ "1": {
+ "then": "Traditionelles Klettern ist hier nicht möglich"
+ },
+ "2": {
+ "then": "Hier gibt es {climbing:traditional} Routen für traditionelles Klettern"
+ }
+ },
+ "question": "Ist hier traditionelles Klettern möglich (eigene Sicherung z.B. mit Klemmkleilen)?"
+ },
+ "website": {
+ "question": "Gibt es eine (inoffizielle) Website mit mehr Informationen (z.B. Topos)?"
+ }
+ }
+ },
+ "climbing_area": {
"name": "Klettermöglichkeiten",
"presets": {
"0": {
@@ -1713,9 +1790,22 @@
"description": "Eine Kletterhalle",
"name": "Kletterhallen",
"tagRenderings": {
+ "Speed climbing?": {
+ "mappings": {
+ "0": {
+ "then": "Hier gibt es eine Speedkletter-Wand"
+ },
+ "1": {
+ "then": "Hier gibt es keine Speedkletter-Wand"
+ },
+ "2": {
+ "then": "Hier gibt es {climbing:speed} Speedkletter-Routen"
+ }
+ },
+ "question": "Gibt es hier eine Speedkletter-Wand?"
+ },
"name": {
- "question": "Wie heißt diese Kletterhalle?",
- "render": "{name} "
+ "question": "Wie heißt diese Kletterhalle?"
}
},
"title": {
@@ -1728,7 +1818,6 @@
}
},
"climbing_opportunity": {
- "description": "Eine Klettergelegenheit?",
"name": "Klettermöglichkeiten?",
"tagRenderings": {
"climbing-opportunity-name": {
@@ -1761,18 +1850,6 @@
}
},
"tagRenderings": {
- "Bolts": {
- "mappings": {
- "0": {
- "then": "Auf dieser Kletterroute sind keine Haken vorhanden"
- },
- "1": {
- "then": "Auf dieser Kletterroute sind keine Haken vorhanden"
- }
- },
- "question": "Wie viele Haken gibt es auf dieser Kletterroute bevor der Umlenker bzw. Standhaken erreicht ist?",
- "render": "Diese Kletterroute hat {climbing:bolts} Haken"
- },
"Difficulty": {
"question": "Wie hoch ist der Schwierigkeitsgrad dieser Kletterroute nach dem französisch/belgischen System?",
"render": "Die Schwierigkeit ist {climbing:grade:french} entsprechend des französisch/belgischen Systems"
@@ -1790,8 +1867,17 @@
"question": "Wie heißt diese Kletterroute?",
"render": "{name} "
},
- "Rock type": {
+ "Rock type via embedded feature": {
"render": "Der Gesteinstyp ist {_embedding_features_with_rock:rock}, wie auf dem umgebenden Felsen angegeben "
+ },
+ "bolts": {
+ "mappings": {
+ "0": {
+ "then": "Auf dieser Kletterroute sind keine Haken vorhanden"
+ }
+ },
+ "question": "Wie viele Haken gibt es auf dieser Kletterroute bevor der Umlenker bzw. Standhaken erreicht ist?",
+ "render": "Diese Kletterroute hat {climbing:bolts} Haken"
}
},
"title": {
diff --git a/langs/layers/en.json b/langs/layers/en.json
index aaf6f81d83..4bb4e86551 100644
--- a/langs/layers/en.json
+++ b/langs/layers/en.json
@@ -2303,7 +2303,102 @@
}
},
"climbing": {
- "description": "A climbing opportunity",
+ "description": "A dummy layer which contains tagrenderings, shared among the climbing layers",
+ "tagRenderings": {
+ "average_length": {
+ "question": "What is the (average) length of the routes in meters?",
+ "render": "The routes are {canonical(climbing:length)} long on average"
+ },
+ "bouldering": {
+ "mappings": {
+ "0": {
+ "then": "Bouldering is possible here"
+ },
+ "1": {
+ "then": "Bouldering is not possible here"
+ },
+ "2": {
+ "then": "Bouldering is possible, allthough there are only a few routes"
+ },
+ "3": {
+ "then": "There are {climbing:boulder} boulder routes"
+ }
+ },
+ "question": "Is bouldering possible here?"
+ },
+ "fee": {
+ "mappings": {
+ "0": {
+ "then": "Climbing here is free of charge"
+ },
+ "1": {
+ "then": "Paying a fee is required to climb here"
+ }
+ },
+ "question": "Is a fee required to climb here?",
+ "render": "A fee of {charge} should be paid for climbing here"
+ },
+ "max_bolts": {
+ "question": "How many bolts do routes in {title()} have at most?",
+ "render": "The sport climbing routes here have at most {climbing:bolts:max} bolts.This is without relays and indicates how much quickdraws a climber needs
"
+ },
+ "max_difficulty": {
+ "question": "What is the highest grade route here, according to the french classification system?",
+ "render": "The highest grade is {climbing:grade:french:max} according to the french/belgian system"
+ },
+ "min_difficulty": {
+ "question": "What is the grade of the easiest route here, according to the french classification system?",
+ "render": "The lowest grade is {climbing:grade:french:min} according to the french/belgian system"
+ },
+ "sportclimbing": {
+ "mappings": {
+ "0": {
+ "then": "Sport climbing is possible here"
+ },
+ "1": {
+ "then": "Sport climbing is not possible here"
+ },
+ "2": {
+ "then": "There are {climbing:sport} sport climbing routes"
+ }
+ },
+ "question": "Is sport climbing possible here on fixed anchors?"
+ },
+ "toprope": {
+ "mappings": {
+ "0": {
+ "then": "Toprope climbing is possible here"
+ },
+ "1": {
+ "then": "Toprope climbing is not possible here"
+ },
+ "2": {
+ "then": "There are {climbing:toprope} toprope routes"
+ }
+ },
+ "question": "Is toprope climbing possible here?"
+ },
+ "trad_climbing": {
+ "mappings": {
+ "0": {
+ "then": "Traditional climbing is possible here"
+ },
+ "1": {
+ "then": "Traditional climbing is not possible here"
+ },
+ "2": {
+ "then": "There are {climbing:traditional} traditional climbing routes"
+ }
+ },
+ "question": "Is traditional climbing possible here (using own gear e.g. chocks)?"
+ },
+ "website": {
+ "question": "Is there a (unofficial) website with more informations (e.g. topos)?"
+ }
+ }
+ },
+ "climbing_area": {
+ "description": "An area where climbing is possible, e.g. a crag, site, boulder, ... Contains aggregation of routes",
"name": "Climbing opportunities",
"presets": {
"0": {
@@ -2400,9 +2495,22 @@
"description": "A climbing gym",
"name": "Climbing gyms",
"tagRenderings": {
+ "Speed climbing?": {
+ "mappings": {
+ "0": {
+ "then": "There is a speed climbing wall"
+ },
+ "1": {
+ "then": "There is no speed climbing wall"
+ },
+ "2": {
+ "then": "There are {climbing:speed} speed climbing walls"
+ }
+ },
+ "question": "Is there a speed climbing wall?"
+ },
"name": {
- "question": "What is the name of this climbing gym?",
- "render": "{name} "
+ "question": "What is the name of this climbing gym?"
}
},
"title": {
@@ -2415,7 +2523,7 @@
}
},
"climbing_opportunity": {
- "description": "A climbing opportunity?",
+ "description": "Fallback layer with items on which climbing _might_ be possible. It is loaded when zoomed in a lot, to prevent duplicate items to be added",
"name": "Climbing opportunities?",
"tagRenderings": {
"climbing-opportunity-name": {
@@ -2441,6 +2549,7 @@
}
},
"climbing_route": {
+ "description": "A single climbing route and its properties. Some properties are derived from the containing features",
"name": "Climbing routes",
"presets": {
"0": {
@@ -2448,18 +2557,6 @@
}
},
"tagRenderings": {
- "Bolts": {
- "mappings": {
- "0": {
- "then": "This route is not bolted"
- },
- "1": {
- "then": "This route is not bolted"
- }
- },
- "question": "How many bolts does this route have before reaching the anchor?",
- "render": "This route has {climbing:bolts} bolts"
- },
"Difficulty": {
"question": "What is the grade of this climbing route according to the french/belgian system?",
"render": "The grade is {climbing:grade:french} according to the french/belgian system"
@@ -2477,8 +2574,17 @@
"question": "What is the name of this climbing route?",
"render": "{name} "
},
- "Rock type": {
+ "Rock type via embedded feature": {
"render": "The rock type is {_embedding_features_with_rock:rock} as stated on the surrounding crag "
+ },
+ "bolts": {
+ "mappings": {
+ "0": {
+ "then": "This route is not bolted"
+ }
+ },
+ "question": "How many bolts does this route have before reaching the anchor?",
+ "render": "This route has {climbing:bolts} bolts This is without relays and indicates how much quickdraws a climber needs
"
}
},
"title": {
diff --git a/langs/layers/fr.json b/langs/layers/fr.json
index 702021fff4..90d813408e 100644
--- a/langs/layers/fr.json
+++ b/langs/layers/fr.json
@@ -1036,7 +1036,66 @@
}
},
"climbing": {
- "description": "Opportunité d’escalade",
+ "tagRenderings": {
+ "average_length": {
+ "question": "Quelle est la longueur moyenne des voies en mètres ?",
+ "render": "Les voies font {canonical(climbing:length)} de long en moyenne"
+ },
+ "bouldering": {
+ "mappings": {
+ "0": {
+ "then": "L’escalade de bloc est possible"
+ },
+ "1": {
+ "then": "L’escalade de bloc n’est pas possible"
+ },
+ "2": {
+ "then": "L’escalade de bloc est possible sur des voies précises"
+ },
+ "3": {
+ "then": "Il y a {climbing:boulder} voies d’escalade de bloc"
+ }
+ },
+ "question": "L’escalade de bloc est-elle possible ici ?"
+ },
+ "max_difficulty": {
+ "question": "Quel est le niveau de la voie la plus difficile selon la classification franco-belge ?",
+ "render": "La difficulté maximale est {climbing:grade:french:max} selon la classification franco-belge"
+ },
+ "min_difficulty": {
+ "question": "Quel est le niveau de la voie la plus simple selon la classification franco-belge ?",
+ "render": "La difficulté minimale est {climbing:grade:french:min} selon la classification franco-belge"
+ },
+ "sportclimbing": {
+ "mappings": {
+ "0": {
+ "then": "De l’escalade est possible ici"
+ },
+ "1": {
+ "then": "L’escalade est impossible ici"
+ }
+ }
+ },
+ "toprope": {
+ "mappings": {
+ "0": {
+ "then": "L’escalade à la moulinette est possible"
+ },
+ "1": {
+ "then": "L’escalade à la moulinette n’est pas possible"
+ },
+ "2": {
+ "then": "{climbing:toprope} voies sont équipées de moulinettes"
+ }
+ },
+ "question": "Est-il possible d’escalader à la moulinette ?"
+ },
+ "website": {
+ "question": "Existe-t’il un site avec plus d’informations (ex : topographie) ?"
+ }
+ }
+ },
+ "climbing_area": {
"name": "Opportunité d’escalade",
"presets": {
"0": {
@@ -1134,8 +1193,7 @@
"name": "Salle d’escalade",
"tagRenderings": {
"name": {
- "question": "Quel est le nom de la salle d’escalade ?",
- "render": "{name} "
+ "question": "Quel est le nom de la salle d’escalade ?"
}
},
"title": {
@@ -1148,7 +1206,6 @@
}
},
"climbing_opportunity": {
- "description": "Opportunité d’escalade ?",
"name": "Opportunités d’escalade ?",
"tagRenderings": {
"climbing-opportunity-name": {
@@ -1181,18 +1238,6 @@
}
},
"tagRenderings": {
- "Bolts": {
- "mappings": {
- "0": {
- "then": "Cette voie n’a pas de prises"
- },
- "1": {
- "then": "Cette voie n’a pas de prises"
- }
- },
- "question": "Combien de prises cette voie possède avant d’atteindre la moulinette ?",
- "render": "Cette voie a {climbing:bolts} prises"
- },
"Difficulty": {
"question": "Quelle est la difficulté de cette voie selon le système franco-belge ?",
"render": "Selon le système franco-belge, la difficulté de cette voie est de {climbing:grade:french}"
@@ -1210,8 +1255,17 @@
"question": "Quel est le nom de cette voie d’escalade ?",
"render": "{name} "
},
- "Rock type": {
+ "Rock type via embedded feature": {
"render": "Le type de roche est {_embedding_features_with_rock:rock} selon le mur "
+ },
+ "bolts": {
+ "mappings": {
+ "0": {
+ "then": "Cette voie n’a pas de prises"
+ }
+ },
+ "question": "Combien de prises cette voie possède avant d’atteindre la moulinette ?",
+ "render": "Cette voie a {climbing:bolts} prises"
}
},
"title": {
diff --git a/langs/layers/hu.json b/langs/layers/hu.json
index 5bed8bef61..e85e53de42 100644
--- a/langs/layers/hu.json
+++ b/langs/layers/hu.json
@@ -474,6 +474,20 @@
}
}
},
+ "climbing": {
+ "tagRenderings": {
+ "sportclimbing": {
+ "mappings": {
+ "0": {
+ "then": "Itt lehetőség van sportmászásra"
+ },
+ "1": {
+ "then": "Itt nincs lehetőség sportmászásra"
+ }
+ }
+ }
+ }
+ },
"climbing_club": {
"description": "Mászóegyesület vagy -szervezet",
"name": "Mászóegyesület",
diff --git a/langs/layers/id.json b/langs/layers/id.json
index 12c017556d..dabc2bbedf 100644
--- a/langs/layers/id.json
+++ b/langs/layers/id.json
@@ -151,7 +151,7 @@
}
}
},
- "climbing": {
+ "climbing_area": {
"tagRenderings": {
"name": {
"render": "{name} "
@@ -165,13 +165,6 @@
}
}
},
- "climbing_gym": {
- "tagRenderings": {
- "name": {
- "render": "{name} "
- }
- }
- },
"climbing_opportunity": {
"tagRenderings": {
"climbing-opportunity-name": {
diff --git a/langs/layers/it.json b/langs/layers/it.json
index 859806f3a4..09f17d5937 100644
--- a/langs/layers/it.json
+++ b/langs/layers/it.json
@@ -792,7 +792,84 @@
}
},
"climbing": {
- "description": "Un’opportunità di arrampicata",
+ "tagRenderings": {
+ "average_length": {
+ "question": "Quale è la lunghezza (media) delle vie in metri?",
+ "render": "Le vie sono lunghe mediamente {canonical(climbing:length)} "
+ },
+ "bouldering": {
+ "mappings": {
+ "0": {
+ "then": "L’arrampicata su massi è possibile qua"
+ },
+ "1": {
+ "then": "L’arrampicata su massi non è possibile qua"
+ },
+ "2": {
+ "then": "L’arrampicata su massi è possibile anche se su poche vie"
+ },
+ "3": {
+ "then": "Sono presenti {climbing:boulder} vie di arrampicata su massi"
+ }
+ },
+ "question": "È possibile praticare ‘bouldering’ qua?"
+ },
+ "max_difficulty": {
+ "question": "Qual è il livello della via più difficile qua, secondo il sistema di classificazione francese?",
+ "render": "Il massimo livello di difficoltà è {climbing:grade:french:max} secondo il sistema francese/belga"
+ },
+ "min_difficulty": {
+ "question": "Qual è il livello della via più facile qua, secondo il sistema di classificazione francese?",
+ "render": "Il minimo livello di difficoltà è {climbing:grade:french:min} secondo il sistema francese/belga"
+ },
+ "sportclimbing": {
+ "mappings": {
+ "0": {
+ "then": "L’arrampicata sportiva è possibile qua"
+ },
+ "1": {
+ "then": "L’arrampicata sportiva non è possibile qua"
+ },
+ "2": {
+ "then": "Sono presenti {climbing:sport} vie di arrampicata sportiva"
+ }
+ },
+ "question": "È possibile arrampicarsi qua con ancoraggi fissi?"
+ },
+ "toprope": {
+ "mappings": {
+ "0": {
+ "then": "È possibile arrampicarsi con moulinette qua"
+ },
+ "1": {
+ "then": "Non è possibile arrampicarsi con moulinette qua"
+ },
+ "2": {
+ "then": "Sono presenti {climbing:toprope} vie con moulinette"
+ }
+ },
+ "question": "È possibile arrampicarsi con la corda dall’alto qua?"
+ },
+ "trad_climbing": {
+ "mappings": {
+ "0": {
+ "then": "L’arrampicata tradizionale è possibile qua"
+ },
+ "1": {
+ "then": "L’arrampicata tradizionale non è possibile qua"
+ },
+ "2": {
+ "then": "Sono presenti {climbing:traditional} vie di arrampicata tradizionale"
+ }
+ },
+ "question": "È possibile arrampicarsi in maniera tradizionale qua (usando attrezzi propri, ad es. dadi)?"
+ },
+ "website": {
+ "question": "C’è un sito web (anche non ufficiale) con qualche informazione in più (ad es. topografie)?"
+ }
+ }
+ },
+ "climbing_area": {
"name": "Opportunità di arrampicata",
"presets": {
"0": {
@@ -889,9 +966,22 @@
"description": "Una palestra di arrampicata",
"name": "Palestre di arrampicata",
"tagRenderings": {
+ "Speed climbing?": {
+ "mappings": {
+ "0": {
+ "then": "È presente una parete per l’arrampicata di velocità"
+ },
+ "1": {
+ "then": "Non è presente una parete per l’arrampicata di velocità"
+ },
+ "2": {
+ "then": "Sono presenti {climbing:speed} pareti per l’arrampicata di velocità"
+ }
+ },
+ "question": "È presente una prete per l’arrampicata di velocità?"
+ },
"name": {
- "question": "Qual è il nome di questa palestra di arrampicata?",
- "render": "{name} "
+ "question": "Qual è il nome di questa palestra di arrampicata?"
}
},
"title": {
@@ -904,7 +994,6 @@
}
},
"climbing_opportunity": {
- "description": "Un’opportunità di arrampicata?",
"name": "Opportunità di arrampicata?",
"tagRenderings": {
"climbing-opportunity-name": {
@@ -937,18 +1026,6 @@
}
},
"tagRenderings": {
- "Bolts": {
- "mappings": {
- "0": {
- "then": "In questo percorso non sono presenti bulloni"
- },
- "1": {
- "then": "In questo percorso non sono presenti bulloni"
- }
- },
- "question": "Quanti bulloni sono presenti in questo percorso prima di arrivare alla moulinette?",
- "render": "Questo percorso ha {climbing:bolts} bulloni"
- },
"Difficulty": {
"question": "Qual è la difficoltà di questa via di arrampicata nel sistema francese/belga?",
"render": "Il grado di difficoltà è {climbing:grade:french} nel sistema francese/belga"
@@ -966,8 +1043,17 @@
"question": "Come si chiama questa via di arrampicata?",
"render": "{name} "
},
- "Rock type": {
+ "Rock type via embedded feature": {
"render": "Il tipo di roccia è {_embedding_features_with_rock:rock} come dichiarato sul muro circostante "
+ },
+ "bolts": {
+ "mappings": {
+ "0": {
+ "then": "In questo percorso non sono presenti bulloni"
+ }
+ },
+ "question": "Quanti bulloni sono presenti in questo percorso prima di arrivare alla moulinette?",
+ "render": "Questo percorso ha {climbing:bolts} bulloni"
}
},
"title": {
diff --git a/langs/layers/ja.json b/langs/layers/ja.json
index b8075b171f..a7f4631cde 100644
--- a/langs/layers/ja.json
+++ b/langs/layers/ja.json
@@ -122,7 +122,84 @@
}
},
"climbing": {
- "description": "登坂教室",
+ "tagRenderings": {
+ "average_length": {
+ "question": "ルートの(平均)長さはメートル単位でいくつですか?",
+ "render": "ルートの長さは平均で{canonical(climbing:length)} です"
+ },
+ "bouldering": {
+ "mappings": {
+ "0": {
+ "then": "ボルダリングはここで可能です"
+ },
+ "1": {
+ "then": "ここではボルダリングはできません"
+ },
+ "2": {
+ "then": "ボルダリングは可能ですが、少しのルートしかありません"
+ },
+ "3": {
+ "then": "{climbing:boulder} ボルダールートがある"
+ }
+ },
+ "question": "ここでボルダリングはできますか?"
+ },
+ "max_difficulty": {
+ "question": "フランスのランク評価によると、ここで一番難しいルートのレベルはどれくらいですか?",
+ "render": "フランス/ベルギーのランク評価システムでは、最大の難易度は{climbing:grade:french:max}です"
+ },
+ "min_difficulty": {
+ "question": "ここで一番簡単なルートのレベルは、フランスのランク評価システムで何ですか?",
+ "render": "フランス/ベルギーのランク評価システムでは、最小の難易度は{climbing:grade:french:min}です"
+ },
+ "sportclimbing": {
+ "mappings": {
+ "0": {
+ "then": "ここでスポーツクライミングができます"
+ },
+ "1": {
+ "then": "ここではスポーツクライミングはできません"
+ },
+ "2": {
+ "then": "スポーツクライミングの {climbing:sport} ルートがある"
+ }
+ },
+ "question": "ここでは固定アンカー式のスポーツクライミングはできますか?"
+ },
+ "toprope": {
+ "mappings": {
+ "0": {
+ "then": "ここでToprope登坂ができます"
+ },
+ "1": {
+ "then": "ここではToprope登坂はできません"
+ },
+ "2": {
+ "then": "{climbing:toprope} 登坂ルートがある"
+ }
+ },
+ "question": "ここでtoprope登坂はできますか?"
+ },
+ "trad_climbing": {
+ "mappings": {
+ "0": {
+ "then": "ここでは伝統的な登山が可能です"
+ },
+ "1": {
+ "then": "伝統的な登山はここではできない"
+ },
+ "2": {
+ "then": "{climbing:traditional} の伝統的な登山ルートがある"
+ }
+ },
+ "question": "伝統的な登山はここで可能ですか(例えば、チョックのような独自のギアを使用して)?"
+ },
+ "website": {
+ "question": "もっと情報のある(非公式の)ウェブサイトはありますか(例えば、topos)?"
+ }
+ }
+ },
+ "climbing_area": {
"name": "登坂教室",
"presets": {
"0": {
@@ -177,9 +254,22 @@
"description": "クライミングジム",
"name": "クライミングジム",
"tagRenderings": {
+ "Speed climbing?": {
+ "mappings": {
+ "0": {
+ "then": "スピードクライミングウォールがある"
+ },
+ "1": {
+ "then": "スピードクライミングウォールがない"
+ },
+ "2": {
+ "then": "{climbing:speed} のスピードクライミングウォールがある"
+ }
+ },
+ "question": "スピードクライミングウォールはありますか?"
+ },
"name": {
- "question": "このクライミングジムは何という名前ですか?",
- "render": "{name} "
+ "question": "このクライミングジムは何という名前ですか?"
}
},
"title": {
@@ -192,7 +282,6 @@
}
},
"climbing_opportunity": {
- "description": "登坂教室?",
"name": "登坂教室?",
"tagRenderings": {
"climbing-opportunity-name": {
diff --git a/langs/layers/nb_NO.json b/langs/layers/nb_NO.json
index 487780cc5a..d6d054e450 100644
--- a/langs/layers/nb_NO.json
+++ b/langs/layers/nb_NO.json
@@ -196,7 +196,21 @@
}
},
"climbing": {
- "description": "En klatremulighet",
+ "tagRenderings": {
+ "bouldering": {
+ "mappings": {
+ "0": {
+ "then": "Buldring er mulig her"
+ },
+ "1": {
+ "then": "Buldring er ikke mulig her"
+ }
+ },
+ "question": "Er buldring mulig her?"
+ }
+ }
+ },
+ "climbing_area": {
"presets": {
"0": {
"description": "En klatremulighet",
@@ -221,7 +235,6 @@
}
},
"climbing_opportunity": {
- "description": "En klatremulighet?",
"name": "Klatremuligheter?",
"tagRenderings": {
"climbing-possible": {
diff --git a/langs/layers/nl.json b/langs/layers/nl.json
index fd581e4590..4cfc56cb70 100644
--- a/langs/layers/nl.json
+++ b/langs/layers/nl.json
@@ -2312,7 +2312,84 @@
}
},
"climbing": {
- "description": "Een klimgelegenheid",
+ "tagRenderings": {
+ "average_length": {
+ "question": "Wat is de (gemiddelde) lengte van de klimroutes, in meter?",
+ "render": "De klimroutes zijn gemiddeld {canonical(climbing:length)} lang"
+ },
+ "bouldering": {
+ "mappings": {
+ "0": {
+ "then": "Bolderen kan hier"
+ },
+ "1": {
+ "then": "Bolderen kan hier niet"
+ },
+ "2": {
+ "then": "Bolderen kan hier, maar er zijn niet zoveel routes"
+ },
+ "3": {
+ "then": "Er zijn hier {climbing:boulder} bolderroutes"
+ }
+ },
+ "question": "Is het mogelijk om hier te bolderen?"
+ },
+ "max_difficulty": {
+ "question": "Wat is het niveau van de moeilijkste route, volgens het Franse classificatiesysteem?",
+ "render": "De maximale klimmoeilijkheid is {climbing:grade:french:max} volgens het Franse/Belgische systeem"
+ },
+ "min_difficulty": {
+ "question": "Wat is het niveau van de makkelijkste route, volgens het Franse classificatiesysteem?",
+ "render": "De minimale klimmoeilijkheid is {climbing:grade:french:min} volgens het Franse/Belgische systeem"
+ },
+ "sportclimbing": {
+ "mappings": {
+ "0": {
+ "then": "Sportklimmen/voorklimmen kan hier"
+ },
+ "1": {
+ "then": "Sportklimmen/voorklimmen kan hier niet"
+ },
+ "2": {
+ "then": "Er zijn hier {climbing:sport} sportklimroutes/voorklimroutes"
+ }
+ },
+ "question": "Is het mogelijk om hier te sportklimmen/voorklimmen op reeds aangebrachte haken?"
+ },
+ "toprope": {
+ "mappings": {
+ "0": {
+ "then": "Toprope-klimmen kan hier"
+ },
+ "1": {
+ "then": "Toprope-klimmen kan hier niet"
+ },
+ "2": {
+ "then": "Er zijn hier {climbing:toprope} toprope routes"
+ }
+ },
+ "question": "Is het mogelijk om hier te toprope-klimmen?"
+ },
+ "trad_climbing": {
+ "mappings": {
+ "0": {
+ "then": "Traditioneel klimmen kan hier"
+ },
+ "1": {
+ "then": "Traditioneel klimmen kan hier niet"
+ },
+ "2": {
+ "then": "Er zijn hier {climbing:traditional} traditionele klimroutes"
+ }
+ },
+ "question": "Is het mogelijk om hier traditioneel te klimmen? (Dit is klimmen met klemblokjes en friends) "
+ },
+ "website": {
+ "question": "Is er een (onofficiële) website met meer informatie (b.v. met topos)?"
+ }
+ }
+ },
+ "climbing_area": {
"name": "Klimgelegenheden",
"presets": {
"0": {
@@ -2385,9 +2462,22 @@
"description": "Een klimzaal",
"name": "Klimzalen",
"tagRenderings": {
+ "Speed climbing?": {
+ "mappings": {
+ "0": {
+ "then": "Er is een snelklimmuur voor speed climbing"
+ },
+ "1": {
+ "then": "Er is geen snelklimmuur voor speed climbing"
+ },
+ "2": {
+ "then": "Er zijn hier {climbing:speed} snelklimmuren"
+ }
+ },
+ "question": "Is er een snelklimmuur (speed climbing)?"
+ },
"name": {
- "question": "Wat is de naam van dit Klimzaal?",
- "render": "{name} "
+ "question": "Wat is de naam van dit Klimzaal?"
}
},
"title": {
@@ -2400,7 +2490,6 @@
}
},
"climbing_opportunity": {
- "description": "Een klimgelegenheid?",
"name": "Klimgelegenheiden?",
"tagRenderings": {
"climbing-opportunity-name": {
diff --git a/langs/layers/ru.json b/langs/layers/ru.json
index fca36af7e7..9d4b2bc2c8 100644
--- a/langs/layers/ru.json
+++ b/langs/layers/ru.json
@@ -764,6 +764,23 @@
}
},
"climbing": {
+ "tagRenderings": {
+ "sportclimbing": {
+ "mappings": {
+ "0": {
+ "then": "Здесь можно заняться спортивным скалолазанием"
+ },
+ "1": {
+ "then": "Спортивное скалолазание здесь невозможно"
+ }
+ }
+ },
+ "website": {
+ "question": "Есть ли (неофициальный) веб-сайт с более подробной информацией (напр., topos)?"
+ }
+ }
+ },
+ "climbing_area": {
"tagRenderings": {
"name": {
"render": "{name} "
@@ -790,11 +807,6 @@
"climbing_gym": {
"description": "Комплекс скалолазания",
"name": "Комплексы скалолазания",
- "tagRenderings": {
- "name": {
- "render": "{name} "
- }
- },
"title": {
"render": "Комплекс скалолазания"
}
diff --git a/langs/themes/ca.json b/langs/themes/ca.json
index 4fe0334155..85a3e24363 100644
--- a/langs/themes/ca.json
+++ b/langs/themes/ca.json
@@ -49,27 +49,31 @@
"title": "Estacions de càrrega"
},
"climbing": {
- "overrideAll": {
- "tagRenderings+": {
- "2": {
- "mappings": {
- "2": {
- "then": "Només clients"
- },
- "3": {
- "then": "Només membres del club"
- }
- }
- }
- },
- "units+": {
- "0": {
- "applicableUnits": {
- "0": {
- "human": " metre"
- },
+ "layers": {
+ "0": {
+ "override": {
+ "tagRenderings+": {
"1": {
- "human": " peus"
+ "mappings": {
+ "2": {
+ "then": "Només clients"
+ },
+ "3": {
+ "then": "Només membres del club"
+ }
+ }
+ }
+ },
+ "units+": {
+ "0": {
+ "applicableUnits": {
+ "0": {
+ "human": " metre"
+ },
+ "1": {
+ "human": " peus"
+ }
+ }
}
}
}
diff --git a/langs/themes/de.json b/langs/themes/de.json
index 56f22bcfe9..cef05927a1 100644
--- a/langs/themes/de.json
+++ b/langs/themes/de.json
@@ -269,138 +269,54 @@
"climbing": {
"description": "Eine Karte mit Klettermöglichkeiten wie Kletterhallen, Kletterparks oder Felsen.",
"descriptionTail": "kletterspots.de wird betrieben von Christian Neumann . Bitte melden Sie sich , wenn Sie Feedback oder Fragen haben.
Das Projekt nutzt OpenStreetMap Daten und basiert auf der freien Software MapComplete .
",
- "overrideAll": {
- "tagRenderings+": {
- "0": {
- "question": "Gibt es eine (inoffizielle) Website mit mehr Informationen (z.B. Topos)?"
- },
- "1": {
- "mappings": {
+ "layers": {
+ "0": {
+ "override": {
+ "tagRenderings+": {
"0": {
- "then": "Das enthaltende Objekt gibt an, dass es öffentlich zugänglich ist {_embedding_feature:access:description}"
+ "mappings": {
+ "0": {
+ "then": "Das enthaltende Objekt gibt an, dass es öffentlich zugänglich ist {_embedding_feature:access:description}"
+ },
+ "1": {
+ "then": "Das enthaltende Objekt besagt, dass eine Genehmigung erforderlich ist für den Zugang zu {_embedding_feature:access:description}"
+ },
+ "2": {
+ "then": "Das enthaltende Objekt besagt, dass es nur für Kunden zugänglich ist {_embedding_feature:access:description}"
+ },
+ "3": {
+ "then": "Das enthaltende Objekt besagt, dass es nur für Mitglieder zugänglich ist {_embedding_feature:access:description}"
+ }
+ }
},
"1": {
- "then": "Das enthaltende Objekt besagt, dass eine Genehmigung erforderlich ist für den Zugang zu {_embedding_feature:access:description}"
- },
- "2": {
- "then": "Das enthaltende Objekt besagt, dass es nur für Kunden zugänglich ist {_embedding_feature:access:description}"
- },
- "3": {
- "then": "Das enthaltende Objekt besagt, dass es nur für Mitglieder zugänglich ist {_embedding_feature:access:description}"
- }
- }
- },
- "2": {
- "mappings": {
- "0": {
- "then": "Öffentlich zugänglich für jedermann"
- },
- "1": {
- "then": "Zugang nur mit Genehmigung"
- },
- "2": {
- "then": "Nur für Kunden"
- },
- "3": {
- "then": "Nur für Vereinsmitglieder"
+ "mappings": {
+ "0": {
+ "then": "Öffentlich zugänglich für jedermann"
+ },
+ "1": {
+ "then": "Zugang nur mit Genehmigung"
+ },
+ "2": {
+ "then": "Nur für Kunden"
+ },
+ "3": {
+ "then": "Nur für Vereinsmitglieder"
+ }
+ },
+ "question": "Wer hat hier Zugang?"
}
},
- "question": "Wer hat hier Zugang?"
- },
- "4": {
- "question": "Wie lang sind die Routen (durchschnittlich) in Metern?",
- "render": "Die Routen sind durchschnittlich {canonical(climbing:length)} lang"
- },
- "5": {
- "question": "Welche Schwierigkeit hat hier die leichteste Route (französisch/belgisches System)?",
- "render": "Die leichteste Route hat hier die Schwierigkeit {climbing:grade:french:min} (französisch/belgisches System)"
- },
- "6": {
- "question": "Welche Schwierigkeit hat hier die schwerste Route (französisch/belgisches System)?",
- "render": "Die schwierigste Route hat hier die Schwierigkeitsstufe {climbing:grade:french:max} (französisch/belgisches System)"
- },
- "7": {
- "mappings": {
+ "units+": {
"0": {
- "then": "Hier kann gebouldert werden"
- },
- "1": {
- "then": "Hier kann nicht gebouldert werden"
- },
- "2": {
- "then": "Bouldern ist hier nur an wenigen Routen möglich"
- },
- "3": {
- "then": "Hier gibt es {climbing:boulder} Boulder-Routen"
- }
- },
- "question": "Kann hier gebouldert werden?"
- },
- "8": {
- "mappings": {
- "0": {
- "then": "Toprope-Klettern ist hier möglich"
- },
- "1": {
- "then": "Toprope-Climbing ist hier nicht möglich"
- },
- "2": {
- "then": "Hier gibt es {climbing:toprope} Toprope-Routen"
- }
- },
- "question": "Ist Toprope-Klettern hier möglich?"
- },
- "9": {
- "mappings": {
- "0": {
- "then": "Sportklettern ist hier möglich"
- },
- "1": {
- "then": "Sportklettern ist hier nicht möglich"
- },
- "2": {
- "then": "Hier gibt es {climbing:sport} Sportkletter-Routen"
- }
- },
- "question": "Ist hier Sportklettern möglich (feste Ankerpunkte)?"
- },
- "10": {
- "mappings": {
- "0": {
- "then": "Traditionelles Klettern ist hier möglich"
- },
- "1": {
- "then": "Traditionelles Klettern ist hier nicht möglich"
- },
- "2": {
- "then": "Hier gibt es {climbing:traditional} Routen für traditionelles Klettern"
- }
- },
- "question": "Ist hier traditionelles Klettern möglich (eigene Sicherung z.B. mit Klemmkleilen)?"
- },
- "11": {
- "mappings": {
- "0": {
- "then": "Hier gibt es eine Speedkletter-Wand"
- },
- "1": {
- "then": "Hier gibt es keine Speedkletter-Wand"
- },
- "2": {
- "then": "Hier gibt es {climbing:speed} Speedkletter-Routen"
- }
- },
- "question": "Gibt es hier eine Speedkletter-Wand?"
- }
- },
- "units+": {
- "0": {
- "applicableUnits": {
- "0": {
- "human": " Meter"
- },
- "1": {
- "human": " Fuß"
+ "applicableUnits": {
+ "0": {
+ "human": " Meter"
+ },
+ "1": {
+ "human": " Fuß"
+ }
+ }
}
}
}
@@ -724,68 +640,6 @@
"shortDescription": "Hydranten, Feuerlöscher, Feuerwachen und Rettungswachen.",
"title": "Hydranten, Feuerlöscher, Feuerwachen und Rettungswachen"
},
- "mapcomplete-changes": {
- "description": "Diese Karte zeigt alle Änderungen die mit MapComplete gemacht wurden",
- "layers": {
- "0": {
- "description": "Zeigt alle MapComplete Änderungen",
- "filter": {
- "0": {
- "options": {
- "0": {
- "question": "Themenname enthält {search}"
- }
- }
- },
- "1": {
- "options": {
- "0": {
- "question": "Erstellt von {search}"
- }
- }
- },
- "2": {
- "options": {
- "0": {
- "question": "Nicht erstellt von {search}"
- }
- }
- }
- },
- "name": "Schwerpunkte von Änderungssätzen",
- "tagRenderings": {
- "contributor": {
- "render": "Änderung wurde von {_last_edit:contributor} gemacht"
- },
- "render_id": {
- "render": "Änderung {id} "
- },
- "theme": {
- "mappings": {
- "0": {
- "then": "Änderung mit inoffiziellem Thema {theme} "
- }
- },
- "render": "Änderung mit Thema {theme} "
- }
- },
- "title": {
- "render": "Änderungen für {theme}"
- }
- },
- "1": {
- "override": {
- "tagRenderings": {
- "link_to_more": {
- "render": "Weitere Statistiken finden Sie hier "
- }
- }
- }
- }
- },
- "shortDescription": "Zeigt Änderungen von MapComplete",
- "title": "Änderungen mit MapComplete"
- },
"maps": {
"description": "Auf dieser Karte findest du alle Karten, die OpenStreetMap kennt - typischerweise eine große Karte auf einer Informationstafel, die das Gebiet, die Stadt oder die Region zeigt, z.B. eine touristische Karte auf der Rückseite einer Plakatwand, eine Karte eines Naturschutzgebietes, eine Karte der Radwegenetze in der Region, ...) Wenn eine Karte fehlt, können Sie diese leicht auf OpenStreetMap kartieren.",
"shortDescription": "Dieses Thema zeigt alle (touristischen) Karten, die OpenStreetMap kennt",
diff --git a/langs/themes/en.json b/langs/themes/en.json
index 58279b6f6d..9751769163 100644
--- a/langs/themes/en.json
+++ b/langs/themes/en.json
@@ -269,138 +269,54 @@
"climbing": {
"description": "On this map you will find various climbing opportunities such as climbing gyms, bouldering halls and rocks in nature.",
"descriptionTail": "The climbing map was originally made by Christian Neumann . Please get in touch if you have feedback or questions.
The project uses data of the OpenStreetMap project.
",
- "overrideAll": {
- "tagRenderings+": {
- "0": {
- "question": "Is there a (unofficial) website with more informations (e.g. topos)?"
- },
- "1": {
- "mappings": {
+ "layers": {
+ "0": {
+ "override": {
+ "tagRenderings+": {
"0": {
- "then": "The containing feature states that this is publicly accessible {_embedding_feature:access:description}"
+ "mappings": {
+ "0": {
+ "then": "The containing feature states that this is publicly accessible {_embedding_feature:access:description}"
+ },
+ "1": {
+ "then": "The containing feature states that a permit is needed to access {_embedding_feature:access:description}"
+ },
+ "2": {
+ "then": "The containing feature states that this is only accessible to customers {_embedding_feature:access:description}"
+ },
+ "3": {
+ "then": "The containing feature states that this is only accessible to club members {_embedding_feature:access:description}"
+ }
+ }
},
"1": {
- "then": "The containing feature states that a permit is needed to access {_embedding_feature:access:description}"
- },
- "2": {
- "then": "The containing feature states that this is only accessible to customers {_embedding_feature:access:description}"
- },
- "3": {
- "then": "The containing feature states that this is only accessible to club members {_embedding_feature:access:description}"
- }
- }
- },
- "2": {
- "mappings": {
- "0": {
- "then": "Publicly accessible to anyone"
- },
- "1": {
- "then": "You need a permit to access here"
- },
- "2": {
- "then": "Only customers"
- },
- "3": {
- "then": "Only club members"
+ "mappings": {
+ "0": {
+ "then": "Publicly accessible to anyone"
+ },
+ "1": {
+ "then": "You need a permit to access here"
+ },
+ "2": {
+ "then": "Only customers"
+ },
+ "3": {
+ "then": "Only club members"
+ }
+ },
+ "question": "Who can access here?"
}
},
- "question": "Who can access here?"
- },
- "4": {
- "question": "What is the (average) length of the routes in meters?",
- "render": "The routes are {canonical(climbing:length)} long on average"
- },
- "5": {
- "question": "What is the grade of the easiest route here, according to the french classification system?",
- "render": "The lowest grade is {climbing:grade:french:min} according to the french/belgian system"
- },
- "6": {
- "question": "What is the highest grade route here, according to the french classification system?",
- "render": "The highest grade is {climbing:grade:french:max} according to the french/belgian system"
- },
- "7": {
- "mappings": {
+ "units+": {
"0": {
- "then": "Bouldering is possible here"
- },
- "1": {
- "then": "Bouldering is not possible here"
- },
- "2": {
- "then": "Bouldering is possible, allthough there are only a few routes"
- },
- "3": {
- "then": "There are {climbing:boulder} boulder routes"
- }
- },
- "question": "Is bouldering possible here?"
- },
- "8": {
- "mappings": {
- "0": {
- "then": "Toprope climbing is possible here"
- },
- "1": {
- "then": "Toprope climbing is not possible here"
- },
- "2": {
- "then": "There are {climbing:toprope} toprope routes"
- }
- },
- "question": "Is toprope climbing possible here?"
- },
- "9": {
- "mappings": {
- "0": {
- "then": "Sport climbing is possible here"
- },
- "1": {
- "then": "Sport climbing is not possible here"
- },
- "2": {
- "then": "There are {climbing:sport} sport climbing routes"
- }
- },
- "question": "Is sport climbing possible here on fixed anchors?"
- },
- "10": {
- "mappings": {
- "0": {
- "then": "Traditional climbing is possible here"
- },
- "1": {
- "then": "Traditional climbing is not possible here"
- },
- "2": {
- "then": "There are {climbing:traditional} traditional climbing routes"
- }
- },
- "question": "Is traditional climbing possible here (using own gear e.g. chocks)?"
- },
- "11": {
- "mappings": {
- "0": {
- "then": "There is a speed climbing wall"
- },
- "1": {
- "then": "There is no speed climbing wall"
- },
- "2": {
- "then": "There are {climbing:speed} speed climbing walls"
- }
- },
- "question": "Is there a speed climbing wall?"
- }
- },
- "units+": {
- "0": {
- "applicableUnits": {
- "0": {
- "human": " meter"
- },
- "1": {
- "human": " feet"
+ "applicableUnits": {
+ "0": {
+ "human": " meter"
+ },
+ "1": {
+ "human": " feet"
+ }
+ }
}
}
}
diff --git a/langs/themes/eo.json b/langs/themes/eo.json
index 4912597b35..ba4794e65b 100644
--- a/langs/themes/eo.json
+++ b/langs/themes/eo.json
@@ -1,14 +1,18 @@
{
"climbing": {
- "overrideAll": {
- "units+": {
- "0": {
- "applicableUnits": {
+ "layers": {
+ "0": {
+ "override": {
+ "units+": {
"0": {
- "human": " metro"
- },
- "1": {
- "human": " futo"
+ "applicableUnits": {
+ "0": {
+ "human": " metro"
+ },
+ "1": {
+ "human": " futo"
+ }
+ }
}
}
}
diff --git a/langs/themes/fr.json b/langs/themes/fr.json
index eeb226c080..b7de97d2a9 100644
--- a/langs/themes/fr.json
+++ b/langs/themes/fr.json
@@ -269,106 +269,54 @@
"climbing": {
"description": "Cette carte indique les sites d’escalades comme les salles d’escalade ou les sites naturels.",
"descriptionTail": "La carte des sites d'escalade a été créée par Christian Neumann . Merci de le contacter pour des avis ou des questions.Ce projet utilise les données OpenStreetMap .
",
- "overrideAll": {
- "tagRenderings+": {
- "0": {
- "question": "Existe-t’il un site avec plus d’informations (ex : topographie) ?"
- },
- "1": {
- "mappings": {
+ "layers": {
+ "0": {
+ "override": {
+ "tagRenderings+": {
"0": {
- "then": "L’élément englobant indique un accès libre {_embedding_feature:access:description}"
+ "mappings": {
+ "0": {
+ "then": "L’élément englobant indique un accès libre {_embedding_feature:access:description}"
+ },
+ "1": {
+ "then": "L’élément englobant indique qu’ une autorisation d’accès est nécessaire {_embedding_feature:access:description}"
+ },
+ "2": {
+ "then": "L’élément englobant indique que l’accès est réservés aux clients {_embedding_feature:access:description}"
+ },
+ "3": {
+ "then": "L’élément englobant indique que l’accès est réservé aux membres {_embedding_feature:access:description}"
+ }
+ }
},
"1": {
- "then": "L’élément englobant indique qu’ une autorisation d’accès est nécessaire {_embedding_feature:access:description}"
- },
- "2": {
- "then": "L’élément englobant indique que l’accès est réservés aux clients {_embedding_feature:access:description}"
- },
- "3": {
- "then": "L’élément englobant indique que l’accès est réservé aux membres {_embedding_feature:access:description}"
- }
- }
- },
- "2": {
- "mappings": {
- "0": {
- "then": "Libre d’accès"
- },
- "1": {
- "then": "Une autorisation est nécessaire"
- },
- "2": {
- "then": "Réservé aux clients"
- },
- "3": {
- "then": "Réservé aux membres"
+ "mappings": {
+ "0": {
+ "then": "Libre d’accès"
+ },
+ "1": {
+ "then": "Une autorisation est nécessaire"
+ },
+ "2": {
+ "then": "Réservé aux clients"
+ },
+ "3": {
+ "then": "Réservé aux membres"
+ }
+ },
+ "question": "Qui peut y accéder ?"
}
},
- "question": "Qui peut y accéder ?"
- },
- "4": {
- "question": "Quelle est la longueur moyenne des voies en mètres ?",
- "render": "Les voies font {canonical(climbing:length)} de long en moyenne"
- },
- "5": {
- "question": "Quel est le niveau de la voie la plus simple selon la classification franco-belge ?",
- "render": "La difficulté minimale est {climbing:grade:french:min} selon la classification franco-belge"
- },
- "6": {
- "question": "Quel est le niveau de la voie la plus difficile selon la classification franco-belge ?",
- "render": "La difficulté maximale est {climbing:grade:french:max} selon la classification franco-belge"
- },
- "7": {
- "mappings": {
+ "units+": {
"0": {
- "then": "L’escalade de bloc est possible"
- },
- "1": {
- "then": "L’escalade de bloc n’est pas possible"
- },
- "2": {
- "then": "L’escalade de bloc est possible sur des voies précises"
- },
- "3": {
- "then": "Il y a {climbing:boulder} voies d’escalade de bloc"
- }
- },
- "question": "L’escalade de bloc est-elle possible ici ?"
- },
- "8": {
- "mappings": {
- "0": {
- "then": "L’escalade à la moulinette est possible"
- },
- "1": {
- "then": "L’escalade à la moulinette n’est pas possible"
- },
- "2": {
- "then": "{climbing:toprope} voies sont équipées de moulinettes"
- }
- },
- "question": "Est-il possible d’escalader à la moulinette ?"
- },
- "9": {
- "mappings": {
- "0": {
- "then": "De l’escalade est possible ici"
- },
- "1": {
- "then": "L’escalade est impossible ici"
- }
- }
- }
- },
- "units+": {
- "0": {
- "applicableUnits": {
- "0": {
- "human": " mètres"
- },
- "1": {
- "human": " pieds"
+ "applicableUnits": {
+ "0": {
+ "human": " mètres"
+ },
+ "1": {
+ "human": " pieds"
+ }
+ }
}
}
}
diff --git a/langs/themes/hu.json b/langs/themes/hu.json
index c7e3fc42de..40bce9c7b8 100644
--- a/langs/themes/hu.json
+++ b/langs/themes/hu.json
@@ -93,20 +93,6 @@
"climbing": {
"description": "Ezen a térképen különböző mászási lehetőségeket talál, például falmászótermeket, bouldertermeket és sziklákat a természetben.",
"descriptionTail": "A mászótérképet eredetileg Christian Neumann készítette. Ha észrevétele vagy kérdése van, kérjük, vele lépjen kapcsolatba . A projekt az OpenStreetMap adatait használja
",
- "overrideAll": {
- "tagRenderings+": {
- "9": {
- "mappings": {
- "0": {
- "then": "Itt lehetőség van sportmászásra"
- },
- "1": {
- "then": "Itt nincs lehetőség sportmászásra"
- }
- }
- }
- }
- },
"title": "Mászótérkép"
},
"cycle_infra": {
diff --git a/langs/themes/it.json b/langs/themes/it.json
index b39d5b4506..f9518d0d42 100644
--- a/langs/themes/it.json
+++ b/langs/themes/it.json
@@ -263,138 +263,54 @@
"climbing": {
"description": "In questa cartina puoi trovare vari luoghi per arrampicata come ad esempio palestre di arrampicata, sale di pratica e rocce naturali.",
"descriptionTail": "La cartina di arrampicata è stata originariamente creata da Christian Neumann . Si prega di scrivere qua se si hanno commenti o domande da fare.Il progetto usa i dati del progetto OpenStreetMap .
",
- "overrideAll": {
- "tagRenderings+": {
- "0": {
- "question": "C’è un sito web (anche non ufficiale) con qualche informazione in più (ad es. topografie)?"
- },
- "1": {
- "mappings": {
+ "layers": {
+ "0": {
+ "override": {
+ "tagRenderings+": {
"0": {
- "then": "L’ elemento in cui è contenuto indica che è pubblicamente accessibile {_embedding_feature:access:description}"
+ "mappings": {
+ "0": {
+ "then": "L’ elemento in cui è contenuto indica che è pubblicamente accessibile {_embedding_feature:access:description}"
+ },
+ "1": {
+ "then": "L’elemento che lo contiene indica che è richiesto un’autorizzazione per accedervi {_embedding_feature:access:description}"
+ },
+ "2": {
+ "then": "L’ elemento che lo contiene indica che è accessibile solo ai clienti {_embedding_feature:access:description}"
+ },
+ "3": {
+ "then": "L’ elemento che lo contiene indica che è accessibile solamente ai membri del club {_embedding_feature:access:description}"
+ }
+ }
},
"1": {
- "then": "L’elemento che lo contiene indica che è richiesto un’autorizzazione per accedervi {_embedding_feature:access:description}"
- },
- "2": {
- "then": "L’ elemento che lo contiene indica che è accessibile solo ai clienti {_embedding_feature:access:description}"
- },
- "3": {
- "then": "L’ elemento che lo contiene indica che è accessibile solamente ai membri del club {_embedding_feature:access:description}"
- }
- }
- },
- "2": {
- "mappings": {
- "0": {
- "then": "Pubblicamente accessibile a chiunque"
- },
- "1": {
- "then": "È necessario avere un’autorizzazione per entrare"
- },
- "2": {
- "then": "Riservato ai clienti"
- },
- "3": {
- "then": "Riservato ai membri del club"
+ "mappings": {
+ "0": {
+ "then": "Pubblicamente accessibile a chiunque"
+ },
+ "1": {
+ "then": "È necessario avere un’autorizzazione per entrare"
+ },
+ "2": {
+ "then": "Riservato ai clienti"
+ },
+ "3": {
+ "then": "Riservato ai membri del club"
+ }
+ },
+ "question": "Chi può accedervi?"
}
},
- "question": "Chi può accedervi?"
- },
- "4": {
- "question": "Quale è la lunghezza (media) delle vie in metri?",
- "render": "Le vie sono lunghe mediamente {canonical(climbing:length)} "
- },
- "5": {
- "question": "Qual è il livello della via più facile qua, secondo il sistema di classificazione francese?",
- "render": "Il minimo livello di difficoltà è {climbing:grade:french:min} secondo il sistema francese/belga"
- },
- "6": {
- "question": "Qual è il livello della via più difficile qua, secondo il sistema di classificazione francese?",
- "render": "Il massimo livello di difficoltà è {climbing:grade:french:max} secondo il sistema francese/belga"
- },
- "7": {
- "mappings": {
+ "units+": {
"0": {
- "then": "L’arrampicata su massi è possibile qua"
- },
- "1": {
- "then": "L’arrampicata su massi non è possibile qua"
- },
- "2": {
- "then": "L’arrampicata su massi è possibile anche se su poche vie"
- },
- "3": {
- "then": "Sono presenti {climbing:boulder} vie di arrampicata su massi"
- }
- },
- "question": "È possibile praticare ‘bouldering’ qua?"
- },
- "8": {
- "mappings": {
- "0": {
- "then": "È possibile arrampicarsi con moulinette qua"
- },
- "1": {
- "then": "Non è possibile arrampicarsi con moulinette qua"
- },
- "2": {
- "then": "Sono presenti {climbing:toprope} vie con moulinette"
- }
- },
- "question": "È possibile arrampicarsi con la corda dall’alto qua?"
- },
- "9": {
- "mappings": {
- "0": {
- "then": "L’arrampicata sportiva è possibile qua"
- },
- "1": {
- "then": "L’arrampicata sportiva non è possibile qua"
- },
- "2": {
- "then": "Sono presenti {climbing:sport} vie di arrampicata sportiva"
- }
- },
- "question": "È possibile arrampicarsi qua con ancoraggi fissi?"
- },
- "10": {
- "mappings": {
- "0": {
- "then": "L’arrampicata tradizionale è possibile qua"
- },
- "1": {
- "then": "L’arrampicata tradizionale non è possibile qua"
- },
- "2": {
- "then": "Sono presenti {climbing:traditional} vie di arrampicata tradizionale"
- }
- },
- "question": "È possibile arrampicarsi in maniera tradizionale qua (usando attrezzi propri, ad es. dadi)?"
- },
- "11": {
- "mappings": {
- "0": {
- "then": "È presente una parete per l’arrampicata di velocità"
- },
- "1": {
- "then": "Non è presente una parete per l’arrampicata di velocità"
- },
- "2": {
- "then": "Sono presenti {climbing:speed} pareti per l’arrampicata di velocità"
- }
- },
- "question": "È presente una prete per l’arrampicata di velocità?"
- }
- },
- "units+": {
- "0": {
- "applicableUnits": {
- "0": {
- "human": " metri"
- },
- "1": {
- "human": " piedi"
+ "applicableUnits": {
+ "0": {
+ "human": " metri"
+ },
+ "1": {
+ "human": " piedi"
+ }
+ }
}
}
}
diff --git a/langs/themes/ja.json b/langs/themes/ja.json
index 35b0d41efd..fc80489e40 100644
--- a/langs/themes/ja.json
+++ b/langs/themes/ja.json
@@ -255,98 +255,6 @@
"climbing": {
"description": "この地図には、自然の中のクライミングジム、ボルダリングホール、岩など、さまざまなクライミングの機会があります。",
"descriptionTail": "登山地図はもともと Christian Neumann によって作成されたものです。フィードバックや質問がありましたら、ご連絡 ください。このプロジェクトでは、OpenStreetMap プロジェクトのデータを使用します。
",
- "overrideAll": {
- "tagRenderings+": {
- "0": {
- "question": "もっと情報のある(非公式の)ウェブサイトはありますか(例えば、topos)?"
- },
- "4": {
- "question": "ルートの(平均)長さはメートル単位でいくつですか?",
- "render": "ルートの長さは平均で{canonical(climbing:length)} です"
- },
- "5": {
- "question": "ここで一番簡単なルートのレベルは、フランスのランク評価システムで何ですか?",
- "render": "フランス/ベルギーのランク評価システムでは、最小の難易度は{climbing:grade:french:min}です"
- },
- "6": {
- "question": "フランスのランク評価によると、ここで一番難しいルートのレベルはどれくらいですか?",
- "render": "フランス/ベルギーのランク評価システムでは、最大の難易度は{climbing:grade:french:max}です"
- },
- "7": {
- "mappings": {
- "0": {
- "then": "ボルダリングはここで可能です"
- },
- "1": {
- "then": "ここではボルダリングはできません"
- },
- "2": {
- "then": "ボルダリングは可能ですが、少しのルートしかありません"
- },
- "3": {
- "then": "{climbing:boulder} ボルダールートがある"
- }
- },
- "question": "ここでボルダリングはできますか?"
- },
- "8": {
- "mappings": {
- "0": {
- "then": "ここでToprope登坂ができます"
- },
- "1": {
- "then": "ここではToprope登坂はできません"
- },
- "2": {
- "then": "{climbing:toprope} 登坂ルートがある"
- }
- },
- "question": "ここでtoprope登坂はできますか?"
- },
- "9": {
- "mappings": {
- "0": {
- "then": "ここでスポーツクライミングができます"
- },
- "1": {
- "then": "ここではスポーツクライミングはできません"
- },
- "2": {
- "then": "スポーツクライミングの {climbing:sport} ルートがある"
- }
- },
- "question": "ここでは固定アンカー式のスポーツクライミングはできますか?"
- },
- "10": {
- "mappings": {
- "0": {
- "then": "ここでは伝統的な登山が可能です"
- },
- "1": {
- "then": "伝統的な登山はここではできない"
- },
- "2": {
- "then": "{climbing:traditional} の伝統的な登山ルートがある"
- }
- },
- "question": "伝統的な登山はここで可能ですか(例えば、チョックのような独自のギアを使用して)?"
- },
- "11": {
- "mappings": {
- "0": {
- "then": "スピードクライミングウォールがある"
- },
- "1": {
- "then": "スピードクライミングウォールがない"
- },
- "2": {
- "then": "{climbing:speed} のスピードクライミングウォールがある"
- }
- },
- "question": "スピードクライミングウォールはありますか?"
- }
- }
- },
"title": "登山地図を開く"
},
"cyclestreets": {
diff --git a/langs/themes/nb_NO.json b/langs/themes/nb_NO.json
index 12fd79fccf..42c134b0a5 100644
--- a/langs/themes/nb_NO.json
+++ b/langs/themes/nb_NO.json
@@ -69,21 +69,6 @@
"title": "Ladestasjoner"
},
"climbing": {
- "overrideAll": {
- "tagRenderings+": {
- "7": {
- "mappings": {
- "0": {
- "then": "Buldring er mulig her"
- },
- "1": {
- "then": "Buldring er ikke mulig her"
- }
- },
- "question": "Er buldring mulig her?"
- }
- }
- },
"title": "Åpent klatrekart"
},
"cycle_infra": {
diff --git a/langs/themes/nl.json b/langs/themes/nl.json
index deab1b60c3..535e63ac47 100644
--- a/langs/themes/nl.json
+++ b/langs/themes/nl.json
@@ -275,115 +275,31 @@
"climbing": {
"description": "Op deze kaart vind je verschillende klimgelegenheden, zoals klimzalen, bolderzalen en klimmen in de natuur",
"descriptionTail": "De klimkaart is oorspronkelijk gemaakt door Christian Neumann op kletterspots.de .",
- "overrideAll": {
- "tagRenderings+": {
- "0": {
- "question": "Is er een (onofficiële) website met meer informatie (b.v. met topos)?"
- },
- "1": {
- "mappings": {
+ "layers": {
+ "0": {
+ "override": {
+ "tagRenderings+": {
"0": {
- "then": "Een omvattend element geeft aan dat dit publiek toegangkelijk is {_embedding_feature:access:description}"
- },
- "1": {
- "then": "Een omvattend element geeft aan dat een toelating nodig is om hier te klimmen {_embedding_feature:access:description}"
- }
- }
- },
- "4": {
- "question": "Wat is de (gemiddelde) lengte van de klimroutes, in meter?",
- "render": "De klimroutes zijn gemiddeld {canonical(climbing:length)} lang"
- },
- "5": {
- "question": "Wat is het niveau van de makkelijkste route, volgens het Franse classificatiesysteem?",
- "render": "De minimale klimmoeilijkheid is {climbing:grade:french:min} volgens het Franse/Belgische systeem"
- },
- "6": {
- "question": "Wat is het niveau van de moeilijkste route, volgens het Franse classificatiesysteem?",
- "render": "De maximale klimmoeilijkheid is {climbing:grade:french:max} volgens het Franse/Belgische systeem"
- },
- "7": {
- "mappings": {
- "0": {
- "then": "Bolderen kan hier"
- },
- "1": {
- "then": "Bolderen kan hier niet"
- },
- "2": {
- "then": "Bolderen kan hier, maar er zijn niet zoveel routes"
- },
- "3": {
- "then": "Er zijn hier {climbing:boulder} bolderroutes"
+ "mappings": {
+ "0": {
+ "then": "Een omvattend element geeft aan dat dit publiek toegangkelijk is {_embedding_feature:access:description}"
+ },
+ "1": {
+ "then": "Een omvattend element geeft aan dat een toelating nodig is om hier te klimmen {_embedding_feature:access:description}"
+ }
+ }
}
},
- "question": "Is het mogelijk om hier te bolderen?"
- },
- "8": {
- "mappings": {
+ "units+": {
"0": {
- "then": "Toprope-klimmen kan hier"
- },
- "1": {
- "then": "Toprope-klimmen kan hier niet"
- },
- "2": {
- "then": "Er zijn hier {climbing:toprope} toprope routes"
- }
- },
- "question": "Is het mogelijk om hier te toprope-klimmen?"
- },
- "9": {
- "mappings": {
- "0": {
- "then": "Sportklimmen/voorklimmen kan hier"
- },
- "1": {
- "then": "Sportklimmen/voorklimmen kan hier niet"
- },
- "2": {
- "then": "Er zijn hier {climbing:sport} sportklimroutes/voorklimroutes"
- }
- },
- "question": "Is het mogelijk om hier te sportklimmen/voorklimmen op reeds aangebrachte haken?"
- },
- "10": {
- "mappings": {
- "0": {
- "then": "Traditioneel klimmen kan hier"
- },
- "1": {
- "then": "Traditioneel klimmen kan hier niet"
- },
- "2": {
- "then": "Er zijn hier {climbing:traditional} traditionele klimroutes"
- }
- },
- "question": "Is het mogelijk om hier traditioneel te klimmen? (Dit is klimmen met klemblokjes en friends) "
- },
- "11": {
- "mappings": {
- "0": {
- "then": "Er is een snelklimmuur voor speed climbing"
- },
- "1": {
- "then": "Er is geen snelklimmuur voor speed climbing"
- },
- "2": {
- "then": "Er zijn hier {climbing:speed} snelklimmuren"
- }
- },
- "question": "Is er een snelklimmuur (speed climbing)?"
- }
- },
- "units+": {
- "0": {
- "applicableUnits": {
- "0": {
- "human": " meter"
- },
- "1": {
- "human": " voet"
+ "applicableUnits": {
+ "0": {
+ "human": " meter"
+ },
+ "1": {
+ "human": " voet"
+ }
+ }
}
}
}
diff --git a/langs/themes/ru.json b/langs/themes/ru.json
index 07e2e72901..800a312d13 100644
--- a/langs/themes/ru.json
+++ b/langs/themes/ru.json
@@ -227,34 +227,25 @@
"climbing": {
"description": "На этой карте вы найдете различные возможности для скалолазания, такие как скалодромы, залы для боулдеринга и скалы на природе.",
"descriptionTail": "Создатель карты скалолазания — Christian Neumann . Пожалуйста, пишите если у вас есть отзыв или вопросы.Проект использует данные OpenStreetMap .
",
- "overrideAll": {
- "tagRenderings+": {
- "0": {
- "question": "Есть ли (неофициальный) веб-сайт с более подробной информацией (напр., topos)?"
- },
- "2": {
- "mappings": {
- "3": {
- "then": "Только членам клуба"
- }
- }
- },
- "9": {
- "mappings": {
- "0": {
- "then": "Здесь можно заняться спортивным скалолазанием"
- },
+ "layers": {
+ "0": {
+ "override": {
+ "tagRenderings+": {
"1": {
- "then": "Спортивное скалолазание здесь невозможно"
+ "mappings": {
+ "3": {
+ "then": "Только членам клуба"
+ }
+ }
}
- }
- }
- },
- "units+": {
- "0": {
- "applicableUnits": {
+ },
+ "units+": {
"0": {
- "human": " метр"
+ "applicableUnits": {
+ "0": {
+ "human": " метр"
+ }
+ }
}
}
}
From 9086713657cc7b337b5b819570c338cbe13f4be6 Mon Sep 17 00:00:00 2001
From: pietervdvn
Date: Sun, 24 Apr 2022 16:10:17 +0200
Subject: [PATCH 11/27] Add 'subs' to title of IMGUR images, fixes #771
---
UI/Image/ImageUploadFlow.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/UI/Image/ImageUploadFlow.ts b/UI/Image/ImageUploadFlow.ts
index 47e775289b..a696b553e6 100644
--- a/UI/Image/ImageUploadFlow.ts
+++ b/UI/Image/ImageUploadFlow.ts
@@ -112,7 +112,7 @@ export class ImageUploadFlow extends Toggle {
}
- const title = matchingLayer?.title?.GetRenderValue(tags)?.ConstructElement()?.innerText ?? tags.name ?? "Unknown area";
+ const title = matchingLayer?.title?.GetRenderValue(tags)?.Subs(tags)?.ConstructElement()?.innerText ?? tags.name ?? "https//osm.org/"+tags.id;
const description = [
"author:" + state.osmConnection.userDetails.data.name,
"license:" + license,
From a22ff2f36b50beff89975d85521ebf63cf7dfada Mon Sep 17 00:00:00 2001
From: pietervdvn
Date: Tue, 26 Apr 2022 10:30:19 +0200
Subject: [PATCH 12/27] More climbing styling
---
.../layers/climbing_area/climbing_area.json | 2 +-
.../layers/climbing_route/climbing_route.json | 11 +-
assets/themes/climbing/climbing.css | 44 ++++++++
assets/themes/climbing/climbing.json | 100 +++---------------
assets/themes/climbing/css-test.html | 23 ++++
5 files changed, 91 insertions(+), 89 deletions(-)
create mode 100644 assets/themes/climbing/climbing.css
create mode 100644 assets/themes/climbing/css-test.html
diff --git a/assets/layers/climbing_area/climbing_area.json b/assets/layers/climbing_area/climbing_area.json
index 22c0d28844..22b694ba5a 100644
--- a/assets/layers/climbing_area/climbing_area.json
+++ b/assets/layers/climbing_area/climbing_area.json
@@ -93,7 +93,7 @@
},
"calculatedTags": [
"_contained_climbing_routes_properties=feat.overlapWith('climbing_route').map(f => f.feat.properties).map(p => {return {id: p.id, name: p.name, 'climbing:grade:french': p['climbing:grade:french'], 'climbing:length': p['climbing:length']} })",
- "_contained_climbing_routes=feat.get('_contained_climbing_routes_properties')?.map(p => `${p.name ?? 'climbing route'} (${p['climbing:grade:french'] ?? 'unknown difficulty'} , ${p['climbing:length'] ?? 'unkown length'} meter) `).join('')",
+ "_contained_climbing_routes=feat.get('_contained_climbing_routes_properties')?.map(p => `${p.name ?? 'climbing route'} (${p['climbing:grade:french'] ?? 'unknown difficulty'} , ${p['climbing:length'] ?? 'unkown length'} meter) `).join('')",
"_contained_climbing_route_ids=feat.get('_contained_climbing_routes_properties')?.map(p => p.id)",
"_difficulty_hist=feat.get('_contained_climbing_routes_properties')?.map(p => p['climbing:grade:french'])?.filter(p => (p ?? null) !== null)?.sort()",
"_difficulty_max=feat.get('_difficulty_hist')?.at(-1)",
diff --git a/assets/layers/climbing_route/climbing_route.json b/assets/layers/climbing_route/climbing_route.json
index a562c753c1..d95d5748b2 100644
--- a/assets/layers/climbing_route/climbing_route.json
+++ b/assets/layers/climbing_route/climbing_route.json
@@ -217,9 +217,18 @@
],
"label": {
"mappings": [
+ {
+ "if": {
+ "and": [
+ "climbing:grade:french~*",
+ "name~*"
+ ]
+ },
+ "then": "{name} {climbing:grade:french}
"
+ },
{
"if": "name~*",
- "then": "{name}
"
+ "then": "{name}
"
}
]
}
diff --git a/assets/themes/climbing/climbing.css b/assets/themes/climbing/climbing.css
new file mode 100644
index 0000000000..b27086c6f0
--- /dev/null
+++ b/assets/themes/climbing/climbing.css
@@ -0,0 +1,44 @@
+/* a few extra colours, mostly colours difficulties. Debuggable in css-test.html */
+
+.climbing- {
+ /*Fallback in case of unrecognized difficulty*/
+ background: white;
+ border: 1px solid black;
+}
+
+.climbing-2 {
+ background: #a2ff00;
+}
+
+.climbing-3 {
+ background: #a2ff00;
+}
+
+.climbing-4 {
+ background: yellow;
+}
+
+.climbing-5 {
+ background: blue;
+ color: white;
+}
+
+.climbing-6 {
+ background: red;
+ color: white;
+}
+
+.climbing-7 {
+ background: fuchsia;
+}
+
+.climbing-8 {
+ background: black;
+ color: white;
+}
+
+.climbing-9 {
+ background: black;
+ color: white;
+}
+
diff --git a/assets/themes/climbing/climbing.json b/assets/themes/climbing/climbing.json
index 043485b54d..66a53b1820 100644
--- a/assets/themes/climbing/climbing.json
+++ b/assets/themes/climbing/climbing.json
@@ -42,16 +42,17 @@
"startLon": 0,
"startZoom": 1,
"widenFactor": 1.5,
+ "customCss": "./assets/themes/climbing/climbing.css",
"layers": [
{
"builtin": [
- "climbing_club",
- "climbing_gym",
- "climbing_route",
- "climbing_area",
- "climbing_opportunity"
+ "climbing_club",
+ "climbing_gym",
+ "climbing_route",
+ "climbing_area",
+ "climbing_opportunity"
],
- "override": {
+ "override": {
"allowMove": {
"enableRelocation": false,
"enableImproveAccuracy": true
@@ -76,93 +77,15 @@
{
"id": "Min difficulty",
"condition": "__difficulty_min~*",
- "then": " {__difficulty_min}
",
-
- "mappings": [
- {
- "if": "__difficulty_min~(2|3).*",
- "then": " {__difficulty_min}
"
- },
- {
- "if": "__difficulty_min~4.*",
- "then": " {__difficulty_min}
"
- },
- {
- "if": "__difficulty_min~5.*",
- "then": " {__difficulty_min}
"
- },
- {
- "if": "__difficulty_min~6.*",
- "then": " {__difficulty_min}
"
- },
- {
- "if": "__difficulty_min~(7|8).*",
- "then": " {__difficulty_min}
"
- },
- {
- "if": "__difficulty_min~*",
- "then": " {__difficulty_min}
"
- }
- ]
+ "render": " {__difficulty_min}
"
},
{
"id": "max difficulty",
"condition": "__difficulty_max~*",
- "then": " {__difficulty_max}
",
- "mappings": [
- {
- "if": "__difficulty_max~(2|3).*",
- "then": " {__difficulty_max}
"
- },
- {
- "if": "__difficulty_max~4.*",
- "then": " {__difficulty_max}
"
- },
- {
- "if": "__difficulty_max~5.*",
- "then": " {__difficulty_max}
"
- },
- {
- "if": "__difficulty_max~6.*",
- "then": " {__difficulty_max}
"
- },
- {
- "if": "__difficulty_max~(7|8).*",
- "then": " {__difficulty_max}
"
- },
- {
- "if": "__difficulty_max~*",
- "then": " {__difficulty_max}
"
- }
- ]
+ "render": " {__difficulty_max}
"
},
{
- "mappings": [
- {
- "if": "climbing:grade:french~3.*",
- "then": " {climbing:grade:french}
"
- },
- {
- "if": "climbing:grade:french~4.*",
- "then": " {climbing:grade:french}
"
- },
- {
- "if": "climbing:grade:french~5.*",
- "then": " {climbing:grade:french}
"
- },
- {
- "if": "climbing:grade:french~6.*",
- "then": " {climbing:grade:french}
"
- },
- {
- "if": "climbing:grade:french~7.*",
- "then": " {climbing:grade:french}
"
- },
- {
- "if": "climbing:grade:french~*",
- "then": " {climbing:grade:french}
"
- }
- ]
+ "render": " {climbing:grade:french}
"
}
],
"+calculatedTags": [
@@ -176,6 +99,9 @@
"_embedding_feature:id=feat.get('_embedding_features_with_access')?.id",
"__difficulty_max= feat.properties['climbing:grade:french:max'] ?? feat.properties['_difficulty_max']",
"__difficulty_min= feat.properties['climbing:grade:french:min'] ?? feat.properties['_difficulty_min']",
+ "__difficulty_max:char= feat.properties['__difficulty_max']?.at(0)",
+ "__difficulty_min:char= feat.properties['__difficulty_min']?.at(0)",
+ "__difficulty:char= feat.properties['climbing:grade:french']?.at(0)",
"__bolts_max= feat.get('climbing:bolts:max') ?? feat.get('climbing:bolts') ?? feat.get('_bolts_max')"
],
"units+": [
diff --git a/assets/themes/climbing/css-test.html b/assets/themes/climbing/css-test.html
new file mode 100644
index 0000000000..f968878139
--- /dev/null
+++ b/assets/themes/climbing/css-test.html
@@ -0,0 +1,23 @@
+
+
+
+
+ CSS-debugging for climbing theme
+
+
+
+
+
+ 2
+
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ X
+
+
+
\ No newline at end of file
From b2c8c2e7a3decda9c4807925165ca1baf731e5de Mon Sep 17 00:00:00 2001
From: pietervdvn
Date: Tue, 26 Apr 2022 10:31:43 +0200
Subject: [PATCH 13/27] Move import tag into doctest
---
Logic/Osm/Overpass.ts | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/Logic/Osm/Overpass.ts b/Logic/Osm/Overpass.ts
index 5463770acd..b2205cb363 100644
--- a/Logic/Osm/Overpass.ts
+++ b/Logic/Osm/Overpass.ts
@@ -4,8 +4,6 @@ import {Utils} from "../../Utils";
import {UIEventSource} from "../UIEventSource";
import {BBox} from "../BBox";
import * as osmtogeojson from "osmtogeojson";
-// @ts-ignore
-import {Tag} from "../Tags/Tag"; // used in doctest
/**
* Interfaces overpass to get all the latest data
@@ -58,6 +56,10 @@ export class Overpass {
}
/**
+ * Constructs the actual script
+ *
+ * import {Tag} from "../Tags/Tag";
+ *
* new Overpass(new Tag("key","value"), [], "").buildScript("{{bbox}}") // => `[out:json][timeout:90]{{bbox}};(nwr["key"="value"];);out body;out meta;>;out skel qt;`
*/
public buildScript(bbox: string, postCall: string = "", pretty = false): string {
From 4ee03bb130bf18abac13a250fca943d7b6a72d61 Mon Sep 17 00:00:00 2001
From: pietervdvn
Date: Tue, 26 Apr 2022 10:31:58 +0200
Subject: [PATCH 14/27] Remove unneeded import
---
Models/ThemeConfig/LayerConfig.ts | 1 -
1 file changed, 1 deletion(-)
diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts
index 5d95d3cb2c..1f500b994e 100644
--- a/Models/ThemeConfig/LayerConfig.ts
+++ b/Models/ThemeConfig/LayerConfig.ts
@@ -27,7 +27,6 @@ import FilterConfigJson from "./Json/FilterConfigJson";
import {And} from "../../Logic/Tags/And";
import {Overpass} from "../../Logic/Osm/Overpass";
import Constants from "../Constants";
-import undefinedError = Mocha.utils.undefinedError;
export default class LayerConfig extends WithContextLoader {
From 88ad297662f6732ac65a8511aec0184f32a8d0d4 Mon Sep 17 00:00:00 2001
From: pietervdvn
Date: Tue, 26 Apr 2022 10:32:47 +0200
Subject: [PATCH 15/27] Fix loading of custom css
---
assets/themes/natuurpunt/natuurpunt.css | 6 +++---
assets/themes/toerisme_vlaanderen/custom.css | 4 ++--
package.json | 2 +-
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/assets/themes/natuurpunt/natuurpunt.css b/assets/themes/natuurpunt/natuurpunt.css
index 1839fd8962..a8952f42ea 100644
--- a/assets/themes/natuurpunt/natuurpunt.css
+++ b/assets/themes/natuurpunt/natuurpunt.css
@@ -20,12 +20,12 @@
@font-face{
font-family:"Open Sans Regular";
- src:url("./assets/themes/natuurpunt/fonts/OpenSans-Regular.ttf");
+ src:url("/assets/themes/natuurpunt/fonts/OpenSans-Regular.ttf");
}
@font-face{
font-family:"Amaranth";
- src:url("./assets/themes/natuurpunt/fonts/Amaranth-Regular.otf");
+ src:url("/assets/themes/natuurpunt/fonts/Amaranth-Regular.otf");
}
body {
@@ -109,4 +109,4 @@ h1, h2, h3, h4 {
.first-filter-panel {
/* Additional class on the first layer filter */
border-top: unset !important;
-}
\ No newline at end of file
+}
diff --git a/assets/themes/toerisme_vlaanderen/custom.css b/assets/themes/toerisme_vlaanderen/custom.css
index 2ea3bdf21e..27a4894ba6 100644
--- a/assets/themes/toerisme_vlaanderen/custom.css
+++ b/assets/themes/toerisme_vlaanderen/custom.css
@@ -4,12 +4,12 @@
@font-face{
font-family:"FlandersArt";
- src:url("./assets/themes/toerisme_vlaanderen/FlandersArtSans-Light.woff");
+ src:url("/assets/themes/toerisme_vlaanderen/FlandersArtSans-Light.woff");
}
@font-face{
font-family:"FlandersArtSerif";
- src:url("./assets/themes/toerisme_vlaanderen/FlandersArtSerif-Medium.woff");
+ src:url("/assets/themes/toerisme_vlaanderen/FlandersArtSerif-Medium.woff");
}
h1, h2, h3, h4 {
diff --git a/package.json b/package.json
index 1488ede86b..6b4ef97b7e 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,7 @@
"main": "index.js",
"scripts": {
"start": "npm run generate:layeroverview && npm run strt",
- "strt": "export NODE_OPTIONS=--max_old_space_size=8364 && parcel serve *.html UI/** Logic/** assets/*.json assets/svg/* assets/generated/* assets/layers/*/*.svg assets/layers/*/*/*/*.svg assets/layers/*/*.jpg assets/layers/*/*.png assets/layers/*/*.css assets/tagRenderings/*.json assets/themes/*/*.svg assets/themes/*/*.ttf assets/themes/*/*/*.ttf aassets/themes/*/*.otf assets/themes/*/*/*.otf ssets/themes/*/*.css assets/themes/*/*.jpg assets/themes/*/*.png vendor/* vendor/*/*",
+ "strt": "export NODE_OPTIONS=--max_old_space_size=8364 && parcel serve *.html UI/** Logic/** assets/*.json assets/svg/* assets/generated/* assets/layers/*/*.svg assets/layers/*/*/*/*.svg assets/layers/*/*.jpg assets/layers/*/*.png assets/layers/*/*.css assets/tagRenderings/*.json assets/themes/*/*.svg assets/themes/*/*.ttf assets/themes/*/*/*.ttf assets/themes/*/*.otf assets/themes/*/*/*.otf assets/themes/*/*.css assets/themes/*/*.jpg assets/themes/*/*.woff assets/themes/*/*.png vendor/* vendor/*/*",
"strttest": "export NODE_OPTIONS=--max_old_space_size=8364 && parcel serve test.html",
"watch:css": "tailwindcss -i index.css -o css/index-tailwind-output.css --watch",
"generate:css": "tailwindcss -i index.css -o css/index-tailwind-output.css",
From fe3e73d3a25496fc2638d9ab9ece6ab520fb5930 Mon Sep 17 00:00:00 2001
From: pietervdvn
Date: Tue, 26 Apr 2022 14:54:01 +0200
Subject: [PATCH 16/27] Fix filtered layers if no description is known
---
UI/BigComponents/FilterView.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/UI/BigComponents/FilterView.ts b/UI/BigComponents/FilterView.ts
index 092cd31ec1..01f4ff29ff 100644
--- a/UI/BigComponents/FilterView.ts
+++ b/UI/BigComponents/FilterView.ts
@@ -92,9 +92,9 @@ export default class FilterView extends VariableUiElement {
if (filteredLayer.layerDef.name === undefined) {
// Name is not defined: we hide this one
return new Toggle(
- filteredLayer.layerDef.description.Clone().SetClass("subtle") ,
+ filteredLayer?.layerDef?.description?.Clone()?.SetClass("subtle") ,
undefined,
- state.featureSwitchIsDebugging
+ state?.featureSwitchIsDebugging
);
}
const iconStyle = "width:1.5rem;height:1.5rem;margin-left:1.25rem;flex-shrink: 0;";
From 2b9d3a545d1695c98362f2ed312cbd078bc171b4 Mon Sep 17 00:00:00 2001
From: pietervdvn
Date: Wed, 27 Apr 2022 14:39:58 +0200
Subject: [PATCH 17/27] Add screenshots for slides
---
Docs/Misc/ImportANote.gif | Bin 2448420 -> 0 bytes
Docs/Misc/ImportAPoint.gif | Bin 0 -> 2623723 bytes
Docs/Screenshots/AED.png | Bin 0 -> 1699824 bytes
Docs/Screenshots/AddNew.png | Bin 0 -> 236475 bytes
Docs/Screenshots/Cyclestreets.png | Bin 0 -> 1749634 bytes
Docs/Screenshots/Cyclofix.png | Bin 0 -> 1449957 bytes
Docs/Screenshots/Fritures.png | Bin 0 -> 1659032 bytes
Docs/Screenshots/PinJePunt.png | Bin 0 -> 250816 bytes
Docs/Screenshots/Playground-popup-bottom.png | Bin 0 -> 105197 bytes
Docs/Screenshots/Playground-popup-top.png | Bin 0 -> 353137 bytes
Docs/Screenshots/Toilets.png | Bin 0 -> 1771234 bytes
Docs/Screenshots/collage.png | Bin 0 -> 1585214 bytes
12 files changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 Docs/Misc/ImportANote.gif
create mode 100644 Docs/Misc/ImportAPoint.gif
create mode 100644 Docs/Screenshots/AED.png
create mode 100644 Docs/Screenshots/AddNew.png
create mode 100644 Docs/Screenshots/Cyclestreets.png
create mode 100644 Docs/Screenshots/Cyclofix.png
create mode 100644 Docs/Screenshots/Fritures.png
create mode 100644 Docs/Screenshots/PinJePunt.png
create mode 100644 Docs/Screenshots/Playground-popup-bottom.png
create mode 100644 Docs/Screenshots/Playground-popup-top.png
create mode 100644 Docs/Screenshots/Toilets.png
create mode 100644 Docs/Screenshots/collage.png
diff --git a/Docs/Misc/ImportANote.gif b/Docs/Misc/ImportANote.gif
deleted file mode 100644
index f41b96f636cf60d7c7429d9144aaba87b5860f8e..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 2448420
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 =DW6UYaSeQbOiH4L?
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