From 65da7155fa3a0c8705f82f7646a44076be04fd73 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 11 Jan 2024 02:25:13 +0100 Subject: [PATCH 001/195] Tests: validate checksums in all files at once --- test/CodeQuality.spec.ts | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/test/CodeQuality.spec.ts b/test/CodeQuality.spec.ts index 205701af5..ed8815ffb 100644 --- a/test/CodeQuality.spec.ts +++ b/test/CodeQuality.spec.ts @@ -16,7 +16,7 @@ function detectInCode(forbidden: string, reason: string) { * @private */ function detectInCodeUnwrapped(forbidden: string, reason: string): Promise { - return new Promise((done) => { + return new Promise(() => { const excludedDirs = [ ".git", "node_modules", @@ -29,9 +29,9 @@ function detectInCodeUnwrapped(forbidden: string, reason: string): Promise ] const command = - 'grep -n "' + + "grep -n \"" + forbidden + - '" -r . ' + + "\" -r . " + excludedDirs.map((d) => "--exclude-dir=" + d).join(" ") console.log(command) exec(command, (error, stdout, stderr) => { @@ -81,6 +81,7 @@ async function validateScriptIntegrityOf(path: string): Promise { const doc = parse_html(htmlContents) // @ts-ignore const scripts = Array.from(doc.getElementsByTagName("script")) + const failed = new Set() for (const script of scripts) { let src = script.getAttribute("src") if (src === undefined) { @@ -106,12 +107,15 @@ async function validateScriptIntegrityOf(path: string): Promise { const data: string = (await ScriptUtils.Download(src))["content"] const hashed = await webcrypto.subtle.digest("SHA-384", new TextEncoder().encode(data)) const hashedStr = _arrayBufferToBase64(hashed) - console.log(src, hashedStr, integrity) - expect(integrity).to.equal( - "sha384-" + hashedStr, - "Loading a script from '" + src + "' in the file " + path + " has a mismatched checksum" - ) + + const expected = "sha384-" + hashedStr + if (expected !== integrity) { + const msg = "Loading a script from '" + src + "' in the file " + path + " has a mismatched checksum: expected " + expected + " but the HTML-file contains " + integrity + failed.add(msg) + console.warn(msg) + } } + expect(Array.from(failed).join("\n")).to.equal("") } describe("Code quality", () => { @@ -119,21 +123,21 @@ describe("Code quality", () => { "should not contain reverse", detectInCode( "reverse()", - "Reverse is stateful and changes the source list. This often causes subtle bugs" - ) + "Reverse is stateful and changes the source list. This often causes subtle bugs", + ), ) it( "should not contain 'constructor.name'", - detectInCode("constructor\\.name", "This is not allowed, as minification does erase names.") + detectInCode("constructor\\.name", "This is not allowed, as minification does erase names."), ) it( "should not contain 'innerText'", detectInCode( "innerText", - "innerText is not allowed as it is not testable with fakeDom. Use 'textContent' instead." - ) + "innerText is not allowed as it is not testable with fakeDom. Use 'textContent' instead.", + ), ) test("scripts with external sources should have an integrity hash", async () => { From ac9792ac434aaefd40ecfae2a46b4034e5b9b169 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 11 Jan 2024 05:10:04 +0100 Subject: [PATCH 002/195] Docs: improve comments, error checking and defaults --- .../ThemeConfig/Conversion/PrepareTheme.ts | 2 +- .../ThemeConfig/Conversion/Validation.ts | 4 ++ src/Models/ThemeConfig/TagRenderingConfig.ts | 2 +- src/UI/InputElement/Validators.ts | 3 ++ .../Validators/VeloparkValidator.ts | 37 +++++++++++++++++++ .../TagRendering/TagRenderingMapping.svelte | 2 +- 6 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 src/UI/InputElement/Validators/VeloparkValidator.ts diff --git a/src/Models/ThemeConfig/Conversion/PrepareTheme.ts b/src/Models/ThemeConfig/Conversion/PrepareTheme.ts index 0da7d76c2..c369ec378 100644 --- a/src/Models/ThemeConfig/Conversion/PrepareTheme.ts +++ b/src/Models/ThemeConfig/Conversion/PrepareTheme.ts @@ -535,7 +535,7 @@ export class PrepareTheme extends Fuse { new SetDefault("socialImage", "assets/SocialImage.png", true), // We expand all tagrenderings first... new On("layers", new Each(new PrepareLayer(state))), - // Then we apply the override all + // Then we apply the override all. We must first expand everything in case that we override something in an expanded tag new ApplyOverrideAll(), // And then we prepare all the layers _again_ in case that an override all contained unexpanded tagrenderings! new On("layers", new Each(new PrepareLayer(state))), diff --git a/src/Models/ThemeConfig/Conversion/Validation.ts b/src/Models/ThemeConfig/Conversion/Validation.ts index 2f46cc3c1..89235b9b0 100644 --- a/src/Models/ThemeConfig/Conversion/Validation.ts +++ b/src/Models/ThemeConfig/Conversion/Validation.ts @@ -882,6 +882,10 @@ class MiscTagRenderingChecks extends DesugaringStep { ) } + if(json.icon?.["size"]){ + context.enters("icon","size").err("size is not a valid attribute. Did you mean 'class'? Class can be one of `small`, `medium` or `large`") + } + if (json.freeform) { if (json.render === undefined) { context diff --git a/src/Models/ThemeConfig/TagRenderingConfig.ts b/src/Models/ThemeConfig/TagRenderingConfig.ts index 06106a7de..728644602 100644 --- a/src/Models/ThemeConfig/TagRenderingConfig.ts +++ b/src/Models/ThemeConfig/TagRenderingConfig.ts @@ -153,7 +153,7 @@ export default class TagRenderingConfig { this.renderIconClass = "small" } else if (typeof json.icon === "object") { this.renderIcon = json.icon.path - this.renderIconClass = json.icon.class + this.renderIconClass = json.icon.class ?? "small" } this.metacondition = TagUtils.Tag( json.metacondition ?? { and: [] }, diff --git a/src/UI/InputElement/Validators.ts b/src/UI/InputElement/Validators.ts index 4fa0eabaf..d491fe060 100644 --- a/src/UI/InputElement/Validators.ts +++ b/src/UI/InputElement/Validators.ts @@ -27,6 +27,7 @@ import IconValidator from "./Validators/IconValidator" import TagValidator from "./Validators/TagValidator" import IdValidator from "./Validators/IdValidator" import SlopeValidator from "./Validators/SlopeValidator" +import VeloparkValidator from "./Validators/VeloparkValidator" export type ValidatorType = (typeof Validators.availableTypes)[number] @@ -58,6 +59,7 @@ export default class Validators { "fediverse", "id", "slope", + "velopark" ] as const public static readonly AllValidators: ReadonlyArray = [ @@ -86,6 +88,7 @@ export default class Validators { new FediverseValidator(), new IdValidator(), new SlopeValidator(), + new VeloparkValidator() ] private static _byType = Validators._byTypeConstructor() diff --git a/src/UI/InputElement/Validators/VeloparkValidator.ts b/src/UI/InputElement/Validators/VeloparkValidator.ts new file mode 100644 index 000000000..b3f1466bb --- /dev/null +++ b/src/UI/InputElement/Validators/VeloparkValidator.ts @@ -0,0 +1,37 @@ +import { Translation } from "../../i18n/Translation" +import UrlValidator from "./UrlValidator" + +export default class VeloparkValidator extends UrlValidator { + constructor() { + super("velopark", "A custom element to allow copy-pasting velopark-pages") + } + + getFeedback(s: string): Translation { + const superF = super.getFeedback(s) + if (superF) { + return superF + } + const url = new URL(s) + if (url.hostname !== "velopark.be" && url.hostname !== "www.velopark.be" && url.hostname !== "data.velopark.be") { + return new Translation({ "*": "Invalid hostname, expected velopark.be" }) + } + + if(!s.startsWith("https://data.velopark.be/data/") && !s.startsWith("https://www.velopark.be/static/data/")){ + return new Translation({"*":"A valid URL should either start with https://data.velopark.be/data/ or https://www.velopark.be/static/data/"}) + } + + } + + public isValid(str: string) { + return super.isValid(str) + } + + reformat(str: string): string { + const url = new URL(super.reformat(str)) + if(url.pathname.startsWith("/static/data/")){ + const id = str.substring(str.lastIndexOf("/")+1) + return "https://data.velopark.be/data/"+id + } + return super.reformat(str) + } +} diff --git a/src/UI/Popup/TagRendering/TagRenderingMapping.svelte b/src/UI/Popup/TagRendering/TagRenderingMapping.svelte index 31f607964..47d3b9838 100644 --- a/src/UI/Popup/TagRendering/TagRenderingMapping.svelte +++ b/src/UI/Popup/TagRendering/TagRenderingMapping.svelte @@ -29,7 +29,7 @@ {#if mapping.icon !== undefined}
- +
{:else if mapping.then !== undefined} From 1c1cea7f033ea19c74a6efb958a69a3a729bad3c Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 11 Jan 2024 05:10:29 +0100 Subject: [PATCH 003/195] Themes: add velopark theme --- assets/layers/bike_parking/bike_parking.json | 34 ++++++ .../mapcomplete-changes.json | 4 + assets/themes/velopark/license_info.json | 22 ++++ assets/themes/velopark/velopark.json | 102 ++++++++++++++++++ assets/themes/velopark/velopark.png | Bin 0 -> 12235 bytes assets/themes/velopark/velopark.png.license | 2 + assets/themes/velopark/velopark.svg | 43 ++++++++ assets/themes/velopark/velopark.svg.license | 2 + 8 files changed, 209 insertions(+) create mode 100644 assets/themes/velopark/license_info.json create mode 100644 assets/themes/velopark/velopark.json create mode 100644 assets/themes/velopark/velopark.png create mode 100644 assets/themes/velopark/velopark.png.license create mode 100644 assets/themes/velopark/velopark.svg create mode 100644 assets/themes/velopark/velopark.svg.license diff --git a/assets/layers/bike_parking/bike_parking.json b/assets/layers/bike_parking/bike_parking.json index 14a020622..77b74f3b7 100644 --- a/assets/layers/bike_parking/bike_parking.json +++ b/assets/layers/bike_parking/bike_parking.json @@ -634,6 +634,40 @@ ], "id": "Access" }, + { + "id": "fee", + "question": { + "en": "Are these bicycle parkings free to use?", + "nl": "Is deze fietsenstalling gratis te gebruiken?" + }, + "mappings": [ + { + "if": "fee=yes", + "then": { + "en": "One has to pay to use this bicycle parking", + "nl": "Betalende fietsparking" + } + }, + { + "if": "fee=no", + "alsoShowIf": "fee=", + "then": { + "en": "Free to use", + "nl": "Gratis te gebruiken" + } + } + ] + }, + { + "builtin": "opening_hours_24_7", + "override": { + "mappings": [ + { + "alsoShowIf": "opening_hours=" + } + ] + } + }, { "question": { "en": "Does this bicycle parking have spots for cargo bikes?", diff --git a/assets/themes/mapcomplete-changes/mapcomplete-changes.json b/assets/themes/mapcomplete-changes/mapcomplete-changes.json index 6c7376596..328b79b1b 100644 --- a/assets/themes/mapcomplete-changes/mapcomplete-changes.json +++ b/assets/themes/mapcomplete-changes/mapcomplete-changes.json @@ -412,6 +412,10 @@ "if": "theme=uk_addresses", "then": "./assets/themes/uk_addresses/housenumber_unknown.svg" }, + { + "if": "theme=velopark", + "then": "./assets/themes/velopark/velopark.svg" + }, { "if": "theme=vending_machine", "then": "./assets/layers/vending_machine/vending_machine.svg" diff --git a/assets/themes/velopark/license_info.json b/assets/themes/velopark/license_info.json new file mode 100644 index 000000000..207dd4bae --- /dev/null +++ b/assets/themes/velopark/license_info.json @@ -0,0 +1,22 @@ +[ + { + "path": "velopark.png", + "license": "LOGO", + "authors": [ + "Velopark.be" + ], + "sources": [ + "https://velopark.be" + ] + }, + { + "path": "velopark.svg", + "license": "LOGO", + "authors": [ + "Velopark.be" + ], + "sources": [ + "https://velopark.be" + ] + } +] \ No newline at end of file diff --git a/assets/themes/velopark/velopark.json b/assets/themes/velopark/velopark.json new file mode 100644 index 000000000..a275f503c --- /dev/null +++ b/assets/themes/velopark/velopark.json @@ -0,0 +1,102 @@ +{ + "id": "velopark", + "title": { + "nl": "Velopark naar OpenStreetMap sync tool" + }, + "description": "A commisioned theme", + "hideFromOverview": true, + "icon": "./assets/themes/velopark/velopark.svg", + "mustHaveLanguage": [ + "nl" + ], + "lockLocation": [ + [ + 2.51357303225, + 49.5294835476 + ], + [ + 6.15665815596, + 51.4750237087 + ] + ], + "startLat": 51.03753, + "startLon": 3.71025, + "startZoom": 18, + "layers": [ + { + "builtin": [ + "bike_parking" + ], + "override": { + "id": "bike_parking_with_velopark_ref", + "=presets": [], + "=name": { + "en": "Bicycle parkings with velopark ID", + "nl": "Fietsparking met Velopark-referentie" + }, + "source": { + "osmTags": { + "and+": [ + "ref:velopark~*" + ] + } + }, + "calculatedTags": [ + "_velopark:id=feat.properties['ref:velopark'].substr(feat.properties['ref:velopark'].lastIndexOf('/') + 1)" + ], + "pointRendering": [ + { + "marker": [ + { + "color": "#0088ff" + } + ] + } + ], + "=titleIcons": [ + { + "condition": "_velopark:id~*", + "render": "" + }, + "icons.defaults" + ], + "minzoom": 8 + } + }, + "bike_parking", + { + "builtin": ["toilet","bike_repair_station","bicycle_rental"], + "override": { + "minzoom": 18 + } + } + ], + "overrideAll": { + "+tagRenderings": [ + { + "id": "velopark-ref", + "condition": "amenity=bicycle_parking", + "freeform": { + "key": "ref:velopark", + "inline": false, + "type": "velopark" + }, + "question": { + "en": "What is the URL of the data path within Velopark?", + "nl": "Wat is de data-url van deze fietsenstalling in Velopark.be?" + }, + "icon": { + "path": "./assets/themes/velopark/velopark.svg", + "class": "medium" + }, + "render": { + "special": { + "type": "link", + "href": "{ref:velopark}", + "text": "{ref:velopark}" + } + } + } + ] + } +} diff --git a/assets/themes/velopark/velopark.png b/assets/themes/velopark/velopark.png new file mode 100644 index 0000000000000000000000000000000000000000..7b17481ad9e8650b35881caeaf91e751ffafba57 GIT binary patch literal 12235 zcmX|ncQ{<%_cbC!iQapSsG~=((Mb@ZBnG3GXo)&XBzh+py^C&)=!{NuBFZ30q6H)R z=)5~ckVvtp0m&1Yp=DV^`B`xBxE4O!oqr}rTNql3kydP`2F<(4)E=t zR4R{!1xnF+s%+$ky_XxF&!{ndcEcmbw3*X4HY^g6nVC60mcXp=g6SD?^j=*i5^N8A zrk%OHkzJFr_?@GRV$(Bm_UflMGasA5rTpx10nY*rJeOL-bB16S+obO?&~mrIvW@D2lK8 zqYM=PjT)hk&A?ZhFkXofG(IoTmX2K29lSr+8V}s;2lvw9;y)PPeZy$HL7WEtq%D{E zO^H}1YN(Ruzd?Vxf3B4`y36AWRoY)AM6&-a+p{VoKGvmn@_kF8UzZBy7-hZw0flW%+h+FX9 zs(tg=viWVdgLGF2=ya?V{|?%lcNZ9Rmb_N7eFYB|yZD>m0hL`UQ?+v%`X3+t544&Hfz1ORQceyS2L~gdU$IZrd#EguLCt^ZD zoZpr(=8VUf%6*L9O`*{%mN>}uYKgqxW*DA4Tjy2$T7rfB3UwJict?kdjb@Ys=zYPc*@* zaX(uM)iQ8WBoseB^!K_q4?8$9!$&U+uBOh8iCUF>B9EenMk9mA-rNkJr5u6-TVR(Q zrOIe(W0hcrq2hG!O0TRDq#1YaBQH@3DyAYwVTuR2B9VJn9YQt9)%f2VN_BE!LYNlh zT!@%`9#En)GnxxmO+iBTPM_x~7T3RN?TB_KTFV5tZ`KHzkh`=l=55?ytaC zwb)~MZ5OLlcW6WD>R>4T#y0%2#ve!0d6K)`JuyNE*Z_a%!1Ky6%R`zkWYC4@XH$O; zzhgXu&Qb)Q<%aBKL()C%=S~OfkXewg;QU7tKl{_#6|`;NTA?7I&-G1G%&(DHj3y+H z?b$KE@v{eCTgox9agKJ2EKz@G`f%y%9A#mTbmo_;<4+HtrD4^kLb@`Wu z$-TE{^-VkhD@+i0Tdg(vp0uL(ZE;vYx-#T!s9h4)qDOB9E3^# z^=2+r?LOnBG8wCmuLzTN-?XFJQbFV%H^#i^Cf84-_ElbO>e61RL<_{l8w9K~s5h)z zh1Evz`j}Rj3a9JtI};8|BMe345@G(!0*9jTBXC^7n)v4j5z{-4jnYmt1x0Yo*Yx# z^DRMJbTC4fbWw-DclSc+#mhiWI`jq-&x8U3s#EGZ+ zRO=orQa>}wMv@wcb5e(XpI~!p6D>KeM8XpXJfDp&LErDxby{yT)|JSS>1qxu+#$`9dzgS*YzHzrA%`wZk=`E-5{Oo_tNCbqFL0MI z3igDCc@Qk~dY7!rz*Q}zSXl@X>? z)X+i&%F*!d4{q1bekJ~3CBA7#8vTtI%?IB0Hu*M^!~L}cPD_=*5o_|?-&kfAsb_4M zUW%NXfw7i=@L~om_uqq3g2VccUJxMWyV6GlH=I0bO$2GrQh*e7n7=^Y`HBtK^D>EGABbpjbLZGi%r4>RW5U7adn? zXjODtK;FMefsn;2*A>U}9civdzK~b79dGo})Up_v;4Cw!Uhm$s6D2%Kn|lSGd>c%e zTEO;4a?%+BdBy5^?(=H2s|Pf$5_KUi^dWTcRD#DK%0fBoFxg&vxl9V0rjb1s9K z@lRC}zav^3X^;9SMwg%yi$wn{Qe(PO$Ml5chdnBXz)`WOZ;gk=jLt|JtBWUcXWmXZ zHr$m@Rf_*Q&uFBb_?jcRi)FJTSGma~G3qA($=OX%CWW?1SCw?e;^!bkL^< zw5^-)qM|oRMLmOK_URn)5YTgtUe8KrlKYWS)M>{sU}V34kb?ezbYJXb&?4>{+82eC z+-mgi1DAiYz2%zl#Y$48rC-ISr&N%iimhb?0H!;7ECvAm+#_3-7e_(?EHS%K&(ZXsOr*MEt(x zA{+pvm;iqp^3leQmcpSXX85j&ZC%_25Lh6f$@`+jHTWTX5Kw8H3ByJr5$z*fRc$o2 z&?`Axkl=m}w?|ZVH9m7-3tn8`Ed=DQwi8ap#8$OEd2;JyciRElJQps^6ZrWqLYo5u z%69zA{Oj_qjML?9(I$31MTd}9a*buOcUXi|OV(nSi4$!!QrtsV zitiy}inw}}mJF)aJ%_W8BBGNPb?gxFHQQw~Q+4(xl|;2f1mwTWH$iXYYASqGQ4rNE zfMqEd+&?Q6eAe$xGywjI43fYsxomEVa!>$(IL7cP~7-qk;i&hq>L{_dW*A`wv0QA?CDiBb3 zOw-?mER(6$?$jnHIhtI2{rQI7jQsrO-;$roO>?6X6HU=}_a|#Xbq<@C%k34m+-3g) z%^3J604viwf0xEm=3iA}L0zFbGRX^}8zVi*Y~-iJiAC%9FGW31$QBMBL*2dI-S(dA zpf@U%Xk@piPbyAcHa)Oqyqm|BwHK=1BJ;p}p{2MvL>oAA!* zvVU463fav%&cZxvme3dc5#=&Q1`P!u5lhqj)nZLH;77@Fc{7hSo>Kz2szP_S6A2G_ zr5mY%=Ee~6de_XIb-z8VYJO2?FN8UIo!Y6{h8v&{1*7dGdXn1w3_eh=f=j|2M<2a> z3)GY=P4g2RlZ!g96lW?+{dn~ul_hrX-=jutlyjFfVRP}4aZl0r6|{v-TO+$Zwxe{= z_YEtd4c{YI9*E!jE`uG7l%tL2G4O~w(N{~1LZYovtPQ_6&y>t~7J9Y5FTVjc*AzDQ z=X4J!i13U47;rZJGX6ab0WJC2IB@UNo>xBkl*U2R4{nc8 za{zTz=pak}+=qn=&*9(wA3g`;0CD;2H}UDG0p(fY5P11}1(MCKvBwtAFGK5{PQ*4^pcxtvU1?R6=5rxxjcCk5hJYYIhfO8 zjPKIOrw>2`0)|Sqo`uJ1QkB$)v``mi#s!j{ROk};5B!fcl-?JW*jnTFJNmbAj;!MC z$8(vRKyw6ap5qwC)e`FMq?a7j3^L6iBj!cGo<$+Y(l(xMBu6StHojFPZf$Y=e&6@< zX} z5q1Vjhb+vI6O;0|1=d?+_a^oJBMiW{0V9oDQg|eG_k;g4kiZ_ok3IiD7tQ^TC*(L& zpZ19T-@S5cZfshD|9=Z$nFw0c0YLeWlcO-};-)d5LvaiL8yR@t#U}(Un*X~gZmQHR zfkO$fiENk@&Gs-&a*z1`*{rlr7V2Tg|39k%FEnA7c9jr9gCXqydqg_c=>3Vl^!)Eg z<8L-pM;fpGXL$pjEOfgW!chKGf$&0?P!}zMChcsiKy79mzF6p}*{E9EZK7$|5I+IR zh~bA;>$HN}Q36MdSG?MR$#+)+D;*W&-nxMr+Z%vxkk4vDUJm=xO)TSpke<6koE-Ds(0u(C|!r$ zue!9LbgRu{ZQ!j19gb}y!uTW>zB^1lwOU8!hZ}N$Mwu0p+bQ?o>*7HJ%*=^IMZD@Q zn>2x(QbucSG0*;+-Nb|p)>nOwpj#=Izshn^CcTs^_46w!&jSB+?9Ya|$L2bKqEpH= zVc*=+JbuvKNnTA-{fVvwHsacI`sBuxZ<~pU8-Ge(Jm9(^=n>p;yOs|z|Lb?xBXlP~ zQdGkOy1_IRM4tOR(g|N+xQ%y8Z4JTRsMVw!4Tim#rvJ?JHr%-Xtz{qxdtBMk*dAlo zN=(avk*V*id;dd1YR4Hmkmk!Q7H(+~0#26{rtru>a#1`NuD>jcVNrmD_OE_-WBbz)#L4@Q*559e(-GRfU*M>p8p;`n8KdJ7i@@-V9GkAd@Ry&xxh zds`cxgfwlWa2%r4QoA$20XWG6tnOCoUt<5kQC7FUZz2fTA$R}y#T$}l6YJw{r!VKT z-U}&Qd#T~5Sl;@(D4N&eNjfD%LI+;Ssa0pQA`ixPF}=CDj1J;C_qcbJE2F5_@{y%E zuIpM)mWVuo^nSXm@O81O=*>Z#v?tmmXqz#*CWzCJH->Xs$VejAu{@^;JCNSGi`ug0 z9_}N|Z&FubN3pC@tF+0YZMp%(`ogH6+_itL*j~8moVfy@`|ElsgxU)jY822Cq8BAo z2=z~t42ur2zX-7htb)tO)~D(Bnr6*j!XkS4^3^`xiB~A+A$tj#ioCLr2l;vOiIx^y zpeXn*LpmRFljH8$jt&n`fjWVqmOQA4r!IELWDP4c8hLKx>xpkRShBBO%_uz=-m9HQ z1zoP^`)+`7{)4fo6Arx*oqRUI&t_r;_=doO${Mzro@R@lqL>xsnrez5hx39C-76&w?Mx1p1Q zf|JB1&1SW6AYe}qKZ!5u)fPNavwKbbIb*W4(2y{86T}V#Uk94hI9P-VP3FVQPXJE> z0j+sXboiAPxq<>M5BlBJJO%a=0rEPyBoE#=??qW?ZH!bJV5Q9K#?vX{7e>&w-$Q*O zRebJN=Wt-&M^xn>U=jKR9sbyvC8`KnH-=L<5j!jFgyf1bILLK(PD?>{^(1nKpH~ni9*be@o4-hw}7dU`a zGaK_M1YjSjSRkOMfCSYVZwd%Z0tM-fD$I(aVZ8v*r zh>Ak$s{lnF2i~#!@m6^c`307CK2NNpS>nc&a9;Q27c=0n5mxO5372>*Zp(C*ws$T{Bv39%x9jFmL=NP+t_QR$OsULjyIqiC<*Ps_DwFwy!CYN^R7mJ#7mHKvY z$ndvyx^f#aGmR@!zF#dKxak8Rz|*i*w8s|=|K>FDO620{s2<|yT>wS}I{y(>XEas) zO$(zj#H#PW&*DqIW~2Cz8IGFuafwZShiY*%jS5HfQ$ChbYMVjo<;q4IJ;k{KuDY9A zbrsy|;)S+;2bbG03qr9=|MX1KvH1SImCDxrczx>lW?5O-mVPpbItYqQ^%dd|*T)-> zgqkJWF<5?6v*yH%w8W^(^1jAFRzm7G1@j)aOzP99Pj8#6((EkikWXO*aVg&bXa9uP zo$-2~06GSX6J6aR-jz1cmFvO(^#*EDpYatu^cu)337Pe0R$Mz2(9#@wp9-!i1U-I< zpT-CO{CC?cXP~>v17!v$;x72et6z3jn9p@YV;30b=q6wl*Kg{Jn5q~p#V?~yLqlZ; z`jsd5uK;vb-<5S0{rxOk;Gx8dtJC9%7gcl-CVSy8#su~$%;}ag8+*!YnZx&}6d>^8 ziakcc*jv|kgY~w;((@?_>_)XRy4-_b`?7CTIr+{8%K>auI=KX`9B*FYp)6QRr`&8g z-PX%%_@@ojrasJR_sT_j>@9H`!BABJLg8v_8s-yu)XJy(^d1g#drqnMzGvlQ$ye7nA4st6^Jg(9>BkKuslXEa?Y2*(A54 z!>=jtKgU&OITzrGF=NbtjdzHr541>n zlaU7H?dn1cCHrft#Nu&dq%GQ{$SIqtZ*UK`b_k(PJBt|$y2}6)--~QX9Z+k7qg$XiB}W5Cpxnw5>E%QjbXb$ zt+t7A-U13ru@Nx0s^iv& z#3nmUP_j_*ax&HF(4~BMMEqR4wv+-#C^rBck}=Nc=qzM`P<02QL#B=W#d!xNzG7Ox z+}cyKlkyceSHA|q=@I(W9U)qq{=Nzwo`F%VK=J2j%8Wb0Z9~4K>$#}{(#mwH_s3ba zUE_^+*MU^P!NB<=6MA2DP=VK4;^n*}>p`(sVkYf)`Ch4NHy_9R2HP%sNva(5hEvEq z^?mUVw+334zkR%}k1>@ND(d>mX}+;R3yD3-)2h^pO)w@Fo>8`zx=WQBYW3qO5uVSSCA@~QMFQ1I1MO@A<@t(j7ZdulPKC4y{ zMk6sW9}15`BpI!MZnt3_YSi?;ZwMoOVQ^+pF<)S}soly8&XDMJF=7Qzj#F|)_esi( zN5VWyL>FFe`HNGBq8~n=J|cAr%A9tr;BkAEtESu0@NF5BbreK^J-rrFE3Ms-Zpn9@L>>mXS4Rrg}LxUawfvq|=S8b2#5i8GJwj7hZFhxOr@;qwN z&jL`{At2jfoAIeb48$*k2Fk3ex?ziDd|i@TEP7;A!uMI&kwUPCxIjB^kaS5v*kQ41 zWdoz)x%91|$m2mo>aZL0C(=9dZc_^!Ep8DvxMgQrwfFJSd!$DJi-jLRpOe0$5K(#l zp{97GHSffLZ%-uwlikHZ&1He-b=ZdiYL@|)KNGsvM=!ut!?c15fhj}t^y6`R1*V(r zuw^GuNp|zu6#<~)%51>~z(9)#z0$^t?QZ!F>~Q<3V!53>y^`GxJ0DPV9r{$N?XZOt z&=ik#{E6omdzV!+X7AE;w9=V{YM6Il)tg=P4j2YU0Xo6}*hcO5u=>$u`I7;+GLkeA zpOl%zhFHz`Bc2*zg18R==~tW4+8Wo()mpi<9=nrH>>vR3!*qN!rHz+>%C~o7IZ5Ek zIe<=hTo~6v4bjF&4Tu2Bvn83zIR4iBu#=CAT|5?n-hLDiGSlZAt(kxb4y)V+Fz{pB zXVzYKWzsLtl4d~AAwC@o@WMI+v@meEa5!hB4i;=IhM@I!8NSf$k$9d4LzilBuun;U>(WY58tQan))jW-2bBJWE1%RUL+y9k_ZefCXmPsZ6A zMEl6FcrNv(wD9khc;v}15cK?{4M()6Dm#Yx$^hIhck}hfoRa3hW(jtn!=WM-r4zez zzeLBqez}-0s7HURrl0cMQsz{yC@Hw6di^jEM>hBJOF#G}J_Wt7Jz@TUET3NI2~dW6 zF-U!71MNX3#bk%jmv)&SB3UEMJFEHEc1=Osa0GtC!NY#WA7ibQS! zLww7s2}=-si43r;T$UCX25^L{7qvvLTmAgGv?tKugKMgzDy>MfSALR#OhVPy6f6s3 z0s{3ESjbG6tJmcBL*q>m^CDD#xaEx&xiM}7%npu5r66@N>o6qB<{t*6hMJK(mKm~a zFJDFyoPu}U?7EOsF651AiTQ&U2ca7DuG>K7dR$Ub^Clv-*O14Zx!Ou|pb0q*5}PI- z_-nAS>Q+7xG?JM^1p^%-x|Us3wyh`4XM0Gs#3k`XKZx&ujmTTOSt& zt$e+Y1At?TDY@@gNPa;i;^4D5PWqi@4{ICXcH9bPBeArta+=xl4%>uYXS`qC)`jYw zH=S?oU!>%wb%d-u{>s#c0Xo0}embC=<~_K#LkHw_zFL)3C`wjUQ=s%KP@{p*vR)cZ z5rlKs$xOK4&o4|oC?Yif zUjH+?wbJDV(R*sTudPAv_hf8y4;(fQmPQ!~BF&$GmWrH7wndHHZeDDQ-&;1!LwI2E zZv7S=?)iDOT~5Hj^TFLBx3##=f)^5sK!;PVqRTj@D-Lcx}ryg=okdxuCp zT=e8mGMi`ogkhMfk|q1o2-TD)DADQj+I=kNPApf7>c_3I zm!lneFr}^P|47&gV1==aQ~cp>zkRvg5VR9k2cK~)I=Kk6L=X<}WIP$sj{{941Y16{ z<(v$SwqVt^nJ)JWVSUVs32-6{58?%Xj~?!nK;vXT=}AHuC}19o9}U zicOnWf!L8K{*sx#{H-G}TS8cSFZidG+I)!KPoxBItTc4{$?~5Shux%@d^QVpS>s;B z9ITSzd7F5U^H*q!kI2Q&kiU`Wf(t-Tal0NCs5&`Ey)yuA3Jo|| zc>zm@un_Y`0t~qI>_xTo$=wsD$-ER2U}-B-9LD8`{PuvXNJmG|ABUuP&S7CbwZsKwTO(~*lvCy=X7&gZmaUyMvQp&PaGO`X!A3P7_#bprgjvW=`c zIBEMa6kEW*FiRLpPqMW{B-f9;hzu4HsrT~hz=j6xRWGti(%epk%x$BXMlrp(zp$a( zslfPDlB7~2U@YEZv`dL;`nyh~K0ofZW*lws=8up6Nsw#Vx^bQ-$pPVE>PDMzriSXp zHUspA2(&P}vw1e$4-f%GL4ChGprN1_3>R1ML6r&i!0wgsutJXm z%{7#rzm8>{J6*-r(&~a4n%dix4XF9Tga=)Ve0-^JNa*V|N?hlVJ24q*S$pxYTERj6Z2UH-REMx;yfcR~c z8$HPt(2NwIHc7jZt0<5IB$bb9*J(2s=9^VHyT-T7QV7OylgTczwnoW*Fy`&q*98#> zXx*8y;;+mA7#cd!^G%0_rylmpqXpSre`9=X1lf>Q`>lV&zmG4 z;Ohs$8#1MZ9W+4Kq|_9+pxQg>Niz1o{s4e@5-th81M}@0!=Jk!H6ZJh%JAo=AX8w} zcTs(t+eu*d^)H_<><&C6E-ieP)yXObSLiJEK*de-nHZe%i~cJ%91*nOcEddQ8|cVuv{JW}q|xNE6tEP_!flb&6(tQVjCiTE z75fl|5;USFgPQes)DUkkX4hv8n`eCbo@bi9Zwd{)`0=9kWklaFMkQiBP3Myz{Bo`T zQWafVL+j58@QFaihfIK_L)a|>Ubr$-RN~3T8NyL6QN`j^F{}70F#`5)gBjgj$_H6k zne&_s8exVfK$gA6!-z5^G}6AeMBI(+x~2|Xv?I0ikEe8l;%-&(c+&#aT^FkC+21CL z;1z;-F!fQ!`1IPUg%(6+q`3YQaufAUex07x$OZ@U>$g`U9w>Lv&F>-N(6 ziCy;~j@DF}f8O(H zSP=QX?wo&fvXHY8P>J(n_=2L}AudQBZM=>(1RS(0Xdca;efaiwLnpfF5S1l2=)d<* z?vu$uj%1(*^zp)`D3c(5Iej?uJFT&PYZ!~~%zkny zmO+ut;(B~&w&8H)H;)&WU=SzVjZcylkTf4Nbj!elYN=B-<8xjs&ry7K?@w=o#VmolyYl42*fzyG~n@>cn~!^-fB2P7#% z7Eoj!+_SCFSywL~^`xZUBktYqXu?pIDcf;SmcF@S~(o=c=oqY_5 zIvz_zk8N|H*1spZa+L(iCdzW&{ONJ2>;8O5rJz}84X8|Y{a}VS**{j**Bu#Zx$arL zJS{<%Z@&=5I@*3Sgk#+3o + + + diff --git a/assets/themes/velopark/velopark.svg.license b/assets/themes/velopark/velopark.svg.license new file mode 100644 index 000000000..c4e688e62 --- /dev/null +++ b/assets/themes/velopark/velopark.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Velopark.be +SPDX-License-Identifier: LicenseRef-LOGO \ No newline at end of file From 14594b286dd2278bc64ed27f0305b76596d7e190 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sat, 13 Jan 2024 01:58:18 +0100 Subject: [PATCH 004/195] Fix: turn favourites black again, sync them globally --- assets/layers/favourite/favourite.proto.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/assets/layers/favourite/favourite.proto.json b/assets/layers/favourite/favourite.proto.json index 7ad0e3d25..14be372ce 100644 --- a/assets/layers/favourite/favourite.proto.json +++ b/assets/layers/favourite/favourite.proto.json @@ -18,7 +18,7 @@ } ] }, - "color": "red" + "color": "black" } ] } @@ -43,5 +43,6 @@ }, "tagRenderings": [ - ] + ], + "syncSelection": "global" } From ac74fd2683e842380b2c506e1d43ddff93cfee53 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Wed, 10 Jan 2024 23:51:30 +0000 Subject: [PATCH 005/195] Translated using Weblate (English) Currently translated at 100.0% (601 of 601 strings) Translation: MapComplete/Core Translate-URL: https://hosted.weblate.org/projects/mapcomplete/core/en/ --- langs/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/langs/en.json b/langs/en.json index 9437cba7e..00f75c850 100644 --- a/langs/en.json +++ b/langs/en.json @@ -366,7 +366,7 @@ "testing": "Testing - changes won't be saved", "uploadError": "Error while uploading changes: {error}", "uploadGpx": { - "choosePermission": "Choose below if your track should be shared:", + "choosePermission": "Choose below how your track should be shared:", "confirm": "Confirm upload", "gpxServiceOffline": "The GPX-service is currently offline - uploading is currently not possible. Try again later.", "intro0": "By uploading your track, OpenStreetMap.org will retain a full copy of the track.", From 8452e146bebe62016a74b0fa6bc0fd747d4bdbae Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Wed, 10 Jan 2024 14:35:19 +0000 Subject: [PATCH 006/195] Translated using Weblate (English) Currently translated at 100.0% (465 of 465 strings) Translation: MapComplete/themes Translate-URL: https://hosted.weblate.org/projects/mapcomplete/themes/en/ --- langs/themes/en.json | 80 ++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/langs/themes/en.json b/langs/themes/en.json index 98c79d180..477cfc170 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -842,7 +842,7 @@ "hackerspaces": { "description": "On this map you can see hackerspaces, add a new hackerspace or update data directly", "shortDescription": "A map of hackerspaces", - "title": "Hackerspaces" + "title": "Hackerspaces and makerspaces" }, "hailhydrant": { "description": "On this map you can find and update hydrants, fire stations, ambulance stations, and extinguishers in your favorite neighborhoods.", @@ -918,6 +918,13 @@ } } }, + "10": { + "options": { + "0": { + "question": "Exclude etymology theme" + } + } + }, "2": { "options": { "0": { @@ -973,13 +980,6 @@ "question": "Exclude GRB theme" } } - }, - "10": { - "options": { - "0": { - "question": "Exclude etymology theme" - } - } } }, "name": "Changeset centers", @@ -1056,6 +1056,33 @@ "onwheels": { "description": "On this map, publicly weelchair accessible places are shown and can be easily added", "layers": { + "19": { + "override": { + "=title": { + "render": "Statistics" + } + } + }, + "20": { + "override": { + "+tagRenderings": { + "0": { + "render": { + "special": { + "text": "Import" + } + } + }, + "1": { + "render": { + "special": { + "message": "Add all the suggested tags" + } + } + } + } + } + }, "4": { "override": { "filter": { @@ -1098,33 +1125,6 @@ "override": { "name": "Disabled parking spaces" } - }, - "19": { - "override": { - "=title": { - "render": "Statistics" - } - } - }, - "20": { - "override": { - "+tagRenderings": { - "0": { - "render": { - "special": { - "text": "Import" - } - } - }, - "1": { - "render": { - "special": { - "message": "Add all the suggested tags" - } - } - } - } - } } }, "title": "OnWheels" @@ -1285,10 +1285,6 @@ "stations": { "description": "View, edit and add details to a train station", "layers": { - "3": { - "description": "Layer showing train stations", - "name": "Train Stations" - }, "16": { "description": "Displays showing the trains that will leave from this station", "name": "Departures boards", @@ -1320,6 +1316,10 @@ "title": { "render": "Departures board" } + }, + "3": { + "description": "Layer showing train stations", + "name": "Train Stations" } }, "title": "Train Stations" @@ -1498,4 +1498,4 @@ "shortDescription": "A map with waste baskets", "title": "Waste Basket" } -} \ No newline at end of file +} From 4529bd7d96151e435deb4ec147f78ed7f841c9fc Mon Sep 17 00:00:00 2001 From: gallegonovato Date: Wed, 10 Jan 2024 15:05:08 +0000 Subject: [PATCH 007/195] Translated using Weblate (Spanish) Currently translated at 90.7% (422 of 465 strings) Translation: MapComplete/themes Translate-URL: https://hosted.weblate.org/projects/mapcomplete/themes/es/ --- langs/themes/es.json | 145 ++++++++++++++++++++++++++++++++----------- 1 file changed, 108 insertions(+), 37 deletions(-) diff --git a/langs/themes/es.json b/langs/themes/es.json index 8adada4d9..e1a6aa357 100644 --- a/langs/themes/es.json +++ b/langs/themes/es.json @@ -464,7 +464,7 @@ "description": "Este mapa muestra redes de nodos ciclistas y te permita añadir nodos nuevos de manera sencilla", "layers": { "0": { - "name": "enlaces nodo a nodo", + "name": "Vínculos entre nodos", "tagRenderings": { "node2node-survey:date": { "override": { @@ -476,19 +476,34 @@ "title": { "mappings": { "0": { - "then": "enlace nodo a nodo {ref}" + "then": "Vínculos entre nodos {ref}" } }, - "render": "enlace nodo a nodo" + "render": "Vínculos entre nodos" } }, "1": { - "name": "nodos", + "name": "Nodos", + "presets": { + "0": { + "title": "un nodo cíclico" + } + }, "tagRenderings": { "node-expected_rcn_route_relations": { + "freeform": { + "placeholder": "Por ejemplo, 3" + }, "question": "¿A cuántos otros nodos ciclistas enlaza este nodo?", "render": "Este nodo enlaza a {expected_rcn_route_relations} otros nodos ciclistas." }, + "node-rxn_ref": { + "freeform": { + "placeholder": "Por ejemplo, 1" + }, + "question": "¿Cuál es el número de referencia de este nodo cíclico?", + "render": "Este nodo cíclico tiene el número de referencia {rcn_ref}" + }, "node-survey:date": { "override": { "question": "¿Cuándo fue sondeado este nodo ciclista por última vez?", @@ -500,10 +515,30 @@ "mappings": { "0": { "then": "nodo ciclista {rcn_ref}" + }, + "1": { + "then": "Nodo de ciclo propuesto {proposed:rcn_ref}" } }, "render": "nodo ciclista" } + }, + "2": { + "override": { + "name": "Indicadores de ciclismo", + "title": { + "render": "Hito ciclista" + } + } + }, + "3": { + "override": { + "presets": { + "0": { + "title": "Un marcador de ruta para un enlace de nodo a nodo" + } + } + } } }, "title": "Redes de Nodos Ciclistas" @@ -746,6 +781,21 @@ "layers": { "0": { "override": { + "filter+": { + "0": { + "options": { + "0": { + "question": "No se prefiere ningún tipo de aceite" + }, + "1": { + "question": "Solo muestra freiduras que utilizan aceite vegetal" + }, + "2": { + "question": "Solo muestra freiduras que utilizan aceite animal" + } + } + } + }, "name": "Tienda de patatas fritas" } } @@ -792,7 +842,7 @@ "hackerspaces": { "description": "En este mapa puedes ver hackerspaces, añadir un nuevo hackerspace o actualizar datos directamente", "shortDescription": "Un mapa de hackerspaces", - "title": "Hackerspaces" + "title": "Hackerspaces and makerspaces" }, "hailhydrant": { "description": "En este mapa puedes encontrar y actualizar hidrantes contra incendios, estaciones de bomberos, estaciones de ambulancias y extintores en tus vecindarios favoritos.", @@ -824,6 +874,10 @@ "description": "En este mapa encontrarás hoteles en tu zona", "title": "Hoteles" }, + "icecream": { + "description": "Mapa de heladerías y máquinas expendedoras de helados", + "title": "Helado" + }, "indoors": { "description": "En este mapa se muestran los lugares cubiertos de acceso público", "title": "En interiores" @@ -844,6 +898,23 @@ }, "title": "Bordillos y cruces" }, + "mapcomplete-changes": { + "description": "Este mapa muestra todos los cambios realizados con MapComplete", + "layers": { + "0": { + "description": "Muestra todos los cambios de MapComplete", + "filter": { + "0": { + "options": { + "0": { + "question": "El nombre del tema contiene {search}" + } + } + } + } + } + } + }, "maproulette": { "description": "Tema que muestra las tareas de MapRoulette, permitiendo buscarlas, filtrarlas y arreglarlas.", "title": "Tareas de MapRoulette" @@ -875,6 +946,33 @@ "onwheels": { "description": "En este mapa se muestran los lugares accesibles al público en silla de ruedas, que pueden añadirse fácilmente", "layers": { + "19": { + "override": { + "=title": { + "render": "Estadísticas" + } + } + }, + "20": { + "override": { + "+tagRenderings": { + "0": { + "render": { + "special": { + "text": "Importar" + } + } + }, + "1": { + "render": { + "special": { + "message": "Añadir todas las etiquetas sugeridas" + } + } + } + } + } + }, "4": { "override": { "filter": { @@ -917,33 +1015,6 @@ "override": { "name": "Plazas de aparcamiento para discapacitados" } - }, - "19": { - "override": { - "=title": { - "render": "Estadísticas" - } - } - }, - "20": { - "override": { - "+tagRenderings": { - "0": { - "render": { - "special": { - "text": "Importar" - } - } - }, - "1": { - "render": { - "special": { - "message": "Añadir todas las etiquetas sugeridas" - } - } - } - } - } } }, "title": "Sobre ruedas" @@ -1104,10 +1175,6 @@ "stations": { "description": "Ver, editar y añadir detalles a una estación de tren", "layers": { - "3": { - "description": "Capa que muestra las estaciones de tren", - "name": "Estación de Tren" - }, "16": { "description": "Pantallas que muestran los trenes que saldrán de esta estación", "name": "Tableros de salidas", @@ -1139,6 +1206,10 @@ "title": { "render": "Tablero de salidas" } + }, + "3": { + "description": "Capa que muestra las estaciones de tren", + "name": "Estación de Tren" } }, "title": "Estaciones de tren" @@ -1260,4 +1331,4 @@ "shortDescription": "Un mapa con papeleras", "title": "Papeleras" } -} \ No newline at end of file +} From 4ac8ff0de03116122286bd50d12015f37a238ad7 Mon Sep 17 00:00:00 2001 From: kjon Date: Fri, 12 Jan 2024 21:20:43 +0000 Subject: [PATCH 008/195] Translated using Weblate (German) Currently translated at 87.8% (528 of 601 strings) Translation: MapComplete/Core Translate-URL: https://hosted.weblate.org/projects/mapcomplete/core/de/ --- langs/de.json | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/langs/de.json b/langs/de.json index bb1d42e21..4a59b2853 100644 --- a/langs/de.json +++ b/langs/de.json @@ -60,12 +60,13 @@ "unmark": "Von persönlicher Liste bevorzugter Standorte entfernen", "unmarkNotDeleted": "Dieser Punkt wird nicht gelöscht und ist weiterhin auf der entsprechenden Karte für dich und andere sichtbar" }, - "downloadGeojson": "Lade deine Favoriten als geojson herunter", - "downloadGpx": "Lade deine Favoriten als GPX herunter", - "intro": "Du hast {length} Orte als Lieblingsort markiert.", + "downloadGeojson": "Favoriten als geojson herunterladen", + "downloadGpx": "Favoriten als GPX herunterladen", + "intro": "Du hast {length} Orte als Favorit markiert.", "introPrivacy": "Diese Liste ist nur für dich sichtbar", "loginToSeeList": "Melde dich an, um die Liste der Orte zu sehen, die du als Favoriten markiert hast", - "tab": "Deine Favoriten" + "tab": "Deine Favoriten", + "title": "Ausgewählte Orte" }, "flyer": { "aerial": "Diese Karte verwendet einen anderen Hintergrund, nämlich Luftaufnahmen der Agentschap Informatie Vlaanderen", @@ -416,12 +417,16 @@ "hotkeyDocumentation": { "action": "Aktion", "closeSidebar": "Seitenleiste schließen", - "geolocate": "Verschieben Sie die Karte auf den aktuellen Standort oder zoomen Sie die Karte auf den aktuellen Standort. Beantragt Standortberechtigung", + "geolocate": "Karte auf den aktuellen Standort verschieben oder zoomen. Erfordert Standortberechtigung", "intro": "MapComplete unterstützt folgende Tastaturbefehle:", "key": "Tastenkombination", - "openLayersPanel": "Öffnet das Menü für Ebenen und Filter", + "openLayersPanel": "Auswahl für Hintergrundebenen öffnen", "selectAerial": "Hintergrund als Luftbild oder Satellitenbild einstellen. Wechselt zwischen den zwei besten verfügbaren Ebenen", - "selectItem": "Wähle das POI, das dem Kartenmittelpunkt (Fadenkreuz) am nächsten liegt. Nur wenn die Tastaturnavigation verwendet wird", + "selectFavourites": "Favoriten anzeigen", + "selectItem": "Objekt auswählen, das dem Kartenmittelpunkt (Fadenkreuz) am nächsten liegt. Nur wenn die Tastaturnavigation verwendet wird", + "selectItem2": "Objekt auswählen, das dem Kartenmittelpunkt (Fadenkreuz) am zweitnächsten liegt. Nur wenn die Tastaturnavigation verwendet wird", + "selectItem3": "Objekt auswählen, das dem Kartenmittelpunkt (Fadenkreuz) am drittnächsten liegt. Nur wenn die Tastaturnavigation verwendet wird", + "selectItemI": "Objekt auswählen, das dem Kartenmittelpunkt (Fadenkreuz) am viertnächsten liegt. Nur wenn die Tastaturnavigation verwendet wird", "selectMap": "Karte aus externer Quelle als Hintergrund wählen. Wechselt zwischen den zwei besten verfügbaren Ebenen", "selectMapnik": "OpenStreetMap-carto als Hintergrundebene wählen", "selectOsmbasedmap": "OpenStreetMap-basierte Karte als Hintergrund auswählen (oder Hintergrundebene deaktivieren)", From 300df3fb414a05e0d9481768590a7d2198db385c Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sat, 13 Jan 2024 03:42:07 +0100 Subject: [PATCH 009/195] More tests for OpeningHours.ts for edge cases --- src/UI/OpeningHours/OpeningHours.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/UI/OpeningHours/OpeningHours.ts b/src/UI/OpeningHours/OpeningHours.ts index 36abe120b..25edb475e 100644 --- a/src/UI/OpeningHours/OpeningHours.ts +++ b/src/UI/OpeningHours/OpeningHours.ts @@ -127,6 +127,11 @@ export class OH { * const oh1: OpeningHour = { weekday: 0, startHour: 10, startMinutes: 0, endHour: 11, endMinutes: 0 }; * const oh0: OpeningHour = { weekday: 0, startHour: 11, startMinutes: 0, endHour: 12, endMinutes: 0 }; * OH.MergeTimes([oh0, oh1]) // => [{ weekday: 0, startHour: 10, startMinutes: 0, endHour: 12, endMinutes: 0 }] + * + * // should merge touching opening hours spanning days + * const oh0: OpeningHour = { weekday: 0, startHour: 10, startMinutes: 0, endHour: 24, endMinutes: 0 }; + * const oh1: OpeningHour = { weekday: 1, startHour: 0, startMinutes: 0, endHour: 12, endMinutes: 0 }; + * OH.MergeTimes([oh0, oh1]) // => [{ weekday: 0, startHour: 10, startMinutes: 0, endHour: 24, endMinutes: 0 }, { weekday: 1, startHour: 0, startMinutes: 0, endHour: 12, endMinutes: 0 }] */ public static MergeTimes(ohs: OpeningHour[]): OpeningHour[] { const queue = ohs.map((oh) => { @@ -216,6 +221,7 @@ export class OH { // This means that the list is changed only if the lengths are different. // If the lengths are the same, we might just as well return the old list and be a bit more stable if (newList.length !== ohs.length) { + newList.sort((a, b) => b.weekday - a.weekday) return newList } else { return ohs @@ -308,6 +314,12 @@ export class OH { * rules[1].weekday // => 1 * rules[1].startHour // => 0 * rules[1].endHour // => 2 + * + * const rules = OH.ParseRule("Mo 00:00-24:00") + * rules.length // => 1 + * rules[0].weekday // => 0 + * rules[0].startHour // => 0 + * rules[0].endHour // => 24 */ public static ParseRule(rule: string): OpeningHour[] { try { @@ -781,6 +793,7 @@ This list will be sorted /** * OH.ParseHhmmRanges("20:00-22:15") // => [{startHour: 20, startMinutes: 0, endHour: 22, endMinutes: 15}] * OH.ParseHhmmRanges("20:00-02:15") // => [{startHour: 20, startMinutes: 0, endHour: 2, endMinutes: 15}] + * OH.ParseHhmmRanges("00:00-24:00") // => [{startHour: 0, startMinutes: 0, endHour: 24, endMinutes: 0}] */ private static ParseHhmmRanges(hhmms: string): { startHour: number From 250eede6589ffaeb2143ec7e8fb8212da1fc86db Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sat, 13 Jan 2024 05:24:56 +0100 Subject: [PATCH 010/195] Velopark: add first sync tool --- assets/layers/bike_parking/bike_parking.json | 39 +++++ assets/themes/velopark/velopark.json | 18 +- src/Logic/Web/VeloparkLoader.ts | 175 +++++++++++++++++++ src/UI/Comparison/ComparisonAction.svelte | 65 +++++++ src/UI/Comparison/ComparisonTable.svelte | 120 +++++++++++++ src/UI/Comparison/ComparisonTool.svelte | 61 +++++++ src/UI/Image/LinkableImage.svelte | 3 - src/UI/SpecialVisualizations.ts | 23 +++ 8 files changed, 500 insertions(+), 4 deletions(-) create mode 100644 src/Logic/Web/VeloparkLoader.ts create mode 100644 src/UI/Comparison/ComparisonAction.svelte create mode 100644 src/UI/Comparison/ComparisonTable.svelte create mode 100644 src/UI/Comparison/ComparisonTool.svelte diff --git a/assets/layers/bike_parking/bike_parking.json b/assets/layers/bike_parking/bike_parking.json index 77b74f3b7..62e4dc5d0 100644 --- a/assets/layers/bike_parking/bike_parking.json +++ b/assets/layers/bike_parking/bike_parking.json @@ -668,6 +668,45 @@ ] } }, + { + "id": "operator_phone", + "question": { + "en": "What is the phone number of the operator of this bicycle parking?", + "nl": "Wat is het telefoonnummer van de operator van deze fietsenstalling?" + }, + "questionHint": { + "en": "One might be able to call this number in case of problems, e.g. to remove unmaintained bicycles", + "nl": "Men kan dit nummer bellen om bv. fietswrakken of defecten te melden" + }, + "icon": "./assets/layers/questions/phone.svg", + "freeform": { + "key": "operator:phone", + "type": "phone", + "addExtraTags": [ + "phone=", + "contact:phone=" + ] + }, + "render": "{operator:phone}", + "mappings": [ + { + "if": "phone~*", + "hideInAnswer": true, + "then": { + "*": "{phone}" + }, + "icon": "./assets/layers/questions/phone.svg" + }, + { + "if": "contact:phone~*", + "hideInAnswer": true, + "then": { + "*": "{contact:phone}" + }, + "icon": "./assets/layers/questions/phone.svg" + } + ] + }, { "question": { "en": "Does this bicycle parking have spots for cargo bikes?", diff --git a/assets/themes/velopark/velopark.json b/assets/themes/velopark/velopark.json index a275f503c..32ddf3d9b 100644 --- a/assets/themes/velopark/velopark.json +++ b/assets/themes/velopark/velopark.json @@ -63,7 +63,12 @@ "minzoom": 8 } }, - "bike_parking", + { + "builtin": ["bike_parking"], + "override": { + "minzoom": 14 + } + }, { "builtin": ["toilet","bike_repair_station","bicycle_rental"], "override": { @@ -96,6 +101,17 @@ "text": "{ref:velopark}" } } + }, + { + "id": "comparison_tool", + "condition": "ref:velopark~https://data.velopark.be/data/.*" , + "render": { + "special": { + "type": "compare_data", + "url": "ref:velopark", + "postprocessing": "velopark" + } + } } ] } diff --git a/src/Logic/Web/VeloparkLoader.ts b/src/Logic/Web/VeloparkLoader.ts new file mode 100644 index 000000000..91ea8899b --- /dev/null +++ b/src/Logic/Web/VeloparkLoader.ts @@ -0,0 +1,175 @@ +import { Feature, Point } from "geojson" +import { OH } from "../../UI/OpeningHours/OpeningHours" +import EmailValidator from "../../UI/InputElement/Validators/EmailValidator" +import PhoneValidator from "../../UI/InputElement/Validators/PhoneValidator" +import { CountryCoder } from "latlon2country" +import Constants from "../../Models/Constants" +import { Utils } from "../../Utils" + +/** + * Commissioned code, to be kept until 2030 + * + * Reads a velopark-json, converts it to a geojson + */ +export default class VeloparkLoader { + + private static readonly emailReformatting = new EmailValidator() + private static readonly phoneValidator = new PhoneValidator() + + private static readonly coder = new CountryCoder( + Constants.countryCoderEndpoint, + Utils.downloadJson + ) + + public static convert(veloparkData: VeloparkData): Feature { + + const properties: { + "operator:email"?: string, + "operator:phone"?: string, + fee?: string, + opening_hours?: string + access?: string + maxstay?: string + operator?: string + } = {} + + properties.operator = veloparkData.operatedBy?.companyName + + if (veloparkData.contactPoint?.email) { + properties["operator:email"] = VeloparkLoader.emailReformatting.reformat(veloparkData.contactPoint?.email) + } + + + if (veloparkData.contactPoint?.telephone) { + properties["operator:phone"] = VeloparkLoader.phoneValidator.reformat(veloparkData.contactPoint?.telephone, () => "be") + } + + veloparkData.photos.forEach((p, i) => { + if (i === 0) { + properties["image"] = p.image + } else { + properties["image:" + i] = p.image + + } + }) + + let coordinates: [number, number] = undefined + for (const g of veloparkData["@graph"]) { + coordinates = [g.geo[0].longitude, g.geo[0].latitude] + if (g.maximumParkingDuration?.endsWith("D") && g.maximumParkingDuration?.startsWith("P")) { + const duration = g.maximumParkingDuration.substring(1, g.maximumParkingDuration.length - 1) + properties.maxstay = duration + " days" + } + properties.access = g.publicAccess ? "yes" : "no" + const prefix = "http://schema.org/" + const oh = OH.simplify(g.openingHoursSpecification.map(spec => { + const dayOfWeek = spec.dayOfWeek.substring(prefix.length, prefix.length + 2).toLowerCase() + const startHour = spec.opens + const endHour = spec.closes === "23:59" ? "24:00" : spec.closes + const merged = OH.MergeTimes(OH.ParseRule(dayOfWeek + " " + startHour + "-" + endHour)) + return OH.ToString(merged) + }).join("; ")) + properties.opening_hours = oh + + if (g.priceSpecification[0]) { + properties.fee = g.priceSpecification[0].freeOfCharge ? "no" : "yes" + } + } + + + return { type: "Feature", properties, geometry: { type: "Point", coordinates } } + } + +} + +interface VeloparkData { + "@context": any, + "@id": string // "https://data.velopark.be/data/NMBS_541", + "@type": "BicycleParkingStation", + "dateModified": string, + "identifier": number, + "name": [ + { + "@value": string, + "@language": "nl" + } + ], + "ownedBy": { + "@id": string, + "@type": "BusinessEntity", + "companyName": string + }, + "operatedBy": { + "@type": "BusinessEntity", + "companyName": string + }, + "address": any, + "hasMap": any, + "contactPoint": { + "@type": "ContactPoint", + "email": string, + "telephone": string + }, + "photos": { + "@type": "Photograph", + "image": string + }[], + "interactionService": { + "@type": "WebSite", + "url": string + }, + /** + * Contains various extra pieces of data, e.g. services or opening hours + */ + "@graph": [ + { + "@type": "https://data.velopark.be/openvelopark/terms#PublicBicycleParking", + "openingHoursSpecification": { + "@type": "OpeningHoursSpecification", + /** + * Ends with 'Monday', 'Tuesday', ... + */ + "dayOfWeek": "http://schema.org/Monday" + | "http://schema.org/Tuesday" + | "http://schema.org/Wednesday" + | "http://schema.org/Thursday" + | "http://schema.org/Friday" + | "http://schema.org/Saturday" + | "http://schema.org/Sunday", + /** + * opens: 00:00 and closes 23:59 for the entire day + */ + "opens": string, + "closes": string + }[], + /** + * P30D = 30 days + */ + "maximumParkingDuration": "P30D", + "publicAccess": true, + "totalCapacity": 110, + "allows": [ + { + "@type": "AllowedBicycle", + /* TODO is cargo bikes etc also available?*/ + "bicycleType": "https://data.velopark.be/openvelopark/terms#RegularBicycle", + "bicyclesAmount": number + } + ], + "geo": [ + { + "@type": "GeoCoordinates", + "latitude": number, + "longitude": number + } + ], + "priceSpecification": [ + { + "@type": "PriceSpecification", + "freeOfCharge": boolean + } + ] + } + ] + +} diff --git a/src/UI/Comparison/ComparisonAction.svelte b/src/UI/Comparison/ComparisonAction.svelte new file mode 100644 index 000000000..b16f216d4 --- /dev/null +++ b/src/UI/Comparison/ComparisonAction.svelte @@ -0,0 +1,65 @@ + + + + {key} + + + {#if externalProperties[key].startsWith("http")} + + {externalProperties[key]} + + {:else} + {externalProperties[key]} + {/if} + + + {#if currentStep === "init"} + + {:else if currentStep === "applying"} + + {:else if currentStep === "done"} +
Done
+ {:else } +
Error
+ {/if} + + diff --git a/src/UI/Comparison/ComparisonTable.svelte b/src/UI/Comparison/ComparisonTable.svelte new file mode 100644 index 000000000..f6a8758f6 --- /dev/null +++ b/src/UI/Comparison/ComparisonTable.svelte @@ -0,0 +1,120 @@ + + +{#if different.length > 0} +

Conflicting items

+ {JSON.stringify(different)} +{/if} + +{#if missing.length > 0} +

Missing items

+ + {#if currentStep === "init"} + + + + + + + {#each missing as key} + + {/each} + +
KeyExternal
+ + {:else if currentStep === "applying_all"} + Applying all missing values + {:else if currentStep === "all_applied"} +
+ All values are applied +
+ {/if} +{/if} + + +{#if unknownImages.length === 0 && missing.length === 0 && different.length === 0} +
+ + All data from Velopark is also included into OpenStreetMap +
+{/if} + +{#if unknownImages.length > 0} +

Missing pictures

+ {#each unknownImages as image} + + {/each} + + +{/if} + diff --git a/src/UI/Comparison/ComparisonTool.svelte b/src/UI/Comparison/ComparisonTool.svelte new file mode 100644 index 000000000..92652aec8 --- /dev/null +++ b/src/UI/Comparison/ComparisonTool.svelte @@ -0,0 +1,61 @@ + + + +{#if error !== undefined} +
+ Something went wrong: {error} +
+{:else if data === undefined} + + Loading {$tags[url]} + +{:else} + +{/if} diff --git a/src/UI/Image/LinkableImage.svelte b/src/UI/Image/LinkableImage.svelte index 572cc6776..8675cc8a4 100644 --- a/src/UI/Image/LinkableImage.svelte +++ b/src/UI/Image/LinkableImage.svelte @@ -15,8 +15,6 @@ import SpecialTranslation from "../Popup/TagRendering/SpecialTranslation.svelte" export let tags: UIEventSource - export let lon: number - export let lat: number export let state: SpecialVisualizationState export let image: P4CPicture export let feature: Feature @@ -26,7 +24,6 @@ let isLinked = Object.values(tags.data).some((v) => image.pictureUrl === v) const t = Translations.t.image.nearby - const c = [lon, lat] const providedImage: ProvidedImage = { url: image.thumbUrl ?? image.pictureUrl, url_hd: image.pictureUrl, diff --git a/src/UI/SpecialVisualizations.ts b/src/UI/SpecialVisualizations.ts index 6bf55e99f..207362620 100644 --- a/src/UI/SpecialVisualizations.ts +++ b/src/UI/SpecialVisualizations.ts @@ -88,6 +88,7 @@ import MaprouletteSetStatus from "./MapRoulette/MaprouletteSetStatus.svelte" import DirectionIndicator from "./Base/DirectionIndicator.svelte" import Img from "./Base/Img" import Qr from "../Utils/Qr" +import ComparisonTool from "./Comparison/ComparisonTool.svelte" class NearbyImageVis implements SpecialVisualization { // Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests @@ -1638,6 +1639,28 @@ export default class SpecialVisualizations { ) }, }, + { + funcName: "compare_data", + needsUrls: (args) => args[0], + args:[ + { + name: "url", + required: true, + doc: "The attribute containing the url where to fetch more data" + }, + { + name: "postprocessing", + required: false, + doc: "Apply some postprocessing. Currently, only 'velopark' is allowed as value" + } + ], + docs: "Gives an interactive element which shows a tag comparison between the OSM-object and the upstream object. This allows to copy some or all tags into OSM", + constr(state: SpecialVisualizationState, tagSource: UIEventSource>, args: string[], feature: Feature, layer: LayerConfig): BaseUIElement { + const url = args[0] + const postprocessVelopark = args[1] === "velopark" + return new SvelteUIElement(ComparisonTool, {url, postprocessVelopark, state, tags: tagSource, layer, feature}) + } + } ] specialVisualizations.push(new AutoApplyButton(specialVisualizations)) From 3f3d03a603ac530cb55d6782dd908ada4a602157 Mon Sep 17 00:00:00 2001 From: Ettore Atalan Date: Sat, 13 Jan 2024 09:05:18 +0000 Subject: [PATCH 011/195] Translated using Weblate (German) Currently translated at 93.5% (562 of 601 strings) Translation: MapComplete/Core Translate-URL: https://hosted.weblate.org/projects/mapcomplete/core/de/ --- langs/de.json | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/langs/de.json b/langs/de.json index 4a59b2853..d9a6a85a6 100644 --- a/langs/de.json +++ b/langs/de.json @@ -174,12 +174,14 @@ "openIssueTracker": "Fehler melden", "openMapillary": "Mapillary öffnen", "openOsmcha": "Neueste Bearbeitungen mit {theme} ansehen", + "seeOnMapillary": "Dieses Bild auf Mapillary ansehen", "themeBy": "Dieses Thema wurde erstellt von {author}", "title": "Copyright und Urheberrechtsangabe", "translatedBy": "MapComplete wurde übersetzt von {contributors} und {hiddenCount} weiteren Personen" }, "back": "Zurück", "backToIndex": "Zur Übersicht aller Themenkarten", + "backToMap": "Zurück zur Karte", "backgroundMap": "Hintergrundkarte auswählen", "backgroundSwitch": "Hintergrund wechseln", "cancel": "Abbrechen", @@ -225,6 +227,13 @@ "histogram": { "error_loading": "Das Histogramm konnte nicht geladen werden" }, + "labels": { + "background": "Hintergrund ändern", + "filter": "Daten filtern", + "menu": "Menü", + "zoomIn": "Hineinzoomen", + "zoomOut": "Herauszoomen" + }, "layerSelection": { "title": "Ebenen auswählen", "zoomInToSeeThisLayer": "Ausschnitt vergrößern, um Ebene anzuzeigen" @@ -273,7 +282,9 @@ "closed_permanently": "Geschlossen auf unbestimmte Zeit", "closed_until": "Geschlossen bis {date}", "error_loading": "Fehler: Diese Öffnungszeiten können nicht angezeigt werden.", + "friday": "Am Freitag {ranges}", "loadingCountry": "Land ermitteln…", + "monday": "Am Montag {ranges}", "not_all_rules_parsed": "Die Öffnungszeiten sind kompliziert. Folgenden Regeln werden im Eingabefenster ignoriert:", "openTill": "bis", "open_24_7": "Durchgehend geöffnet", @@ -282,7 +293,15 @@ "ph_closed": "geschlossen", "ph_not_known": " ", "ph_open": "geöffnet", - "ph_open_as_usual": "geöffnet wie üblich" + "ph_open_as_usual": "geöffnet wie üblich", + "ranges": "von {starttime} bis {endtime}", + "rangescombined": "{range0} und {range1}", + "saturday": "Am Samstag {ranges}", + "sunday": "Am Sonntag {ranges}", + "thursday": "Am Donnerstag {ranges}", + "tuesday": "Am Dienstag {ranges}", + "unknown": "Die Öffnungszeiten sind unbekannt", + "wednesday": "Am Mittwoch {ranges}" }, "osmLinkTooltip": "Dieses Element auf OpenStreetMap öffnen, um Verlauf und Bearbeitungsmöglichkeiten anzuzeigen", "pdf": { @@ -291,7 +310,7 @@ "generatedWith": "Erstellt mit mapcomplete.org/{layoutid}", "versionInfo": "v{version} - erstellt am {date}" }, - "pickLanguage": "Sprache auswählen: ", + "pickLanguage": "Sprache auswählen", "poweredByOsm": "Unterstützt von OpenStreetMap", "questionBox": { "answeredMultiple": "Du hast {answered} Fragen beantwortet", @@ -325,6 +344,7 @@ "searchShort": "Suche…", "searching": "Suchen …" }, + "share": "Teilen", "sharescreen": { "copiedToClipboard": "Verknüpfung in Zwischenablage kopiert", "documentation": "Für weitere Informationen über verfügbare URL-Parameter, siehe Dokumentation", @@ -370,6 +390,27 @@ "uploadingChanges": "Änderungen werden hochgeladen…", "useSearch": "Verwenden Sie die Suche oben, um Voreinstellungen anzuzeigen", "useSearchForMore": "Verwenden Sie die Suchfunktion, um innerhalb von {total} weitere Werte zu suchen…", + "visualFeedback": { + "directionsAbsolute": { + "E": "Ost", + "N": "Nord", + "NE": "Nordost", + "NW": "Nordwest", + "S": "Süd", + "SE": "Südost", + "SW": "Südwest", + "W": "West" + }, + "directionsRelative": { + "left": "links", + "right": "rechts", + "sharp_left": "scharf links", + "sharp_right": "scharf rechts", + "slight_left": "leicht links", + "slight_right": "leicht rechts", + "straight": "geradeaus" + } + }, "waitingForGeopermission": "Warten auf Ihre Erlaubnis, Standortdaten zu verwenden…", "waitingForLocation": "Ihr Standort wird gesucht…", "weekdays": { From 7bbe4196eb9b6beb4455ef6e4c742c66ab3beff7 Mon Sep 17 00:00:00 2001 From: Ettore Atalan Date: Sat, 13 Jan 2024 09:04:49 +0000 Subject: [PATCH 012/195] Translated using Weblate (German) Currently translated at 100.0% (465 of 465 strings) Translation: MapComplete/themes Translate-URL: https://hosted.weblate.org/projects/mapcomplete/themes/de/ --- langs/themes/de.json | 80 ++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/langs/themes/de.json b/langs/themes/de.json index c20fd97c8..672a46b9c 100644 --- a/langs/themes/de.json +++ b/langs/themes/de.json @@ -842,7 +842,7 @@ "hackerspaces": { "description": "Auf dieser Karte können Sie Hackerspaces sehen, einen neuen Hackerspace hinzufügen oder Daten direkt aktualisieren", "shortDescription": "Eine Karte von Hackerspaces", - "title": "Hackerspaces" + "title": "Hackerspaces und Makerspaces" }, "hailhydrant": { "description": "Auf dieser Karte können Sie Hydranten, Feuerwachen, Krankenwagen und Feuerlöscher in Ihren bevorzugten Stadtvierteln finden und aktualisieren.", @@ -918,6 +918,13 @@ } } }, + "10": { + "options": { + "0": { + "question": "Etymologie-Thema ausschließen" + } + } + }, "2": { "options": { "0": { @@ -973,13 +980,6 @@ "question": "GRB-Theme ausschließen" } } - }, - "10": { - "options": { - "0": { - "question": "Etymologie-Thema ausschließen" - } - } } }, "name": "Zentrum der Änderungssätze", @@ -1056,6 +1056,33 @@ "onwheels": { "description": "Auf dieser Karte können Sie öffentlich zugängliche Orte für Rollstuhlfahrer ansehen, bearbeiten oder hinzufügen", "layers": { + "19": { + "override": { + "=title": { + "render": "Statistik" + } + } + }, + "20": { + "override": { + "+tagRenderings": { + "0": { + "render": { + "special": { + "text": "Import" + } + } + }, + "1": { + "render": { + "special": { + "message": "Alle vorgeschlagenen Tags hinzufügen" + } + } + } + } + } + }, "4": { "override": { "filter": { @@ -1098,33 +1125,6 @@ "override": { "name": "Barrierefreie Parkplätze" } - }, - "19": { - "override": { - "=title": { - "render": "Statistik" - } - } - }, - "20": { - "override": { - "+tagRenderings": { - "0": { - "render": { - "special": { - "text": "Import" - } - } - }, - "1": { - "render": { - "special": { - "message": "Alle vorgeschlagenen Tags hinzufügen" - } - } - } - } - } } }, "title": "Auf Rädern" @@ -1285,10 +1285,6 @@ "stations": { "description": "Bahnhofsdetails ansehen, bearbeiten und hinzufügen", "layers": { - "3": { - "description": "Ebene mit Bahnhöfen", - "name": "Bahnhöfe" - }, "16": { "description": "Anzeigen der Züge, die von diesem Bahnhof abfahren", "name": "Abfahrtstafeln", @@ -1320,6 +1316,10 @@ "title": { "render": "Abfahrtstafel" } + }, + "3": { + "description": "Ebene mit Bahnhöfen", + "name": "Bahnhöfe" } }, "title": "Bahnhöfe" @@ -1498,4 +1498,4 @@ "shortDescription": "Eine Karte mit Abfalleimern", "title": "Abfalleimer" } -} \ No newline at end of file +} From 505844b64a77bce16a09bf1a18a9462b5cc825ba Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 14 Jan 2024 22:24:35 +0100 Subject: [PATCH 013/195] Fix loading external velopark data --- assets/themes/velopark/velopark.json | 1 + scripts/generateLayouts.ts | 16 +++++++++++++--- src/UI/Comparison/ComparisonTable.svelte | 3 --- src/UI/Comparison/ComparisonTool.svelte | 7 ++++--- src/UI/SpecialVisualization.ts | 2 +- src/UI/SpecialVisualizations.ts | 9 +++++++-- 6 files changed, 26 insertions(+), 12 deletions(-) diff --git a/assets/themes/velopark/velopark.json b/assets/themes/velopark/velopark.json index 32ddf3d9b..3da36a318 100644 --- a/assets/themes/velopark/velopark.json +++ b/assets/themes/velopark/velopark.json @@ -109,6 +109,7 @@ "special": { "type": "compare_data", "url": "ref:velopark", + "host":"https://data.velopark.be", "postprocessing": "velopark" } } diff --git a/scripts/generateLayouts.ts b/scripts/generateLayouts.ts index 509bc6959..fba21038e 100644 --- a/scripts/generateLayouts.ts +++ b/scripts/generateLayouts.ts @@ -16,6 +16,8 @@ import * as crypto from "crypto" import * as eli from "../src/assets/editor-layer-index.json" import * as eli_global from "../src/assets/global-raster-layers.json" import ValidationUtils from "../src/Models/ThemeConfig/Conversion/ValidationUtils" +import { LayerConfigJson } from "../src/Models/ThemeConfig/Json/LayerConfigJson" +import { QuestionableTagRenderingConfigJson } from "../src/Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson" const sharp = require("sharp") const template = readFileSync("theme.html", "utf8") @@ -282,19 +284,24 @@ async function generateCsp( SpecialVisualizations.specialVisualizations.forEach((sv) => { if (typeof sv.needsUrls === "function") { + // Handled below return } apiUrls.push(...(sv.needsUrls ?? [])) }) - const usedSpecialVisualisations = ValidationUtils.getSpecialVisualisationsWithArgs(layoutJson) + const usedSpecialVisualisations = [].concat(...layoutJson.layers.map(l => ValidationUtils.getAllSpecialVisualisations( (l).tagRenderings ?? []))) for (const usedSpecialVisualisation of usedSpecialVisualisations) { if (typeof usedSpecialVisualisation === "string") { continue } const neededUrls = usedSpecialVisualisation.func.needsUrls ?? [] if (typeof neededUrls === "function") { - apiUrls.push(...neededUrls(usedSpecialVisualisation.args)) + let needed: string | string[] = neededUrls(usedSpecialVisualisation.args) + if(typeof needed === "string"){ + needed = [needed] + } + apiUrls.push(...needed) } } @@ -306,11 +313,14 @@ async function generateCsp( const vectorLayers = eliLayers.filter((l) => l.properties.type === "vector") const vectorSources = vectorLayers.map((l) => l.properties.url) apiUrls.push(...vectorSources) - for (const connectSource of apiUrls.concat(geojsonSources)) { + for (let connectSource of apiUrls.concat(geojsonSources)) { if (!connectSource) { continue } try { + if(!connectSource.startsWith("http")){ + connectSource = "https://"+connectSource + } const url = new URL(connectSource) hosts.add("https://" + url.host) } catch (e) { diff --git a/src/UI/Comparison/ComparisonTable.svelte b/src/UI/Comparison/ComparisonTable.svelte index f6a8758f6..a749e8f83 100644 --- a/src/UI/Comparison/ComparisonTable.svelte +++ b/src/UI/Comparison/ComparisonTable.svelte @@ -4,7 +4,6 @@ import { UIEventSource } from "../../Logic/UIEventSource" import type { OsmTags } from "../../Models/OsmFeature" import type { SpecialVisualizationState } from "../SpecialVisualization" - import type { P4CPicture } from "../../Logic/Web/NearbyImagesSearch" import type { Feature } from "geojson" import LayerConfig from "../../Models/ThemeConfig/LayerConfig" import ComparisonAction from "./ComparisonAction.svelte" @@ -19,7 +18,6 @@ export let tags: UIEventSource export let state: SpecialVisualizationState - export let image: P4CPicture export let feature: Feature export let layer: LayerConfig @@ -115,6 +113,5 @@ {layer} /> {/each} - {/if} diff --git a/src/UI/Comparison/ComparisonTool.svelte b/src/UI/Comparison/ComparisonTool.svelte index 92652aec8..3acf386f0 100644 --- a/src/UI/Comparison/ComparisonTool.svelte +++ b/src/UI/Comparison/ComparisonTool.svelte @@ -11,11 +11,12 @@ import { UIEventSource } from "../../Logic/UIEventSource" import ComparisonTable from "./ComparisonTable.svelte" import LayerConfig from "../../Models/ThemeConfig/LayerConfig" import type { Feature } from "geojson" +import type { OsmTags } from "../../Models/OsmFeature" export let url: string export let postprocessVelopark: boolean export let state: SpecialVisualizationState -export let tags: UIEventSource> +export let tags: UIEventSource export let layer: LayerConfig export let feature: Feature let data: any = undefined @@ -56,6 +57,6 @@ onMount(async () => { Loading {$tags[url]} -{:else} - +{:else if data.properties !== undefined} + {/if} diff --git a/src/UI/SpecialVisualization.ts b/src/UI/SpecialVisualization.ts index 2799a2e80..14629ce32 100644 --- a/src/UI/SpecialVisualization.ts +++ b/src/UI/SpecialVisualization.ts @@ -98,7 +98,7 @@ export interface SpecialVisualization { readonly funcName: string readonly docs: string | BaseUIElement readonly example?: string - readonly needsUrls?: string[] | ((args: string[]) => string) + readonly needsUrls?: string[] | ((args: string[]) => string | string[]) /** * Indicates that this special visualisation will make requests to the 'alLNodesDatabase' and that it thus should be included diff --git a/src/UI/SpecialVisualizations.ts b/src/UI/SpecialVisualizations.ts index 207362620..d36c0eb7a 100644 --- a/src/UI/SpecialVisualizations.ts +++ b/src/UI/SpecialVisualizations.ts @@ -1641,13 +1641,18 @@ export default class SpecialVisualizations { }, { funcName: "compare_data", - needsUrls: (args) => args[0], + needsUrls: (args) => args[1].split(";"), args:[ { name: "url", required: true, doc: "The attribute containing the url where to fetch more data" }, + { + name:"host", + required:true, + doc: "The domain name(s) where data might be fetched from - this is needed to set the CSP. A domain must include 'https', e.g. 'https://example.com'. For multiple domains, separate them with ';'. If you don't know the possible domains, use '*'. " + }, { name: "postprocessing", required: false, @@ -1657,7 +1662,7 @@ export default class SpecialVisualizations { docs: "Gives an interactive element which shows a tag comparison between the OSM-object and the upstream object. This allows to copy some or all tags into OSM", constr(state: SpecialVisualizationState, tagSource: UIEventSource>, args: string[], feature: Feature, layer: LayerConfig): BaseUIElement { const url = args[0] - const postprocessVelopark = args[1] === "velopark" + const postprocessVelopark = args[2] === "velopark" return new SvelteUIElement(ComparisonTool, {url, postprocessVelopark, state, tags: tagSource, layer, feature}) } } From 7445ad5fc288ef46782db6fc3c541041c63bab9f Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Mon, 15 Jan 2024 00:13:25 +0100 Subject: [PATCH 014/195] Fix crash if there are no photos in the velopark data --- src/Logic/Web/VeloparkLoader.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Logic/Web/VeloparkLoader.ts b/src/Logic/Web/VeloparkLoader.ts index 91ea8899b..30d9b6268 100644 --- a/src/Logic/Web/VeloparkLoader.ts +++ b/src/Logic/Web/VeloparkLoader.ts @@ -44,7 +44,7 @@ export default class VeloparkLoader { properties["operator:phone"] = VeloparkLoader.phoneValidator.reformat(veloparkData.contactPoint?.telephone, () => "be") } - veloparkData.photos.forEach((p, i) => { + veloparkData.photos?.forEach((p, i) => { if (i === 0) { properties["image"] = p.image } else { From 64f6090c447cf5954a122bf6bb3a7ef6bea7f8b1 Mon Sep 17 00:00:00 2001 From: kjon Date: Sun, 14 Jan 2024 17:42:35 +0000 Subject: [PATCH 015/195] Translated using Weblate (German) Currently translated at 98.4% (3132 of 3182 strings) Translation: MapComplete/Layer translations Translate-URL: https://hosted.weblate.org/projects/mapcomplete/layers/de/ --- langs/layers/de.json | 849 +++++++++++++++++++++++++------------------ 1 file changed, 489 insertions(+), 360 deletions(-) diff --git a/langs/layers/de.json b/langs/layers/de.json index 46d5c4cc0..ce42f1fb8 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -35,6 +35,16 @@ "1": { "title": "eine freistehende Posterbox" }, + "10": { + "description": "Verwendet für Werbeschilder, Leuchtreklamen, Logos und institutionelle Eingangsschilder", + "title": "ein Schild" + }, + "11": { + "title": "eine Skulptur" + }, + "12": { + "title": "eine Wandmalerei" + }, "2": { "title": "eine wandmontierte Posterbox" }, @@ -61,16 +71,6 @@ }, "9": { "title": "ein Totem" - }, - "10": { - "description": "Verwendet für Werbeschilder, Leuchtreklamen, Logos und institutionelle Eingangsschilder", - "title": "ein Schild" - }, - "11": { - "title": "eine Skulptur" - }, - "12": { - "title": "eine Wandmalerei" } }, "tagRenderings": { @@ -165,6 +165,9 @@ "1": { "then": "Dies ist ein Brett" }, + "10": { + "then": "Dies ist eine Wandmalerei" + }, "2": { "then": "Dies ist eine Litfaßsäule" }, @@ -188,9 +191,6 @@ }, "9": { "then": "Dies ist ein Totem" - }, - "10": { - "then": "Dies ist eine Wandmalerei" } }, "question": "Welche Art von Werbung ist das?", @@ -205,6 +205,9 @@ "1": { "then": "Brett" }, + "10": { + "then": "Wandmalerei" + }, "2": { "then": "Posterbox" }, @@ -228,9 +231,6 @@ }, "9": { "then": "Totem" - }, - "10": { - "then": "Wandmalerei" } } } @@ -353,6 +353,15 @@ "1": { "then": "Wandbild" }, + "10": { + "then": "Azulejo (spanische dekorative Fliesenarbeit)" + }, + "11": { + "then": "Fliesenarbeit" + }, + "12": { + "then": "Holzschnitzerei" + }, "2": { "then": "Malerei" }, @@ -376,15 +385,6 @@ }, "9": { "then": "Relief" - }, - "10": { - "then": "Azulejo (spanische dekorative Fliesenarbeit)" - }, - "11": { - "then": "Fliesenarbeit" - }, - "12": { - "then": "Holzschnitzerei" } }, "question": "Um welche Art Kunstwerk handelt es sich?", @@ -1942,30 +1942,6 @@ "1": { "question": "Verfügt über einen
Schuko-Stecker ohne Erdungsstift (CEE7/4 Typ F)
" }, - "2": { - "question": "Verfügt über einen
europäischen Netzstecker mit Erdungsstift (CEE7/4 Typ E)
Anschluss" - }, - "3": { - "question": "Verfügt über einen
Chademo
Stecker" - }, - "4": { - "question": "Verfügt über einen
Typ 1 (J1772)
Stecker mit Kabel" - }, - "5": { - "question": "Verfügt über einen
Typ 1 (J1772)Stecker ohne Kabel
" - }, - "6": { - "question": "Verfügt über einen
Typ 1 CCS (Typ 1 Combo)
Stecker" - }, - "7": { - "question": "Verfügt über einen
Tesla Supercharger
Stecker" - }, - "8": { - "question": "Hat einen
Typ 2 (Mennekes)
Anschluss" - }, - "9": { - "question": "Hat einen
Typ 2 CCS (Mennekes)
Anschluss" - }, "10": { "question": "Hat einen
Typ 2 (Mennekes)
Anschluss mit Kabel" }, @@ -1986,6 +1962,30 @@ }, "16": { "question": "Hat einen
Bosch Active Connect Anschluss mit 5 Pins
und Kabel" + }, + "2": { + "question": "Verfügt über einen
europäischen Netzstecker mit Erdungsstift (CEE7/4 Typ E)
Anschluss" + }, + "3": { + "question": "Verfügt über einen
Chademo
Stecker" + }, + "4": { + "question": "Verfügt über einen
Typ 1 (J1772)
Stecker mit Kabel" + }, + "5": { + "question": "Verfügt über einen
Typ 1 (J1772)Stecker ohne Kabel
" + }, + "6": { + "question": "Verfügt über einen
Typ 1 CCS (Typ 1 Combo)
Stecker" + }, + "7": { + "question": "Verfügt über einen
Tesla Supercharger
Stecker" + }, + "8": { + "question": "Hat einen
Typ 2 (Mennekes)
Anschluss" + }, + "9": { + "question": "Hat einen
Typ 2 CCS (Mennekes)
Anschluss" } } } @@ -2041,30 +2041,6 @@ "1": { "then": "Schuko-Stecker ohne Erdungsstift (CEE7/4 Typ F)" }, - "2": { - "then": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" - }, - "3": { - "then": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" - }, - "4": { - "then": "Chademo-Anschluss" - }, - "5": { - "then": "Chademo-Anschluss" - }, - "6": { - "then": "Typ 1 mit Kabel (J1772)" - }, - "7": { - "then": "Typ 1 mit Kabel (J1772)" - }, - "8": { - "then": "Typ 1 ohne Kabel (J1772)" - }, - "9": { - "then": " Typ 1 ohne Kabel (J1772)" - }, "10": { "then": "Typ 1 CCS (Typ 1 Combo)" }, @@ -2095,6 +2071,9 @@ "19": { "then": "Typ 2 mit Kabel (mennekes)" }, + "2": { + "then": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" + }, "20": { "then": "Tesla Supercharger CCS (Typ 2 CSS von Tesla)" }, @@ -2125,11 +2104,32 @@ "29": { "then": " Bosch Active Connect mit 3 Pins und Kabel" }, + "3": { + "then": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" + }, "30": { "then": "Bosch Active Connect mit 5 Pins und Kabel" }, "31": { "then": " Bosch Active Connect mit 5 Pins und Kabel" + }, + "4": { + "then": "Chademo-Anschluss" + }, + "5": { + "then": "Chademo-Anschluss" + }, + "6": { + "then": "Typ 1 mit Kabel (J1772)" + }, + "7": { + "then": "Typ 1 mit Kabel (J1772)" + }, + "8": { + "then": "Typ 1 ohne Kabel (J1772)" + }, + "9": { + "then": " Typ 1 ohne Kabel (J1772)" } }, "question": "Welche Ladeanschlüsse gibt es hier?" @@ -2226,7 +2226,7 @@ "then": "Für den Zugang zur Station muss ein Schlüssel angefordert werden
z.B. eine von einem Hotel betriebene Ladestation, die nur von dessen Gästen genutzt werden kann, die an der Rezeption einen Schlüssel erhalten, um die Ladestation aufzuschließen" }, "4": { - "then": "Die Station ist nicht für die Allgemeinheit zugänglich (z. B. nur für die Eigentümer, Mitarbeiter, …)" + "then": "Die Station ist nicht für die Allgemeinheit zugänglich (z. B. nur für die Eigentümer, Mitarbeiter, ...)" }, "5": { "then": "Diese Ladestation ist zu gewissen Öffnungszeiten oder Bedingungen öffentlich zugänglich. Einschränkungen sind möglich, aber generelle Nutzung ist erlaubt." @@ -2259,7 +2259,7 @@ "then": "Kostenlose Nutzung" }, "3": { - "then": "Die Nutzung ist kostenpflichtig, aber für Kunden des Betreibers der Einrichtung, wie Hotel, Krankenhaus, … kostenlos" + "then": "Die Nutzung ist kostenpflichtig, aber für Kunden des Betreibers der Einrichtung, wie Hotel, Krankenhaus, ... kostenlos" }, "4": { "then": "Die Nutzung ist kostenpflichtig" @@ -2304,7 +2304,67 @@ "mappings": { "then": "{{description}} liefert maximal {{commonCurrent}} A" }, - "question": "Welchen Strom bieten die Stecker mit {{description}}?" + "question": "Welchen Strom bieten die Stecker mit {{description}}?", + "render": "{{description}} liefert maximal {canonical({{key}}:current)}" + }, + "3": { + "mappings": { + "then": "{{description}} liefert maximal {{commonOutput}} A" + }, + "question": "Welche Leistung liefert ein einzelner Stecker des Typs {{description}}?", + "render": "{{description}} liefert maximal {canonical({{key}}:output)}" + } + }, + "rewrite": { + "into": { + "0": { + "2": "Schuko-Stecker ohne Erdungsstift (CEE7/4 Typ F)" + }, + "1": { + "2": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" + }, + "10": { + "2": "Tesla Supercharger CCS (Typ 2 CSS von Tesla)" + }, + "11": { + "2": "Tesla Supercharger (Destination)" + }, + "12": { + "2": "Tesla Supercharger (Destination) (Typ 2 mit Kabel von Tesla)" + }, + "13": { + "2": "USB zum Aufladen von Handys und kleinen Elektrogeräten" + }, + "14": { + "2": " Bosch Active Connect mit 3 Pins und Kabel" + }, + "15": { + "2": " Bosch Active Connect mit 5 Pins und Kabel" + }, + "2": { + "2": "Chademo-Stecker" + }, + "3": { + "2": "Typ 1 mit Kabel (J1772)" + }, + "4": { + "2": " Typ 1 ohne Kabel (J1772)" + }, + "5": { + "2": "Typ 1 CCS (Typ 1 Combo)" + }, + "6": { + "2": "Tesla Supercharger" + }, + "7": { + "2": "Typ 2 (Mennekes)" + }, + "8": { + "2": "Typ 2 CCS (Mennekes)" + }, + "9": { + "2": "Typ 2 mit Kabel (Mennekes)" + } } } }, @@ -3081,6 +3141,15 @@ "1": { "then": "Dieser Radweg hat einen festen Belag" }, + "10": { + "then": "Dieser Radweg besteht aus feinem Schotter" + }, + "11": { + "then": "Der Radweg ist aus Kies" + }, + "12": { + "then": "Dieser Radweg besteht aus Rohboden" + }, "2": { "then": "Der Radweg ist aus Asphalt" }, @@ -3104,15 +3173,6 @@ }, "9": { "then": "Der Radweg ist aus Schotter" - }, - "10": { - "then": "Dieser Radweg besteht aus feinem Schotter" - }, - "11": { - "then": "Der Radweg ist aus Kies" - }, - "12": { - "then": "Dieser Radweg besteht aus Rohboden" } }, "question": "Was ist der Belag dieses Radwegs?", @@ -3161,6 +3221,15 @@ "1": { "then": "Dieser Radweg hat einen festen Belag" }, + "10": { + "then": "Dieser Radweg besteht aus feinem Schotter" + }, + "11": { + "then": "Der Radweg ist aus Kies" + }, + "12": { + "then": "Dieser Radweg besteht aus Rohboden" + }, "2": { "then": "Der Radweg ist aus Asphalt" }, @@ -3184,15 +3253,6 @@ }, "9": { "then": "Der Radweg ist aus Schotter" - }, - "10": { - "then": "Dieser Radweg besteht aus feinem Schotter" - }, - "11": { - "then": "Der Radweg ist aus Kies" - }, - "12": { - "then": "Dieser Radweg besteht aus Rohboden" } }, "question": "Was ist der Belag dieser Straße?", @@ -3340,6 +3400,18 @@ "question": "Wie breit ist der Abstand zwischen Radweg und Straße?", "render": "Der Sicherheitsabstand zu diesem Radweg beträgt {cycleway:buffer} m" }, + "incline": { + "mappings": { + "0": { + "then": "Hier gibt es (wahrscheinlich) keine Steigung" + }, + "1": { + "then": "Die Straße hat eine Steigung" + } + }, + "question": "Hat {title()} eine Steigung?", + "render": "Die Straße hat {incline} Steigung" + }, "is lit?": { "mappings": { "0": { @@ -3632,6 +3704,9 @@ }, "1": { "then": "Dies ist ein Wasserhahn oder eine Wasserpumpe mit nicht trinkbarem Wasser.
Beispiele sind Wasserhähne mit Regenwasser zum Zapfen von Wasser für nahe gelegene Pflanzen
" + }, + "2": { + "then": "Dies ist eine historische, manuelle Wasserpumpe, an der kein Trinkwasser zu finden ist" } } }, @@ -3670,8 +3745,58 @@ "question": "Ist diese Trinkwasserstelle noch in Betrieb?", "render": "Der Betriebsstatus ist {operational_status}" }, + "bench-artwork": { + "mappings": { + "0": { + "then": "Die Trinkwasserstelle hat ein integriertes Kunstwerk" + }, + "1": { + "then": "Die Trinkwasserstelle hat kein integriertes Kunstwerk" + }, + "2": { + "then": "Die Trinkwasserstelle hat wahrscheinlich kein integriertes Kunstwerk" + } + }, + "question": "Verfügt der Trinkwasserbrunnen über ein künstlerisches Element?", + "questionHint": "Z.B. eine integrierte Statue oder andere künstlerische Werke" + }, + "fee": { + "mappings": { + "0": { + "then": "Die Nutzung ist kostenlos" + }, + "1": { + "then": "Die Nutzung ist kostenpflichtig" + } + }, + "question": "Kann diese Trinkwasserstelle kostenlos genutzt werden?" + }, + "opening_hours_24_7": { + "override": { + "+mappings": { + "0": { + "then": "Der Trinkwasserbrunnen ist derzeit nicht in Betrieb. Deshalb werden keine Öffnungszeiten angezeigt." + } + }, + "questionHint": "Dies sind die Öffnungszeiten des Trinkwasserbrunnens, wenn dieser in Betrieb ist." + } + }, "render-closest-drinking-water": { "render": "Eine weitere Trinkwasserstelle befindet sich in {_closest_other_drinking_water_distance} Meter" + }, + "seasonal": { + "mappings": { + "0": { + "then": "Die Trinkwasserstelle ist ganzjährig in Betrieb" + }, + "1": { + "then": "Die Trinkwasserstelle ist nur im Sommer in Betrieb" + }, + "2": { + "then": "Die Trinkwasserstelle ist im Winter nicht in Betrieb" + } + }, + "question": "Ist die Trinkwasserstelle ganzjährig in Betrieb?" } }, "title": { @@ -4068,6 +4193,54 @@ } } }, + "10": { + "options": { + "0": { + "question": "Keine Bevorzugung von Hunden" + }, + "1": { + "question": "Hunde erlaubt" + }, + "2": { + "question": "Keine Hunde erlaubt" + } + } + }, + "11": { + "options": { + "0": { + "question": "Internetzugang vorhanden" + } + } + }, + "12": { + "options": { + "0": { + "question": "Stromanschluss vorhanden" + } + } + }, + "13": { + "options": { + "0": { + "question": "Hat zuckerfreie Angebote" + } + } + }, + "14": { + "options": { + "0": { + "question": "Hat glutenfreie Angebote" + } + } + }, + "15": { + "options": { + "0": { + "question": "Hat laktosefreie Angebote" + } + } + }, "2": { "options": { "0": { @@ -4138,54 +4311,6 @@ "question": "Nutzung kostenlos" } } - }, - "10": { - "options": { - "0": { - "question": "Keine Bevorzugung von Hunden" - }, - "1": { - "question": "Hunde erlaubt" - }, - "2": { - "question": "Keine Hunde erlaubt" - } - } - }, - "11": { - "options": { - "0": { - "question": "Internetzugang vorhanden" - } - } - }, - "12": { - "options": { - "0": { - "question": "Stromanschluss vorhanden" - } - } - }, - "13": { - "options": { - "0": { - "question": "Hat zuckerfreie Angebote" - } - } - }, - "14": { - "options": { - "0": { - "question": "Hat glutenfreie Angebote" - } - } - }, - "15": { - "options": { - "0": { - "question": "Hat laktosefreie Angebote" - } - } } } }, @@ -4305,30 +4430,6 @@ "1": { "then": "Die Fitness-Station hat ein Schild mit Anweisungen für eine bestimmte Übung." }, - "2": { - "then": "Die Fitness-Station hat eine Einrichtung für Sit-ups." - }, - "3": { - "then": "Die Fitness-Station hat eine Vorrichtung für Liegestütze. In der Regel eine oder mehrere niedrige Reckstangen." - }, - "4": { - "then": "Die Fitness-Station hat Stangen zum Dehnen." - }, - "5": { - "then": "Die Fitness-Station hat eine Vorrichtung für Rückenstrecker (Hyperextensions)." - }, - "6": { - "then": "Die Fitness-Station hat Ringe für Gymnastikübungen." - }, - "7": { - "then": "Die Fitness-Station hat eine horizontale Leiter (Monkey Bars)." - }, - "8": { - "then": "Die Fitness-Station hat eine Sprossenwand zum Klettern." - }, - "9": { - "then": "Die Fitness-Station hat Pfosten für Slalomübungen." - }, "10": { "then": "Die Fitness-Station hat Trittsteine." }, @@ -4359,6 +4460,9 @@ "19": { "then": "Die Fitness-Station hat Kampfseile (battle ropes)." }, + "2": { + "then": "Die Fitness-Station hat eine Einrichtung für Sit-ups." + }, "20": { "then": "Die Fitness-Station hat ein Fahrradergometer." }, @@ -4373,6 +4477,27 @@ }, "24": { "then": "Die Fitness-Station hat eine Slackline." + }, + "3": { + "then": "Die Fitness-Station hat eine Vorrichtung für Liegestütze. In der Regel eine oder mehrere niedrige Reckstangen." + }, + "4": { + "then": "Die Fitness-Station hat Stangen zum Dehnen." + }, + "5": { + "then": "Die Fitness-Station hat eine Vorrichtung für Rückenstrecker (Hyperextensions)." + }, + "6": { + "then": "Die Fitness-Station hat Ringe für Gymnastikübungen." + }, + "7": { + "then": "Die Fitness-Station hat eine horizontale Leiter (Monkey Bars)." + }, + "8": { + "then": "Die Fitness-Station hat eine Sprossenwand zum Klettern." + }, + "9": { + "then": "Die Fitness-Station hat Pfosten für Slalomübungen." } }, "question": "Welche Übungsgeräte gibt es an dieser Fitness-Station?" @@ -4492,6 +4617,21 @@ "1": { "then": "Dies ist eine Pommesbude" }, + "10": { + "then": "Hier werden chinesische Gerichte serviert" + }, + "11": { + "then": "Hier werden griechische Gerichte serviert" + }, + "12": { + "then": "Hier werden indische Gerichte serviert" + }, + "13": { + "then": "Hier werden türkische Gerichte serviert" + }, + "14": { + "then": "Hier werden thailändische Gerichte serviert" + }, "2": { "then": "Bietet vorwiegend Pastagerichte an" }, @@ -4515,21 +4655,6 @@ }, "9": { "then": "Hier werden französische Gerichte serviert" - }, - "10": { - "then": "Hier werden chinesische Gerichte serviert" - }, - "11": { - "then": "Hier werden griechische Gerichte serviert" - }, - "12": { - "then": "Hier werden indische Gerichte serviert" - }, - "13": { - "then": "Hier werden türkische Gerichte serviert" - }, - "14": { - "then": "Hier werden thailändische Gerichte serviert" } }, "question": "Was für Essen gibt es hier?", @@ -4897,6 +5022,10 @@ "2": { "1": "eine CNC-Fräse", "2": "CNC-Fräse" + }, + "4": { + "1": "eine Nähmaschine", + "2": "Nähmaschine" } } } @@ -5157,30 +5286,6 @@ "1": { "then": "Dies ist ein Auditorium" }, - "2": { - "then": "Dies ist ein Schlafzimmer" - }, - "3": { - "then": "Dies ist eine Kapelle" - }, - "4": { - "then": "Dies ist ein Klassenzimmer" - }, - "5": { - "then": "Dies ist ein Klassenzimmer" - }, - "6": { - "then": "Dies ist ein Computerraum" - }, - "7": { - "then": "Dies ist ein Konferenzraum" - }, - "8": { - "then": "Dies ist eine Krypta" - }, - "9": { - "then": "Dies ist eine Küche" - }, "10": { "then": "Dies ist ein Labor" }, @@ -5211,6 +5316,9 @@ "19": { "then": "Dies ist ein Lagerraum" }, + "2": { + "then": "Dies ist ein Schlafzimmer" + }, "20": { "then": "Dies ist ein Technikraum" }, @@ -5219,6 +5327,27 @@ }, "22": { "then": "Dies ist ein Wartezimmer" + }, + "3": { + "then": "Dies ist eine Kapelle" + }, + "4": { + "then": "Dies ist ein Klassenzimmer" + }, + "5": { + "then": "Dies ist ein Klassenzimmer" + }, + "6": { + "then": "Dies ist ein Computerraum" + }, + "7": { + "then": "Dies ist ein Konferenzraum" + }, + "8": { + "then": "Dies ist eine Krypta" + }, + "9": { + "then": "Dies ist eine Küche" } }, "question": "Wie wird dieser Raum genutzt?" @@ -5845,6 +5974,19 @@ } } }, + "10": { + "options": { + "0": { + "question": "Alle Notizen" + }, + "1": { + "question": "Importnotizen ausblenden" + }, + "2": { + "question": "Nur Importnotizen anzeigen" + } + } + }, "2": { "options": { "0": { @@ -5900,19 +6042,6 @@ "question": "Nur offene Notizen anzeigen" } } - }, - "10": { - "options": { - "0": { - "question": "Alle Notizen" - }, - "1": { - "question": "Importnotizen ausblenden" - }, - "2": { - "question": "Nur Importnotizen anzeigen" - } - } } }, "name": "OpenStreetMap-Hinweise", @@ -6232,6 +6361,21 @@ "1": { "then": "Dies ist ein normaler Stellplatz." }, + "10": { + "then": "Dies ist ein Stellplatz, der für Eltern mit Kindern reserviert ist." + }, + "11": { + "then": "Dies ist ein Stellplatz, der für das Personal reserviert ist." + }, + "12": { + "then": "Dies ist ein Stellplatz, der für Taxis reserviert ist." + }, + "13": { + "then": "Dies ist ein Stellplatz, der für Fahrzeuge mit Anhänger reserviert ist." + }, + "14": { + "then": "Dies ist ein Stellplatz, der für Carsharing reserviert ist." + }, "2": { "then": "Dies ist ein Behindertenstellplatz." }, @@ -6255,21 +6399,6 @@ }, "9": { "then": "Dies ist ein Stellplatz, der für Motorräder reserviert ist." - }, - "10": { - "then": "Dies ist ein Stellplatz, der für Eltern mit Kindern reserviert ist." - }, - "11": { - "then": "Dies ist ein Stellplatz, der für das Personal reserviert ist." - }, - "12": { - "then": "Dies ist ein Stellplatz, der für Taxis reserviert ist." - }, - "13": { - "then": "Dies ist ein Stellplatz, der für Fahrzeuge mit Anhänger reserviert ist." - }, - "14": { - "then": "Dies ist ein Stellplatz, der für Carsharing reserviert ist." } }, "question": "Welche Art von Stellplatz ist dies?" @@ -6861,6 +6990,21 @@ "1": { "then": "2-Cent-Münzen werden akzeptiert" }, + "10": { + "then": "20-Centime-Münzen werden akzeptiert" + }, + "11": { + "then": "½-Schweizer Franken-Münzen werden akzeptiert" + }, + "12": { + "then": "1-Schweizer Franken-Münzen werden akzeptiert" + }, + "13": { + "then": "2-Schweizer Franken-Münzen werden akzeptiert" + }, + "14": { + "then": "5-Schweizer Franken-Münzen werden akzeptiert" + }, "2": { "then": "5-Cent-Münzen werden akzeptiert" }, @@ -6884,21 +7028,6 @@ }, "9": { "then": "10-Centime-Münzen werden akzeptiert" - }, - "10": { - "then": "20-Centime-Münzen werden akzeptiert" - }, - "11": { - "then": "½-Schweizer Franken-Münzen werden akzeptiert" - }, - "12": { - "then": "1-Schweizer Franken-Münzen werden akzeptiert" - }, - "13": { - "then": "2-Schweizer Franken-Münzen werden akzeptiert" - }, - "14": { - "then": "5-Schweizer Franken-Münzen werden akzeptiert" } }, "question": "Mit welchen Münzen kann man hier bezahlen?" @@ -6911,6 +7040,15 @@ "1": { "then": "10-Euro-Scheine werden angenommen" }, + "10": { + "then": "100-Schweizer Franken-Scheine werden akzeptiert" + }, + "11": { + "then": "200-Schweizer Franken-Scheine werden akzeptiert" + }, + "12": { + "then": "1000-Schweizer Franken-Scheine werden akzeptiert" + }, "2": { "then": "20-Euro-Scheine werden angenommen" }, @@ -6934,15 +7072,6 @@ }, "9": { "then": "50-Schweizer Franken-Scheine werden akzeptiert" - }, - "10": { - "then": "100-Schweizer Franken-Scheine werden akzeptiert" - }, - "11": { - "then": "200-Schweizer Franken-Scheine werden akzeptiert" - }, - "12": { - "then": "1000-Schweizer Franken-Scheine werden akzeptiert" } }, "question": "Mit welchen Banknoten kann man hier bezahlen?" @@ -7381,30 +7510,6 @@ "1": { "question": "Recycling von Batterien" }, - "2": { - "question": "Recycling von Getränkekartons" - }, - "3": { - "question": "Recycling von Dosen" - }, - "4": { - "question": "Recycling von Kleidung" - }, - "5": { - "question": "Recycling von Speiseöl" - }, - "6": { - "question": "Recycling von Motoröl" - }, - "7": { - "question": "Recycling von Leuchtstoffröhren" - }, - "8": { - "question": "Recycling von Grünabfällen" - }, - "9": { - "question": "Recycling von Glasflaschen" - }, "10": { "question": "Recycling von Glas" }, @@ -7435,11 +7540,35 @@ "19": { "question": "Recycling von Restabfällen" }, + "2": { + "question": "Recycling von Getränkekartons" + }, "20": { "question": "Recycling von Druckerpatronen" }, "21": { "question": "Recycling von Fahrrädern" + }, + "3": { + "question": "Recycling von Dosen" + }, + "4": { + "question": "Recycling von Kleidung" + }, + "5": { + "question": "Recycling von Speiseöl" + }, + "6": { + "question": "Recycling von Motoröl" + }, + "7": { + "question": "Recycling von Leuchtstoffröhren" + }, + "8": { + "question": "Recycling von Grünabfällen" + }, + "9": { + "question": "Recycling von Glasflaschen" } } }, @@ -7507,30 +7636,6 @@ "1": { "then": "Getränkekartons können hier recycelt werden" }, - "2": { - "then": "Dosen können hier recycelt werden" - }, - "3": { - "then": "Kleidung kann hier recycelt werden" - }, - "4": { - "then": "Speiseöl kann hier recycelt werden" - }, - "5": { - "then": "Motoröl kann hier recycelt werden" - }, - "6": { - "then": "Hier können Leuchtstoffröhren recycelt werden" - }, - "7": { - "then": "Grünabfälle können hier recycelt werden" - }, - "8": { - "then": "Bio-Abfall kann hier recycelt werden" - }, - "9": { - "then": "Glasflaschen können hier recycelt werden" - }, "10": { "then": "Glas kann hier recycelt werden" }, @@ -7561,6 +7666,9 @@ "19": { "then": "Schuhe können hier recycelt werden" }, + "2": { + "then": "Dosen können hier recycelt werden" + }, "20": { "then": "Elektrokleingeräte können hier recycelt werden" }, @@ -7575,6 +7683,27 @@ }, "24": { "then": "Fahrräder können hier recycelt werden" + }, + "3": { + "then": "Kleidung kann hier recycelt werden" + }, + "4": { + "then": "Speiseöl kann hier recycelt werden" + }, + "5": { + "then": "Motoröl kann hier recycelt werden" + }, + "6": { + "then": "Hier können Leuchtstoffröhren recycelt werden" + }, + "7": { + "then": "Grünabfälle können hier recycelt werden" + }, + "8": { + "then": "Bio-Abfall kann hier recycelt werden" + }, + "9": { + "then": "Glasflaschen können hier recycelt werden" } }, "question": "Was kann hier recycelt werden?" @@ -8467,6 +8596,12 @@ "1": { "then": "Diese Straßenlaterne verwendet LEDs" }, + "10": { + "then": "Diese Straßenlaterne verwendet Hochdruck-Natriumdampflampen (orange mit weiß)" + }, + "11": { + "then": "Diese Straßenlaterne wird mit Gas beleuchtet" + }, "2": { "then": "Diese Straßenlaterne verwendet Glühlampenlicht" }, @@ -8490,12 +8625,6 @@ }, "9": { "then": "Diese Straßenlaterne verwendet Niederdruck-Natriumdampflampen (einfarbig orange)" - }, - "10": { - "then": "Diese Straßenlaterne verwendet Hochdruck-Natriumdampflampen (orange mit weiß)" - }, - "11": { - "then": "Diese Straßenlaterne wird mit Gas beleuchtet" } }, "question": "Mit welcher Art von Beleuchtung arbeitet diese Straßenlaterne?" @@ -9699,30 +9828,6 @@ "1": { "question": "Verkauf von Getränken" }, - "2": { - "question": "Verkauf von Süßigkeiten" - }, - "3": { - "question": "Verkauf von Lebensmitteln" - }, - "4": { - "question": "Verkauf von Zigaretten" - }, - "5": { - "question": "Verkauf von Kondomen" - }, - "6": { - "question": "Verkauf von Kaffee" - }, - "7": { - "question": "Verkauf von Trinkwasser" - }, - "8": { - "question": "Verkauf von Zeitungen" - }, - "9": { - "question": "Verkauf von Fahrradschläuchen" - }, "10": { "question": "Verkauf von Milch" }, @@ -9753,6 +9858,9 @@ "19": { "question": "Verkauf von Blumen" }, + "2": { + "question": "Verkauf von Süßigkeiten" + }, "20": { "question": "Verkauf von Parkscheinen" }, @@ -9776,6 +9884,27 @@ }, "27": { "question": "Verkauf von Fahrradschlössern" + }, + "3": { + "question": "Verkauf von Lebensmitteln" + }, + "4": { + "question": "Verkauf von Zigaretten" + }, + "5": { + "question": "Verkauf von Kondomen" + }, + "6": { + "question": "Verkauf von Kaffee" + }, + "7": { + "question": "Verkauf von Trinkwasser" + }, + "8": { + "question": "Verkauf von Zeitungen" + }, + "9": { + "question": "Verkauf von Fahrradschläuchen" } } } @@ -9822,30 +9951,6 @@ "1": { "then": "Süßigkeiten werden verkauft" }, - "2": { - "then": "Lebensmittel werden verkauft" - }, - "3": { - "then": "Zigaretten werden verkauft" - }, - "4": { - "then": "Kondome werden verkauft" - }, - "5": { - "then": "Kaffee wird verkauft" - }, - "6": { - "then": "Trinkwasser wird verkauft" - }, - "7": { - "then": "Zeitungen werden verkauft" - }, - "8": { - "then": "Fahrradschläuche werden verkauft" - }, - "9": { - "then": "Milch wird verkauft" - }, "10": { "then": "Brot wird verkauft" }, @@ -9876,6 +9981,9 @@ "19": { "then": "Parkscheine werden verkauft" }, + "2": { + "then": "Lebensmittel werden verkauft" + }, "20": { "then": "Souvenirmünzen werden verkauft" }, @@ -9896,6 +10004,27 @@ }, "26": { "then": "Fahrradschlösser werden verkauft" + }, + "3": { + "then": "Zigaretten werden verkauft" + }, + "4": { + "then": "Kondome werden verkauft" + }, + "5": { + "then": "Kaffee wird verkauft" + }, + "6": { + "then": "Trinkwasser wird verkauft" + }, + "7": { + "then": "Zeitungen werden verkauft" + }, + "8": { + "then": "Fahrradschläuche werden verkauft" + }, + "9": { + "then": "Milch wird verkauft" } }, "question": "Was wird in diesem Automaten verkauft?", @@ -10187,4 +10316,4 @@ "render": "Windrad" } } -} \ No newline at end of file +} From 3d865bd82a5ee31aa6ba7796a7c3998beb1b8ff5 Mon Sep 17 00:00:00 2001 From: nilocram Date: Sun, 14 Jan 2024 20:28:26 +0000 Subject: [PATCH 016/195] Translated using Weblate (Italian) Currently translated at 27.3% (870 of 3182 strings) Translation: MapComplete/Layer translations Translate-URL: https://hosted.weblate.org/projects/mapcomplete/layers/it/ --- langs/layers/it.json | 192 ++++++++++++++++++++++++++++++++----------- 1 file changed, 143 insertions(+), 49 deletions(-) diff --git a/langs/layers/it.json b/langs/layers/it.json index dc134a2dc..a50419e9d 100644 --- a/langs/layers/it.json +++ b/langs/layers/it.json @@ -69,6 +69,15 @@ "1": { "then": "Murale" }, + "10": { + "then": "Azulejo (ornamento decorativo piastrellato spagnolo)" + }, + "11": { + "then": "Mosaico di piastrelle" + }, + "12": { + "then": "Scultura in legno" + }, "2": { "then": "Dipinto" }, @@ -92,12 +101,6 @@ }, "9": { "then": "Rilievo" - }, - "10": { - "then": "Azulejo (ornamento decorativo piastrellato spagnolo)" - }, - "11": { - "then": "Mosaico di piastrelle" } }, "question": "Che tipo di opera d’arte è questo?", @@ -110,6 +113,24 @@ "text": "Ulteriori informazioni su questo sito web" } } + }, + "artwork_subject": { + "question": "Che cosa rappresenta quest'opera d'arte?", + "render": "Quest'opera d'arte rappresenta {wikidata_label(subject:wikidata)}{wikipedia(subject:wikidata)}" + }, + "doubles_as_bench": { + "mappings": { + "0": { + "then": "Quest'opera d'arte funge anche da panchina" + }, + "1": { + "then": "Quest'opera d'arte non funge anche da panchina" + }, + "2": { + "then": "Quest'opera d'arte non funge anche da panchina" + } + }, + "question": "Quest'opera d'arte funge da panchina?" } }, "title": { @@ -121,6 +142,79 @@ "render": "Opera d’arte" } }, + "atm": { + "description": "Sportello Bancomat per prelevare denaro", + "name": "Sportelli Bancomat", + "presets": { + "0": { + "title": "Uno sportello bancomat" + } + }, + "tagRenderings": { + "cash_in": { + "mappings": { + "0": { + "then": "Probabilmente non puoi depositare dei contanti in questo sportello bancomat" + }, + "1": { + "then": "Puoi depositare dei contanti in questo sportello bancomat" + }, + "2": { + "then": "Non puoi depositare dei contanti in questo sportello bancomat" + } + }, + "question": "Puoi depositare dei contanti in questo sportello bancomat?" + }, + "cash_out": { + "mappings": { + "0": { + "then": "Puoi prelevare dei contanti da questo sportello bancomat" + }, + "1": { + "then": "Puoi prelevare dei contanti da questo sportello bancomat" + }, + "2": { + "then": "Non puoi prelevare dei contanti da questo sportello bancomat" + } + }, + "question": "Puoi prelevare dei contanti da questo sportello bancomat?" + }, + "cash_out-denominations-notes": { + "mappings": { + "0": { + "then": "Si possono prelevare banconote da 5 euro" + }, + "1": { + "then": "Si possono prelevare banconote da 10 euro" + }, + "2": { + "then": "Si possono prelevare banconote da 20 euro" + }, + "3": { + "then": "Si possono prelevare banconote da 50 euro" + }, + "4": { + "then": "Si possono prelevare banconote da 100 euro" + }, + "5": { + "then": "Non si possono prelevare banconote da 200 euro" + }, + "6": { + "then": "Non si possono prelevare banconote da 500 euro" + } + }, + "question": "Quali banconote si possono ritirare qui?" + }, + "name": { + "render": "Il nome di questo sportello bancomat è {name}" + }, + "operator": { + "freeform": { + "placeholder": "Operatore" + } + } + } + }, "bench": { "name": "Panchine", "presets": { @@ -1898,27 +1992,6 @@ "1": { "question": "Riciclo di batterie" }, - "2": { - "question": "Riciclo di confezioni per bevande" - }, - "3": { - "question": "Riciclo di lattine" - }, - "4": { - "question": "Riciclo di abiti" - }, - "5": { - "question": "Riciclo di olio da cucina" - }, - "6": { - "question": "Riciclo di olio da motore" - }, - "8": { - "question": "Riciclo di umido" - }, - "9": { - "question": "Riciclo di bottiglie di vetro" - }, "10": { "question": "Riciclo di vetro" }, @@ -1946,8 +2019,29 @@ "19": { "question": "Riciclo di secco" }, + "2": { + "question": "Riciclo di confezioni per bevande" + }, "20": { "question": "Riciclo di secco" + }, + "3": { + "question": "Riciclo di lattine" + }, + "4": { + "question": "Riciclo di abiti" + }, + "5": { + "question": "Riciclo di olio da cucina" + }, + "6": { + "question": "Riciclo di olio da motore" + }, + "8": { + "question": "Riciclo di umido" + }, + "9": { + "question": "Riciclo di bottiglie di vetro" } } }, @@ -2000,27 +2094,6 @@ "1": { "then": "Cartoni per bevande" }, - "2": { - "then": "Lattine" - }, - "3": { - "then": "Abiti" - }, - "4": { - "then": "Olio da cucina" - }, - "5": { - "then": "Olio di motore" - }, - "7": { - "then": "Verde" - }, - "8": { - "then": "Umido" - }, - "9": { - "then": "Bottiglie di vetro" - }, "10": { "then": "Vetro" }, @@ -2045,6 +2118,9 @@ "19": { "then": "Scarpe" }, + "2": { + "then": "Lattine" + }, "20": { "then": "Piccoli elettrodomestici" }, @@ -2056,6 +2132,24 @@ }, "23": { "then": "Secco" + }, + "3": { + "then": "Abiti" + }, + "4": { + "then": "Olio da cucina" + }, + "5": { + "then": "Olio di motore" + }, + "7": { + "then": "Verde" + }, + "8": { + "then": "Umido" + }, + "9": { + "then": "Bottiglie di vetro" } }, "question": "Cosa si può riciclare qui?" @@ -2856,4 +2950,4 @@ "render": "pala eolica" } } -} \ No newline at end of file +} From dbff4c9b834ef02f35951b5ecdfa2d72b4985680 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 04:01:10 +0100 Subject: [PATCH 017/195] Tooling: allow to generate only a few themes --- package.json | 1 + scripts/generateLayerOverview.ts | 172 +++++++++++++++++-------------- 2 files changed, 97 insertions(+), 76 deletions(-) diff --git a/package.json b/package.json index 284e24768..c50c85c3a 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "generate:cache:speelplekken": "npm run generate:layeroverview && vite-node scripts/generateCache.ts -- speelplekken 14 ../MapComplete-data/speelplekken_cache/ 51.20 4.35 51.09 4.56", "generate:cache:natuurpunt": "npm run generate:layeroverview && vite-node scripts/generateCache.ts -- natuurpunt 12 ../MapComplete-data/natuurpunt_cache/ 50.40 2.1 51.54 6.4 --generate-point-overview nature_reserve,visitor_information_centre", "generate:layeroverview": "export NODE_OPTIONS=\"--max-old-space-size=8192\" && vite-node scripts/generateLayerOverview.ts", + "generate:layeroverview:velopark": "export NODE_OPTIONS=\"--max-old-space-size=8192\" && vite-node scripts/generateLayerOverview.ts -- --force --themes=velopark --layers=bike_parking,maproulette_challenge", "generate:mapcomplete-changes-theme": "export NODE_OPTIONS=\"--max-old-space-size=8192\" && vite-node scripts/generateLayerOverview.ts -- --generate-change-map", "refresh:layeroverview": "export NODE_OPTIONS=\"--max-old-space-size=8192\" && vite-node scripts/generateLayerOverview.ts -- --force", "generate:licenses": "vite-node scripts/generateLicenseInfo.ts -- --no-fail", diff --git a/scripts/generateLayerOverview.ts b/scripts/generateLayerOverview.ts index ef32924e4..39e8c6424 100644 --- a/scripts/generateLayerOverview.ts +++ b/scripts/generateLayerOverview.ts @@ -14,11 +14,7 @@ import { import { Translation } from "../src/UI/i18n/Translation" import { PrepareLayer } from "../src/Models/ThemeConfig/Conversion/PrepareLayer" import { PrepareTheme } from "../src/Models/ThemeConfig/Conversion/PrepareTheme" -import { - Conversion, - DesugaringContext, - DesugaringStep, -} from "../src/Models/ThemeConfig/Conversion/Conversion" +import { Conversion, DesugaringContext, DesugaringStep } from "../src/Models/ThemeConfig/Conversion/Conversion" import { Utils } from "../src/Utils" import Script from "./Script" import { AllSharedLayers } from "../src/Customizations/AllSharedLayers" @@ -51,7 +47,7 @@ class ParseLayer extends Conversion< convert( path: string, - context: ConversionContext + context: ConversionContext, ): { parsed: LayerConfig raw: LayerConfigJson @@ -106,7 +102,7 @@ class AddIconSummary extends DesugaringStep<{ raw: LayerConfigJson; parsed: Laye const fixed = json.raw const layerConfig = json.parsed const pointRendering: PointRenderingConfig = layerConfig.mapRendering.find((pr) => - pr.location.has("point") + pr.location.has("point"), ) const defaultTags = layerConfig.GetBaseTags() fixed["_layerIcon"] = Utils.NoNull( @@ -121,7 +117,7 @@ class AddIconSummary extends DesugaringStep<{ raw: LayerConfigJson; parsed: Laye result["color"] = c } return result - }) + }), ) return { raw: fixed, parsed: layerConfig } } @@ -143,7 +139,7 @@ class LayerOverviewUtils extends Script { private static extractLayerIdsFrom( themeFile: LayoutConfigJson, - includeInlineLayers = true + includeInlineLayers = true, ): string[] { const publicLayerIds = [] if (!Array.isArray(themeFile.layers)) { @@ -206,10 +202,10 @@ class LayerOverviewUtils extends Script { | LayerConfigJson | string | { - builtin - } - )[] - }[] + builtin + } + )[] + }[], ) { const perId = new Map() for (const theme of themes) { @@ -250,7 +246,7 @@ class LayerOverviewUtils extends Script { writeFileSync( "./src/assets/generated/theme_overview.json", JSON.stringify(sorted, null, " "), - { encoding: "utf8" } + { encoding: "utf8" }, ) } @@ -262,7 +258,7 @@ class LayerOverviewUtils extends Script { writeFileSync( `${LayerOverviewUtils.themePath}${theme.id}.json`, JSON.stringify(theme, null, " "), - { encoding: "utf8" } + { encoding: "utf8" }, ) } @@ -273,13 +269,13 @@ class LayerOverviewUtils extends Script { writeFileSync( `${LayerOverviewUtils.layerPath}${layer.id}.json`, JSON.stringify(layer, null, " "), - { encoding: "utf8" } + { encoding: "utf8" }, ) } getSharedTagRenderings( doesImageExist: DoesImageExist, - bootstrapTagRenderings: Map = null + bootstrapTagRenderings: Map = null, ): Map { const prepareLayer = new PrepareLayer({ tagRenderings: bootstrapTagRenderings, @@ -340,8 +336,8 @@ class LayerOverviewUtils extends Script { if (contents.indexOf(" 0) { console.warn( "The SVG at " + - path + - " contains a `text`-tag. This is highly discouraged. Every machine viewing your theme has their own font libary, and the font you choose might not be present, resulting in a different font being rendered. Solution: open your .svg in inkscape (or another program), select the text and convert it to a path" + path + + " contains a `text`-tag. This is highly discouraged. Every machine viewing your theme has their own font libary, and the font you choose might not be present, resulting in a different font being rendered. Solution: open your .svg in inkscape (or another program), select the text and convert it to a path", ) errCount++ } @@ -353,6 +349,12 @@ class LayerOverviewUtils extends Script { async main(args: string[]) { console.log("Generating layer overview...") + const themeWhitelist = new Set(args.find(a => a.startsWith("--themes=")) + ?.substring("--themes=".length)?.split(",") ?? []) + + const layerWhitelist = new Set(args.find(a => a.startsWith("--layers=")) + ?.substring("--layers=".length)?.split(",") ?? []) + const start = new Date() const forceReload = args.some((a) => a == "--force") @@ -361,7 +363,7 @@ class LayerOverviewUtils extends Script { licensePaths.add(licenses[i].path) } const doesImageExist = new DoesImageExist(licensePaths, existsSync) - const sharedLayers = this.buildLayerIndex(doesImageExist, forceReload) + const sharedLayers = this.buildLayerIndex(doesImageExist, forceReload, layerWhitelist) const priviliged = new Set(Constants.priviliged_layers) sharedLayers.forEach((_, key) => { @@ -379,15 +381,18 @@ class LayerOverviewUtils extends Script { licensePaths, sharedLayers, recompiledThemes, - forceReload + forceReload, + themeWhitelist ) - writeFileSync( - "./src/assets/generated/known_layers.json", - JSON.stringify({ - layers: Array.from(sharedLayers.values()).filter((l) => l.id !== "favourite"), - }) - ) + if (recompiledThemes.length > 0){ + writeFileSync( + "./src/assets/generated/known_layers.json", + JSON.stringify({ + layers: Array.from(sharedLayers.values()).filter((l) => l.id !== "favourite"), + }), + ) + } const mcChangesPath = "./assets/themes/mapcomplete-changes/mapcomplete-changes.json" if ( @@ -406,7 +411,7 @@ class LayerOverviewUtils extends Script { const proto: LayoutConfigJson = JSON.parse( readFileSync("./assets/themes/mapcomplete-changes/mapcomplete-changes.proto.json", { encoding: "utf8", - }) + }), ) const protolayer = ( proto.layers.filter((l) => l["id"] === "mapcomplete-changes")[0] @@ -423,29 +428,31 @@ class LayerOverviewUtils extends Script { layers: ScriptUtils.getLayerFiles().map((f) => f.parsed), themes: ScriptUtils.getThemeFiles().map((f) => f.parsed), }, - ConversionContext.construct([], []) + ConversionContext.construct([], []), ) for (const [_, theme] of sharedThemes) { theme.layers = theme.layers.filter( - (l) => Constants.added_by_default.indexOf(l["id"]) < 0 + (l) => Constants.added_by_default.indexOf(l["id"]) < 0, ) } - writeFileSync( - "./src/assets/generated/known_themes.json", - JSON.stringify({ - themes: Array.from(sharedThemes.values()), - }) - ) + if(recompiledThemes.length > 0) { + writeFileSync( + "./src/assets/generated/known_themes.json", + JSON.stringify({ + themes: Array.from(sharedThemes.values()), + }), + ) + } const end = new Date() const millisNeeded = end.getTime() - start.getTime() if (AllSharedLayers.getSharedLayersConfigs().size == 0) { console.error( "This was a bootstrapping-run. Run generate layeroverview again!(" + - millisNeeded + - " ms)" + millisNeeded + + " ms)", ) } else { const green = (s) => "\x1b[92m" + s + "\x1b[0m" @@ -456,7 +463,7 @@ class LayerOverviewUtils extends Script { private parseLayer( doesImageExist: DoesImageExist, prepLayer: PrepareLayer, - sharedLayerPath: string + sharedLayerPath: string, ): { raw: LayerConfigJson parsed: LayerConfig @@ -467,14 +474,15 @@ class LayerOverviewUtils extends Script { const parsed = parser.convertStrict(sharedLayerPath, context) const result = AddIconSummary.singleton.convertStrict( parsed, - context.inOperation("AddIconSummary") + context.inOperation("AddIconSummary"), ) return { ...result, context } } private buildLayerIndex( doesImageExist: DoesImageExist, - forceReload: boolean + forceReload: boolean, + whitelist: Set ): Map { // First, we expand and validate all builtin layers. These are written to src/assets/generated/layers // At the same time, an index of available layers is built. @@ -492,6 +500,12 @@ class LayerOverviewUtils extends Script { const recompiledLayers: string[] = [] let warningCount = 0 for (const sharedLayerPath of ScriptUtils.getLayerPaths()) { + if(whitelist.size > 0){ + const idByPath = sharedLayerPath.split("/").at(-1).split(".")[0] + if(Constants.priviliged_layers.indexOf( idByPath) < 0 && !whitelist.has(idByPath)){ + continue + } + } { const targetPath = LayerOverviewUtils.layerPath + @@ -520,17 +534,17 @@ class LayerOverviewUtils extends Script { console.log( "Recompiled layers " + - recompiledLayers.join(", ") + - " and skipped " + - skippedLayers.length + - " layers. Detected " + - warningCount + - " warnings" + recompiledLayers.join(", ") + + " and skipped " + + skippedLayers.length + + " layers. Detected " + + warningCount + + " warnings", ) // We always need the calculated tags of 'usersettings', so we export them separately this.extractJavascriptCodeForLayer( state.sharedLayers.get("usersettings"), - "./src/Logic/State/UserSettingsMetaTagging.ts" + "./src/Logic/State/UserSettingsMetaTagging.ts", ) return sharedLayers @@ -547,8 +561,8 @@ class LayerOverviewUtils extends Script { private extractJavascriptCode(themeFile: LayoutConfigJson) { const allCode = [ "import {Feature} from 'geojson'", - 'import { ExtraFuncType } from "../../../Logic/ExtraFunctions";', - 'import { Utils } from "../../../Utils"', + "import { ExtraFuncType } from \"../../../Logic/ExtraFunctions\";", + "import { Utils } from \"../../../Utils\"", "export class ThemeMetaTagging {", " public static readonly themeName = " + JSON.stringify(themeFile.id), "", @@ -560,8 +574,8 @@ class LayerOverviewUtils extends Script { allCode.push( " public metaTaggging_for_" + - id + - "(feat: Feature, helperFunctions: Record Function>) {" + id + + "(feat: Feature, helperFunctions: Record Function>) {", ) allCode.push(" const {" + ExtraFunctions.types.join(", ") + "} = helperFunctions") for (const line of code) { @@ -572,10 +586,10 @@ class LayerOverviewUtils extends Script { if (!isStrict) { allCode.push( " Utils.AddLazyProperty(feat.properties, '" + - attributeName + - "', () => " + - expression + - " ) " + attributeName + + "', () => " + + expression + + " ) ", ) } else { attributeName = attributeName.substring(0, attributeName.length - 1).trim() @@ -620,7 +634,7 @@ class LayerOverviewUtils extends Script { const code = l.calculatedTags ?? [] allCode.push( - " public metaTaggging_for_" + l.id + "(feat: {properties: Record}) {" + " public metaTaggging_for_" + l.id + "(feat: {properties: Record}) {", ) for (const line of code) { const firstEq = line.indexOf("=") @@ -630,10 +644,10 @@ class LayerOverviewUtils extends Script { if (!isStrict) { allCode.push( " Utils.AddLazyProperty(feat.properties, '" + - attributeName + - "', () => " + - expression + - " ) " + attributeName + + "', () => " + + expression + + " ) ", ) } else { attributeName = attributeName.substring(0, attributeName.length - 2).trim() @@ -657,20 +671,21 @@ class LayerOverviewUtils extends Script { licensePaths: Set, sharedLayers: Map, recompiledThemes: string[], - forceReload: boolean + forceReload: boolean, + whitelist: Set ): Map { console.log(" ---------- VALIDATING BUILTIN THEMES ---------") const themeFiles = ScriptUtils.getThemeFiles() const fixed = new Map() const publicLayers = LayerOverviewUtils.publicLayerIdsFrom( - themeFiles.map((th) => th.parsed) + themeFiles.map((th) => th.parsed), ) const convertState: DesugaringContext = { sharedLayers, tagRenderings: this.getSharedTagRenderings( - new DoesImageExist(licensePaths, existsSync) + new DoesImageExist(licensePaths, existsSync), ), publicLayers, } @@ -695,20 +710,23 @@ class LayerOverviewUtils extends Script { const themeInfo = themeFiles[i] const themePath = themeInfo.path let themeFile = themeInfo.parsed + if(whitelist.size > 0 && !whitelist.has(themeFile.id)){ + continue + } const targetPath = LayerOverviewUtils.themePath + "/" + themePath.substring(themePath.lastIndexOf("/")) const usedLayers = Array.from( - LayerOverviewUtils.extractLayerIdsFrom(themeFile, false) + LayerOverviewUtils.extractLayerIdsFrom(themeFile, false), ).map((id) => LayerOverviewUtils.layerPath + id + ".json") if (!forceReload && !this.shouldBeUpdated([themePath, ...usedLayers], targetPath)) { fixed.set( themeFile.id, JSON.parse( - readFileSync(LayerOverviewUtils.themePath + themeFile.id + ".json", "utf8") - ) + readFileSync(LayerOverviewUtils.themePath + themeFile.id + ".json", "utf8"), + ), ) ScriptUtils.erasableLog("Skipping", themeFile.id) skippedThemes.push(themeFile.id) @@ -719,23 +737,23 @@ class LayerOverviewUtils extends Script { new PrevalidateTheme().convertStrict( themeFile, - ConversionContext.construct([themePath], ["PrepareLayer"]) + ConversionContext.construct([themePath], ["PrepareLayer"]), ) try { themeFile = new PrepareTheme(convertState, { skipDefaultLayers: true, }).convertStrict( themeFile, - ConversionContext.construct([themePath], ["PrepareLayer"]) + ConversionContext.construct([themePath], ["PrepareLayer"]), ) new ValidateThemeAndLayers( new DoesImageExist(licensePaths, existsSync, knownTagRenderings), themePath, true, - knownTagRenderings + knownTagRenderings, ).convertStrict( themeFile, - ConversionContext.construct([themePath], ["PrepareLayer"]) + ConversionContext.construct([themePath], ["PrepareLayer"]), ) if (themeFile.icon.endsWith(".svg")) { @@ -777,6 +795,7 @@ class LayerOverviewUtils extends Script { } } + if(whitelist.size == 0){ this.writeSmallOverview( Array.from(fixed.values()).map((t) => { return { @@ -789,15 +808,16 @@ class LayerOverviewUtils extends Script { .OnEveryLanguage((s) => parse_html(s).textContent).translations, mustHaveLanguage: t.mustHaveLanguage?.length > 0, } - }) + }), ) + } console.log( "Recompiled themes " + - recompiledThemes.join(", ") + - " and skipped " + - skippedThemes.length + - " themes" + recompiledThemes.join(", ") + + " and skipped " + + skippedThemes.length + + " themes", ) return fixed From e731c677b920f435c054b00ab7f8d4a69ad27b01 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 04:07:02 +0100 Subject: [PATCH 018/195] Scripts: add velopark export script --- scripts/velopark/veloParkToGeojson.ts | 55 +++++++++++++++++++++++++++ src/Logic/Web/VeloparkLoader.ts | 44 ++++++++++++--------- 2 files changed, 80 insertions(+), 19 deletions(-) create mode 100644 scripts/velopark/veloParkToGeojson.ts diff --git a/scripts/velopark/veloParkToGeojson.ts b/scripts/velopark/veloParkToGeojson.ts new file mode 100644 index 000000000..a3226cf39 --- /dev/null +++ b/scripts/velopark/veloParkToGeojson.ts @@ -0,0 +1,55 @@ +import Script from "../Script" +import { Utils } from "../../src/Utils" +import VeloparkLoader, { VeloparkData } from "../../src/Logic/Web/VeloparkLoader" +import fs from "fs" +import OverpassFeatureSource from "../../src/Logic/FeatureSource/Sources/OverpassFeatureSource" +import { Overpass } from "../../src/Logic/Osm/Overpass" +import { RegexTag } from "../../src/Logic/Tags/RegexTag" +import Constants from "../../src/Models/Constants" +import { ImmutableStore } from "../../src/Logic/UIEventSource" +import { BBox } from "../../src/Logic/BBox" +class VeloParkToGeojson extends Script { + constructor() { + super("Downloads the latest Velopark data and converts it to a geojson, which will be saved at the current directory") + } + + async main(args: string[]): Promise { + console.log("Downloading velopark data") + // Download data for NIS-code 1000. 1000 means: all of belgium + const url = "https://www.velopark.be/api/parkings/1000" + const data = await Utils.downloadJson(url) + + const bboxBelgium = new BBox([[2.51357303225, 49.5294835476],[ 6.15665815596, 51.4750237087]]) + const alreadyLinkedQuery = new Overpass(new RegexTag("ref:velopark", /.+/), + [], + Constants.defaultOverpassUrls[0], + new ImmutableStore(60*5), + false + ) + const alreadyLinkedFeatures = await alreadyLinkedQuery.queryGeoJson(bboxBelgium) + const seenIds = new Set(alreadyLinkedFeatures[0].features.map(f => f.properties["ref:velopark"])) + const features = data.map(f => VeloparkLoader.convert(f)) + .filter(f => !seenIds.has(f.properties["ref:velopark"])) + + const allProperties = new Set() + for (const feature of features) { + Object.keys(feature.properties).forEach(k => allProperties.add(k)) + } + allProperties.delete("ref:velopark") + for (const feature of features) { + allProperties.forEach(k => { + delete feature.properties[k] + }) + } + + fs.writeFileSync("velopark_id_only_export_" + new Date().toISOString() + ".geojson", JSON.stringify({ + "type": "FeatureCollection", + features, + }, null, " ")) + + } + + +} + +new VeloParkToGeojson().run() diff --git a/src/Logic/Web/VeloparkLoader.ts b/src/Logic/Web/VeloparkLoader.ts index 30d9b6268..1101aaaaf 100644 --- a/src/Logic/Web/VeloparkLoader.ts +++ b/src/Logic/Web/VeloparkLoader.ts @@ -1,4 +1,4 @@ -import { Feature, Point } from "geojson" +import { Feature, Geometry, Point } from "geojson" import { OH } from "../../UI/OpeningHours/OpeningHours" import EmailValidator from "../../UI/InputElement/Validators/EmailValidator" import PhoneValidator from "../../UI/InputElement/Validators/PhoneValidator" @@ -18,12 +18,13 @@ export default class VeloparkLoader { private static readonly coder = new CountryCoder( Constants.countryCoderEndpoint, - Utils.downloadJson + Utils.downloadJson, ) - public static convert(veloparkData: VeloparkData): Feature { + public static convert(veloparkData: VeloparkData): Feature { const properties: { + "ref:velopark":string, "operator:email"?: string, "operator:phone"?: string, fee?: string, @@ -31,7 +32,9 @@ export default class VeloparkLoader { access?: string maxstay?: string operator?: string - } = {} + } = { + "ref:velopark": veloparkData["id"] ?? veloparkData["@id"] + } properties.operator = veloparkData.operatedBy?.companyName @@ -53,36 +56,39 @@ export default class VeloparkLoader { } }) - let coordinates: [number, number] = undefined + let geometry = veloparkData.geometry for (const g of veloparkData["@graph"]) { - coordinates = [g.geo[0].longitude, g.geo[0].latitude] + if (g.geo[0]) { + geometry = { type: "Point", coordinates: [g.geo[0].longitude, g.geo[0].latitude] } + } if (g.maximumParkingDuration?.endsWith("D") && g.maximumParkingDuration?.startsWith("P")) { const duration = g.maximumParkingDuration.substring(1, g.maximumParkingDuration.length - 1) properties.maxstay = duration + " days" } properties.access = g.publicAccess ? "yes" : "no" const prefix = "http://schema.org/" - const oh = OH.simplify(g.openingHoursSpecification.map(spec => { - const dayOfWeek = spec.dayOfWeek.substring(prefix.length, prefix.length + 2).toLowerCase() - const startHour = spec.opens - const endHour = spec.closes === "23:59" ? "24:00" : spec.closes - const merged = OH.MergeTimes(OH.ParseRule(dayOfWeek + " " + startHour + "-" + endHour)) - return OH.ToString(merged) - }).join("; ")) - properties.opening_hours = oh - - if (g.priceSpecification[0]) { + if (g.openingHoursSpecification) { + const oh = OH.simplify(g.openingHoursSpecification.map(spec => { + const dayOfWeek = spec.dayOfWeek.substring(prefix.length, prefix.length + 2).toLowerCase() + const startHour = spec.opens + const endHour = spec.closes === "23:59" ? "24:00" : spec.closes + const merged = OH.MergeTimes(OH.ParseRule(dayOfWeek + " " + startHour + "-" + endHour)) + return OH.ToString(merged) + }).join("; ")) + properties.opening_hours = oh + } + if (g.priceSpecification?.[0]) { properties.fee = g.priceSpecification[0].freeOfCharge ? "no" : "yes" } } - - return { type: "Feature", properties, geometry: { type: "Point", coordinates } } + return { type: "Feature", properties, geometry } } } -interface VeloparkData { +export interface VeloparkData { + geometry?: Geometry "@context": any, "@id": string // "https://data.velopark.be/data/NMBS_541", "@type": "BicycleParkingStation", From 593d7bd07ac520875c5acfcab5df364fb04dd77c Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 04:09:58 +0100 Subject: [PATCH 019/195] Fix: correctly flatten special cases --- src/Models/Constants.ts | 2 ++ .../ThemeConfig/Conversion/PrepareLayer.ts | 35 +++++++++++++------ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/Models/Constants.ts b/src/Models/Constants.ts index 326087e09..08beb6a97 100644 --- a/src/Models/Constants.ts +++ b/src/Models/Constants.ts @@ -35,6 +35,8 @@ export default class Constants { "current_view", "import_candidate", "usersettings", + "icons", + "filters" ] as const /** * Layer IDs of layers which have special properties through built-in hooks diff --git a/src/Models/ThemeConfig/Conversion/PrepareLayer.ts b/src/Models/ThemeConfig/Conversion/PrepareLayer.ts index 32dbd513e..16611e0d7 100644 --- a/src/Models/ThemeConfig/Conversion/PrepareLayer.ts +++ b/src/Models/ThemeConfig/Conversion/PrepareLayer.ts @@ -32,7 +32,6 @@ import { ConfigMeta } from "../../../UI/Studio/configMeta" import LineRenderingConfigJson from "../Json/LineRenderingConfigJson" import { ConversionContext } from "./ConversionContext" import { ExpandRewrite } from "./ExpandRewrite" -import { ALL } from "node:dns" class ExpandFilter extends DesugaringStep { private static readonly predefinedFilters = ExpandFilter.load_filters() @@ -700,6 +699,15 @@ export class RewriteSpecial extends DesugaringStep { ) } + private static escapeStr(v: string): string{ + return v + .replace(/,/g, "&COMMA") + .replace(/\{/g, "&LBRACE") + .replace(/}/g, "&RBRACE") + .replace(/\(/g, "&LPARENS") + .replace(/\)/g, "&RPARENS") + } + /** * Does the heavy lifting and conversion * @@ -756,6 +764,19 @@ export class RewriteSpecial extends DesugaringStep { * const context = ConversionContext.test() * RewriteSpecial.convertIfNeeded(special, context) // => {"en": "

Entrances

This building has {_entrances_count} entrances:{multi(_entrance_properties_with_width,An entrance of &LBRACEcanonical&LPARENSwidth&RPARENS&RBRACE)}{_entrances_count_without_width_count} entrances don't have width information yet"} * context.getAll("error") // => [] + * + * // another actual test + * const special = { + * "special":{ + * "type": "multi", + * "key": "_nearby_bicycle_parkings:props", + * "tagrendering": { + * "*": "{id} ({distance}m) {tagApply(a,b,c)}" + * } + * }} + * const context = ConversionContext.test() + * RewriteSpecial.convertIfNeeded(special, context) // => {"*": "{multi(_nearby_bicycle_parkings:props,&LBRACEid&RBRACE &LPARENS&LBRACEdistance&RBRACEm&RPARENS &LBRACEtagApply&LPARENSa&COMMAb&COMMAc&RPARENS&RBRACE)}"} + * context.getAll("error") // => [] */ private static convertIfNeeded( input: @@ -838,7 +859,7 @@ export class RewriteSpecial extends DesugaringStep { const translatedArgs = argNamesList .map((nm) => special[nm]) .filter((v) => v !== undefined) - .filter((v) => Translations.isProbablyATranslation(v)) + .filter((v) => Translations.isProbablyATranslation(v) || v["*"] !== undefined) for (const translatedArg of translatedArgs) { for (const ln of Object.keys(translatedArg)) { foundLanguages.add(ln) @@ -856,7 +877,7 @@ export class RewriteSpecial extends DesugaringStep { } if (foundLanguages.size === 0) { - const args = argNamesList.map((nm) => special[nm] ?? "").join(",") + const args = argNamesList.map((nm) => RewriteSpecial.escapeStr(special[nm] ?? "")).join(",") return { "*": `{${type}(${args})}`, } @@ -874,13 +895,7 @@ export class RewriteSpecial extends DesugaringStep { } if (typeof v === "string") { - const txt = v - .replace(/,/g, "&COMMA") - .replace(/\{/g, "&LBRACE") - .replace(/}/g, "&RBRACE") - .replace(/\(/g, "&LPARENS") - .replace(/\)/g, "&RPARENS") - args.push(txt) + args.push(RewriteSpecial.escapeStr(v)) } else if (typeof v === "object") { args.push(JSON.stringify(v)) } else { From c298e16f02d9fd7e049d7dd4ba401b21aae3ace7 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 04:12:13 +0100 Subject: [PATCH 020/195] ExtraFunctions: closestN now can work with multiple layers to pick from --- src/Logic/ExtraFunctions.ts | 60 +++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/src/Logic/ExtraFunctions.ts b/src/Logic/ExtraFunctions.ts index 497ecea46..6575a543f 100644 --- a/src/Logic/ExtraFunctions.ts +++ b/src/Logic/ExtraFunctions.ts @@ -15,7 +15,7 @@ export interface ExtraFuncParams { */ getFeaturesWithin: ( layerId: string, - bbox: BBox + bbox: BBox, ) => Feature>[][] getFeatureById: (id: string) => Feature> } @@ -71,7 +71,7 @@ class EnclosingFunc implements ExtraFunction { if ( GeoOperations.completelyWithin( feat, - >otherFeature + >otherFeature, ) ) { result.push({ feat: otherFeature }) @@ -162,7 +162,7 @@ class IntersectionFunc implements ExtraFunction { for (const otherFeature of otherFeatures) { const intersections = GeoOperations.LineIntersections( feat, - >>otherFeature + >>otherFeature, ) if (intersections.length === 0) { continue @@ -192,7 +192,7 @@ class DistanceToFunc implements ExtraFunction { // Feature._lon and ._lat is conveniently place by one of the other metatags return GeoOperations.distanceBetween( [arg0, lat], - GeoOperations.centerpointCoordinates(feature) + GeoOperations.centerpointCoordinates(feature), ) } if (typeof arg0 === "string") { @@ -207,7 +207,7 @@ class DistanceToFunc implements ExtraFunction { // arg0 is probably a geojsonfeature return GeoOperations.distanceBetween( GeoOperations.centerpointCoordinates(arg0), - GeoOperations.centerpointCoordinates(feature) + GeoOperations.centerpointCoordinates(feature), ) } } @@ -252,22 +252,29 @@ class ClosestNObjectFunc implements ExtraFunction { static GetClosestNFeatures( params: ExtraFuncParams, feature: any, - features: string | Feature[], - options?: { maxFeatures?: number; uniqueTag?: string | undefined; maxDistance?: number } + features: string | string[] | Feature[], + options?: { maxFeatures?: number; uniqueTag?: string | undefined; maxDistance?: number }, ): { feat: any; distance: number }[] { const maxFeatures = options?.maxFeatures ?? 1 const maxDistance = options?.maxDistance ?? 500 const uniqueTag: string | undefined = options?.uniqueTag let allFeatures: Feature[][] if (typeof features === "string") { - const name = features - const bbox = GeoOperations.bbox( - GeoOperations.buffer(GeoOperations.bbox(feature), maxDistance) - ) - const coors = <[number, number][]>bbox.geometry.coordinates - allFeatures = params.getFeaturesWithin(name, new BBox(coors)) + features = [features] } else { - allFeatures = [features] + allFeatures = [] + for (const spec of features) { + if (typeof spec === "string") { + const name = spec + const bbox = GeoOperations.bbox( + GeoOperations.buffer(GeoOperations.bbox(feature), maxDistance), + ) + const coors = <[number, number][]>bbox.geometry.coordinates + allFeatures.push(...params.getFeaturesWithin(name, new BBox(coors))) + } else { + allFeatures.push([spec]) + } + } } if (features === undefined) { return @@ -278,6 +285,9 @@ class ClosestNObjectFunc implements ExtraFunction { for (const feats of allFeatures) { for (const otherFeature of feats) { + if (otherFeature.properties === undefined) { + console.warn("OtherFeature does not have properties:", otherFeature) + } if ( otherFeature === feature || otherFeature.properties.id === feature.properties.id @@ -286,14 +296,14 @@ class ClosestNObjectFunc implements ExtraFunction { } const distance = GeoOperations.distanceBetween( GeoOperations.centerpointCoordinates(otherFeature), - selfCenter + selfCenter, ) if (distance === undefined || distance === null || isNaN(distance)) { console.error( "Could not calculate the distance between", feature, "and", - otherFeature + otherFeature, ) throw "Undefined distance!" } @@ -303,7 +313,7 @@ class ClosestNObjectFunc implements ExtraFunction { "Got a suspiciously zero distance between", otherFeature, "and self-feature", - feature + feature, ) } @@ -337,7 +347,7 @@ class ClosestNObjectFunc implements ExtraFunction { const uniqueTagsMatch = otherFeature.properties[uniqueTag] !== undefined && closestFeature.feat.properties[uniqueTag] === - otherFeature.properties[uniqueTag] + otherFeature.properties[uniqueTag] if (uniqueTagsMatch) { targetIndex = -1 if (closestFeature.distance > distance) { @@ -430,7 +440,7 @@ class GetParsed implements ExtraFunction { return parsed } catch (e) { console.warn( - "Could not parse property " + key + " due to: " + e + ", the value is " + value + "Could not parse property " + key + " due to: " + e + ", the value is " + value, ) return undefined } @@ -454,15 +464,15 @@ export class ExtraFunctions { ]), "To enable this feature, add a field `calculatedTags` in the layer object, e.g.:", "````", - '"calculatedTags": [', - ' "_someKey=javascript-expression (lazy execution)",', - ' "_some_other_key:=javascript expression (strict execution)', - ' "name=feat.properties.name ?? feat.properties.ref ?? feat.properties.operator",', + "\"calculatedTags\": [", + " \"_someKey=javascript-expression (lazy execution)\",", + " \"_some_other_key:=javascript expression (strict execution)", + " \"name=feat.properties.name ?? feat.properties.ref ?? feat.properties.operator\",", " \"_distanceCloserThen3Km=distanceTo(feat)( some_lon, some_lat) < 3 ? 'yes' : 'no'\" ", " ]", "````", "", - "By using `:=` as separator, the attribute will be calculated as soone as the data is loaded (strict evaluation)", + "By using `:=` as separator, the attribute will be calculated as soon as the data is loaded (strict evaluation)", "The default behaviour, using `=` as separator, is lazy loading", "", "The above code will be executed for every feature in the layer. The feature is accessible as `feat` and is an amended geojson object:", @@ -496,7 +506,7 @@ export class ExtraFunctions { ] public static constructHelpers( - params: ExtraFuncParams + params: ExtraFuncParams, ): Record Function> { const record: Record Function> = {} for (const f of ExtraFunctions.allFuncs) { From a70336ee16f62fb561f804f37b78c4bbc1eae4a2 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 04:14:55 +0100 Subject: [PATCH 021/195] SpecialViz: Remove obsolete layer parameter from Minimap --- src/UI/Popup/MinimapViz.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/UI/Popup/MinimapViz.ts b/src/UI/Popup/MinimapViz.ts index fc0a1a61f..444d0d49a 100644 --- a/src/UI/Popup/MinimapViz.ts +++ b/src/UI/Popup/MinimapViz.ts @@ -33,9 +33,8 @@ export class MinimapViz implements SpecialVisualization { tagSource: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig ) { - if (state === undefined || feature === undefined || layer.source === undefined) { + if (state === undefined || feature === undefined) { return undefined } const keys = [...args] From f8644dd4adc8fb70a06df379ff4835c007f16538 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 04:17:02 +0100 Subject: [PATCH 022/195] SpecialViz: tag_apply_button now works with a generic MR-id --- src/UI/Popup/TagApplyButton.ts | 39 ++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/UI/Popup/TagApplyButton.ts b/src/UI/Popup/TagApplyButton.ts index df4c2e083..7c5e632ed 100644 --- a/src/UI/Popup/TagApplyButton.ts +++ b/src/UI/Popup/TagApplyButton.ts @@ -18,6 +18,9 @@ import { IndexedFeatureSource } from "../../Logic/FeatureSource/FeatureSource" import { Feature } from "geojson" import LayerConfig from "../../Models/ThemeConfig/LayerConfig" import Maproulette from "../../Logic/Maproulette" +import SvelteUIElement from "../Base/SvelteUIElement" +import Icon from "../Map/Icon.svelte" +import { Map } from "maplibre-gl" export default class TagApplyButton implements AutoAction, SpecialVisualization { public readonly funcName = "tag_apply" @@ -45,9 +48,9 @@ export default class TagApplyButton implements AutoAction, SpecialVisualization doc: "If specified, applies the the tags onto _another_ object. The id will be read from properties[id_of_object_to_apply_this_one] of the selected object. The tags are still calculated based on the tags of the _selected_ element", }, { - name: "maproulette_task_id", + name: "maproulette_id", defaultValue: undefined, - doc: "If specified, this maproulette-challenge will be closed when the tags are applied", + doc: "If specified, this maproulette-challenge will be closed when the tags are applied. This should be the ID of the task, _not_ the task_id.", }, ] public readonly example = @@ -81,7 +84,7 @@ export default class TagApplyButton implements AutoAction, SpecialVisualization for (const [key, value] of tgsSpec) { if (value.indexOf("$") >= 0) { let parts = value.split("$") - // THe first of the split won't start with a '$', so no substitution needed + // The first item of the split won't start with a '$', so no substitution needed let actualValue = parts[0] parts.shift() @@ -111,7 +114,6 @@ export default class TagApplyButton implements AutoAction, SpecialVisualization while (spec.length > 0) { const [part] = spec.match(/((\\;)|[^;])*/) - console.log("Spec is", part, spec) spec = spec.substring(part.length + 1) // +1 to remove the pending ';' as well const kv = part.split("=").map((s) => s.trim().replace("\\;", ";")) if (kv.length == 2) { @@ -133,12 +135,8 @@ export default class TagApplyButton implements AutoAction, SpecialVisualization } public async applyActionOn( - feature: Feature, - state: { - layout: LayoutConfig - changes: Changes - indexedFeatures: IndexedFeatureSource - }, + _: Feature, + state: SpecialVisualizationState, tags: UIEventSource, args: string[] ): Promise { @@ -156,14 +154,22 @@ export default class TagApplyButton implements AutoAction, SpecialVisualization } ) await state.changes.applyAction(changeAction) + try { + state.selectedElement.setData(state.indexedFeatures.featuresById.data.get(targetId)) + }catch (e) { + console.error(e) + } const maproulette_id_key = args[4] if (maproulette_id_key) { - const maproulette_id = Number(tags.data[maproulette_id_key]) - await Maproulette.singleton.closeTask(maproulette_id, Maproulette.STATUS_FIXED, { + const maproulette_id = tags.data[ maproulette_id_key] + const maproulette_feature= state.indexedFeatures.featuresById.data.get( + maproulette_id) + const maproulette_task_id = Number(maproulette_feature.properties.mr_taskId) + await Maproulette.singleton.closeTask(maproulette_task_id, Maproulette.STATUS_FIXED, { comment: "Tags are copied onto " + targetId + " with MapComplete", }) - tags.data["mr_taskStatus"] = "Fixed" - tags.ping() + maproulette_feature.properties["mr_taskStatus"] = "Fixed" + state.featureProperties.getStore(maproulette_id).ping() } } @@ -180,6 +186,7 @@ export default class TagApplyButton implements AutoAction, SpecialVisualization if (image === "" || image === "undefined") { image = undefined } + const targetIdKey = args[3] const t = Translations.t.general.apply_button @@ -195,9 +202,9 @@ export default class TagApplyButton implements AutoAction, SpecialVisualization }) ).SetClass("subtle") const self = this - const applied = new UIEventSource(tags?.data?.["mr_taskStatus"] !== "Created") // This will default to 'false' for non-maproulette challenges + const applied = new UIEventSource(tags?.data?.["mr_taskStatus"] !== undefined && tags?.data?.["mr_taskStatus"] !== "Created") // This will default to 'false' for non-maproulette challenges const applyButton = new SubtleButton( - image, + new SvelteUIElement(Icon, {icon: image}), new Combine([msg, tagsExplanation]).SetClass("flex flex-col") ).onClick(async () => { applied.setData(true) From 7872f221512797ee82ea1e92455326baaa9874ab Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 04:17:59 +0100 Subject: [PATCH 023/195] Themes: add 'link' as builtin, available Icon --- src/Models/Constants.ts | 1 + src/UI/Map/Icon.svelte | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/Models/Constants.ts b/src/Models/Constants.ts index 08beb6a97..f2d6f43b2 100644 --- a/src/Models/Constants.ts +++ b/src/Models/Constants.ts @@ -137,6 +137,7 @@ export default class Constants { "close", "heart", "heart_outline", + "link", ] as const public static readonly defaultPinIcons: string[] = Constants._defaultPinIcons diff --git a/src/UI/Map/Icon.svelte b/src/UI/Map/Icon.svelte index 7bec894bc..b8523f50d 100644 --- a/src/UI/Map/Icon.svelte +++ b/src/UI/Map/Icon.svelte @@ -31,6 +31,7 @@ import Mastodon from "../../assets/svg/Mastodon.svelte" import Party from "../../assets/svg/Party.svelte" import AddSmall from "../../assets/svg/AddSmall.svelte" + import { LinkIcon } from "@babeard/svelte-heroicons/mini" /** * Renders a single icon. @@ -114,6 +115,8 @@ {:else if icon === "addSmall"} + {:else if icon === "link"} + {:else} {/if} From 915cad225353912c194960dddc00c396356277a8 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 04:20:25 +0100 Subject: [PATCH 024/195] SpecialVis: allow import flows to work with multiple target layers --- .../NewPointLocationInput.svelte | 236 ++++++++------- src/UI/Popup/ImportButtons/ImportFlow.svelte | 96 +++--- src/UI/Popup/ImportButtons/ImportFlow.ts | 56 ++-- .../ImportButtons/PointImportButtonViz.ts | 2 +- .../ImportButtons/PointImportFlow.svelte | 7 +- src/UI/SpecialVisualizations.ts | 281 ++++++++++-------- 6 files changed, 349 insertions(+), 329 deletions(-) diff --git a/src/UI/BigComponents/NewPointLocationInput.svelte b/src/UI/BigComponents/NewPointLocationInput.svelte index 5625910d9..bf17a273a 100644 --- a/src/UI/BigComponents/NewPointLocationInput.svelte +++ b/src/UI/BigComponents/NewPointLocationInput.svelte @@ -1,129 +1,135 @@ dispatch("click", data)} - mapProperties={initialMapProperties} - value={preciseLocation} initialCoordinate={coordinate} - maxDistanceInMeters="50" + {map} + mapProperties={initialMapProperties} + maxDistanceInMeters={50} + on:click={(data) => dispatch("click", data)} + value={preciseLocation} > diff --git a/src/UI/Popup/ImportButtons/ImportFlow.svelte b/src/UI/Popup/ImportButtons/ImportFlow.svelte index 415d4b897..4c766c25e 100644 --- a/src/UI/Popup/ImportButtons/ImportFlow.svelte +++ b/src/UI/Popup/ImportButtons/ImportFlow.svelte @@ -1,41 +1,57 @@ @@ -44,13 +60,13 @@ {#if $canBeImported.extraHelp} {/if} - {:else if !$isDisplayed} + {:else if undisplayedLayer !== undefined}
@@ -60,7 +76,7 @@ class="flex w-full gap-x-1" on:click={() => { abort() - state.guistate.openFilterView(importFlow.targetLayer.layerDef) + state.guistate.openFilterView(filteredLayer.layerDef) }} > @@ -70,19 +86,19 @@ - {:else if $hasFilter} + {:else if filteredLayer !== undefined}
@@ -93,7 +109,7 @@ class="primary flex w-full gap-x-1" on:click={() => { abort() - importFlow.targetLayer.disableAllFilters() + filteredLayer.disableAllFilters() }} > @@ -103,7 +119,7 @@ class="flex w-full gap-x-1" on:click={() => { abort() - state.guistate.openFilterView(importFlow.targetLayer.layerDef) + state.guistate.openFilterView(filteredLayer.layerDef) }} > diff --git a/src/UI/Popup/ImportButtons/ImportFlow.ts b/src/UI/Popup/ImportButtons/ImportFlow.ts index 50943d8af..22a54eed7 100644 --- a/src/UI/Popup/ImportButtons/ImportFlow.ts +++ b/src/UI/Popup/ImportButtons/ImportFlow.ts @@ -6,7 +6,6 @@ import TagApplyButton from "../TagApplyButton" import { PointImportFlowArguments } from "./PointImportFlowState" import { Translation } from "../../i18n/Translation" import Translations from "../../i18n/Translations" -import { OsmConnection } from "../../../Logic/Osm/OsmConnection" import FilteredLayer from "../../../Models/FilteredLayer" import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" import { LayerConfigJson } from "../../../Models/ThemeConfig/Json/LayerConfigJson" @@ -25,7 +24,7 @@ export class ImportFlowUtils { public static readonly conflationLayer = new LayerConfig( conflation_json, "all_known_layers", - true + true, ) public static readonly documentationGeneral = `\n\n\nNote that the contributor must zoom to at least zoomlevel 18 to be able to use this functionality. @@ -67,7 +66,7 @@ ${Utils.special_visualizations_importRequirementDocs} */ public static getTagsToApply( originalFeatureTags: UIEventSource, - args: { tags: string } + args: { tags: string }, ): Store { if (originalFeatureTags === undefined) { return undefined @@ -83,9 +82,9 @@ ${Utils.special_visualizations_importRequirementDocs} const items: string = originalFeatureTags.data[tags] console.debug( "The import button is using tags from properties[" + - tags + - "] of this object, namely ", - items + tags + + "] of this object, namely ", + items, ) if (items.startsWith("{")) { @@ -108,13 +107,12 @@ ${Utils.special_visualizations_importRequirementDocs} * - targetLayer * * Others (e.g.: snapOnto-layers) are not to be handled here - * @param argsRaw */ - public static getLayerDependencies(argsRaw: string[], argSpec?) { + public static getLayerDependencies(argsRaw: string[], argSpec?): string[] { const args: ImportFlowArguments = ( Utils.ParseVisArgs(argSpec ?? ImportFlowUtils.generalArguments, argsRaw) ) - return [args.targetLayer] + return args.targetLayer.split(" ") } public static getLayerDependenciesWithSnapOnto( @@ -122,7 +120,7 @@ ${Utils.special_visualizations_importRequirementDocs} name: string defaultValue?: string }[], - argsRaw: string[] + argsRaw: string[], ): string[] { const deps = ImportFlowUtils.getLayerDependencies(argsRaw, argSpec) const argsParsed: PointImportFlowArguments = Utils.ParseVisArgs(argSpec, argsRaw) @@ -130,30 +128,6 @@ ${Utils.special_visualizations_importRequirementDocs} deps.push(...snapOntoLayers) return deps } - - public static buildTagSpec( - args: ImportFlowArguments, - tagSource: Store> - ): Store { - let tagSpec = args.tags - return tagSource.mapD((tags) => { - if ( - tagSpec.indexOf(" ") < 0 && - tagSpec.indexOf(";") < 0 && - tags[args.tags] !== undefined - ) { - // This is probably a key - tagSpec = tags[args.tags] - console.debug( - "The import button is using tags from properties[" + - args.tags + - "] of this object, namely ", - tagSpec - ) - } - return tagSpec - }) - } } /** @@ -164,7 +138,7 @@ ${Utils.special_visualizations_importRequirementDocs} export default abstract class ImportFlow { public readonly state: SpecialVisualizationState public readonly args: ArgT - public readonly targetLayer: FilteredLayer + public readonly targetLayer: FilteredLayer[] public readonly tagsToApply: Store protected readonly _originalFeatureTags: UIEventSource> @@ -172,13 +146,19 @@ export default abstract class ImportFlow { state: SpecialVisualizationState, args: ArgT, tagsToApply: Store, - originalTags: UIEventSource> + originalTags: UIEventSource>, ) { this.state = state this.args = args this.tagsToApply = tagsToApply this._originalFeatureTags = originalTags - this.targetLayer = state.layerState.filteredLayers.get(args.targetLayer) + this.targetLayer = args.targetLayer.split(" ").map(tl => { + let found = state.layerState.filteredLayers.get(tl) + if (!found) { + throw "Layer " + tl + " not found" + } + return found + }) } /** @@ -218,7 +198,7 @@ export default abstract class ImportFlow { return undefined }, - [state.mapProperties.zoom, state.dataIsLoading, this._originalFeatureTags] + [state.mapProperties.zoom, state.dataIsLoading, this._originalFeatureTags], ) } } diff --git a/src/UI/Popup/ImportButtons/PointImportButtonViz.ts b/src/UI/Popup/ImportButtons/PointImportButtonViz.ts index 583e5d3c3..67f92dcc8 100644 --- a/src/UI/Popup/ImportButtons/PointImportButtonViz.ts +++ b/src/UI/Popup/ImportButtons/PointImportButtonViz.ts @@ -17,7 +17,7 @@ export class PointImportButtonViz implements SpecialVisualization { public readonly funcName: string public readonly docs: string | BaseUIElement public readonly example?: string - public readonly args: { name: string; defaultValue?: string; doc: string }[] + public readonly args: { name: string; defaultValue?: string; doc: string, split?: boolean }[] public needsUrls = [] constructor() { diff --git a/src/UI/Popup/ImportButtons/PointImportFlow.svelte b/src/UI/Popup/ImportButtons/PointImportFlow.svelte index 906384248..e02ae27b1 100644 --- a/src/UI/Popup/ImportButtons/PointImportFlow.svelte +++ b/src/UI/Popup/ImportButtons/PointImportFlow.svelte @@ -13,7 +13,7 @@ const args = importFlow.args // The following variables are used for the map - const targetLayer: LayerConfig = state.layout.layers.find((l) => l.id === args.targetLayer) + const targetLayers: LayerConfig[] = args.targetLayer.split(" ").map(tl => state.layout.layers.find((l) => l.id === tl)) const snapToLayers: string[] | undefined = args.snap_onto_layers?.split(",")?.map((l) => l.trim()) ?? [] const maxSnapDistance: number = Number(args.max_snap_distance ?? 25) ?? 25 @@ -33,21 +33,20 @@ async function onConfirm(): Promise { const importedId = await importFlow.onConfirm(value.data, snappedTo.data) - state.selectedLayer.setData(targetLayer) state.selectedElement.setData(state.indexedFeatures.featuresById.data.get(importedId)) }
-
+
diff --git a/src/UI/SpecialVisualizations.ts b/src/UI/SpecialVisualizations.ts index d36c0eb7a..5bfc47024 100644 --- a/src/UI/SpecialVisualizations.ts +++ b/src/UI/SpecialVisualizations.ts @@ -3,11 +3,7 @@ import { FixedUiElement } from "./Base/FixedUiElement" import BaseUIElement from "./BaseUIElement" import Title from "./Base/Title" import Table from "./Base/Table" -import { - RenderingSpecification, - SpecialVisualization, - SpecialVisualizationState, -} from "./SpecialVisualization" +import { RenderingSpecification, SpecialVisualization, SpecialVisualizationState } from "./SpecialVisualization" import { HistogramViz } from "./Popup/HistogramViz" import { MinimapViz } from "./Popup/MinimapViz" import { ShareLinkViz } from "./Popup/ShareLinkViz" @@ -110,7 +106,7 @@ class NearbyImageVis implements SpecialVisualization { tags: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { const isOpen = args[0] === "open" const [lon, lat] = GeoOperations.centerpointCoordinates(feature) @@ -175,7 +171,7 @@ class StealViz implements SpecialVisualization { selectedElement: otherFeature, state, layer, - }) + }), ) } if (elements.length === 1) { @@ -183,8 +179,8 @@ class StealViz implements SpecialVisualization { } return new Combine(elements).SetClass("flex flex-col") }, - [state.indexedFeatures.featuresById] - ) + [state.indexedFeatures.featuresById], + ), ) } @@ -223,7 +219,7 @@ export class QuestionViz implements SpecialVisualization { tags: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { const labels = args[0] ?.split(";") @@ -277,17 +273,17 @@ export default class SpecialVisualizations { * templ.args[0] = "{email}" */ public static constructSpecification( - template: string, - extraMappings: SpecialVisualization[] = [] + template: string | { special: Record> & { type: string } }, + extraMappings: SpecialVisualization[] = [], ): RenderingSpecification[] { if (template === "") { return [] } - if (template["type"] !== undefined) { + if (typeof template !== "string") { console.trace( "Got a non-expanded template while constructing the specification, it still has a 'special-key':", - template + template, ) throw "Got a non-expanded template while constructing the specification" } @@ -295,20 +291,20 @@ export default class SpecialVisualizations { for (const knownSpecial of allKnownSpecials) { // Note: the '.*?' in the regex reads as 'any character, but in a non-greedy way' const matched = template.match( - new RegExp(`(.*){${knownSpecial.funcName}\\((.*?)\\)(:.*)?}(.*)`, "s") + new RegExp(`(.*){${knownSpecial.funcName}\\((.*?)\\)(:.*)?}(.*)`, "s"), ) if (matched != null) { // We found a special component that should be brought to live const partBefore = SpecialVisualizations.constructSpecification( matched[1], - extraMappings + extraMappings, ) const argument = matched[2] /* .trim() // We don't trim, as spaces might be relevant, e.g. "what is ... of {title()}"*/ const style = matched[3]?.substring(1) ?? "" const partAfter = SpecialVisualizations.constructSpecification( matched[4], - extraMappings + extraMappings, ) const args = knownSpecial.args.map((arg) => arg.defaultValue ?? "") if (argument.length > 0) { @@ -347,31 +343,31 @@ export default class SpecialVisualizations { viz.docs, viz.args.length > 0 ? new Table( - ["name", "default", "description"], - viz.args.map((arg) => { - let defaultArg = arg.defaultValue ?? "_undefined_" - if (defaultArg == "") { - defaultArg = "_empty string_" - } - return [arg.name, defaultArg, arg.doc] - }) - ) + ["name", "default", "description"], + viz.args.map((arg) => { + let defaultArg = arg.defaultValue ?? "_undefined_" + if (defaultArg == "") { + defaultArg = "_empty string_" + } + return [arg.name, defaultArg, arg.doc] + }), + ) : undefined, new Title("Example usage of " + viz.funcName, 4), new FixedUiElement( viz.example ?? - "`{" + - viz.funcName + - "(" + - viz.args.map((arg) => arg.defaultValue).join(",") + - ")}`" + "`{" + + viz.funcName + + "(" + + viz.args.map((arg) => arg.defaultValue).join(",") + + ")}`", ).SetClass("literal-code"), ]) } public static HelpMessage() { const helpTexts = SpecialVisualizations.specialVisualizations.map((viz) => - SpecialVisualizations.DocumentationFor(viz) + SpecialVisualizations.DocumentationFor(viz), ) return new Combine([ @@ -405,10 +401,10 @@ export default class SpecialVisualizations { }, }, null, - " " - ) + " ", + ), ).SetClass("code"), - 'In other words: use `{ "before": ..., "after": ..., "special": {"type": ..., "argname": ...argvalue...}`. The args are in the `special` block; an argvalue can be a string, a translation or another value. (Refer to class `RewriteSpecial` in case of problems)', + "In other words: use `{ \"before\": ..., \"after\": ..., \"special\": {\"type\": ..., \"argname\": ...argvalue...}`. The args are in the `special` block; an argvalue can be a string, a translation or another value. (Refer to class `RewriteSpecial` in case of problems)", ]).SetClass("flex flex-col"), ...helpTexts, ]).SetClass("flex flex-col") @@ -417,20 +413,20 @@ export default class SpecialVisualizations { // noinspection JSUnusedGlobalSymbols public static renderExampleOfSpecial( state: SpecialVisualizationState, - s: SpecialVisualization + s: SpecialVisualization, ): BaseUIElement { const examples = s.structuredExamples === undefined ? [] : s.structuredExamples().map((e) => { - return s.constr( - state, - new UIEventSource>(e.feature.properties), - e.args, - e.feature, - undefined - ) - }) + return s.constr( + state, + new UIEventSource>(e.feature.properties), + e.args, + e.feature, + undefined, + ) + }) return new Combine([new Title(s.funcName), s.docs, ...examples]) } @@ -470,7 +466,7 @@ export default class SpecialVisualizations { assignTo: state.userRelatedState.language, availableLanguages: state.layout.language, preferredLanguages: state.osmConnection.userDetails.map( - (ud) => ud.languages + (ud) => ud.languages, ), }) }, @@ -495,7 +491,7 @@ export default class SpecialVisualizations { constr( state: SpecialVisualizationState, - tagSource: UIEventSource> + tagSource: UIEventSource>, ): BaseUIElement { return new VariableUiElement( tagSource @@ -505,7 +501,7 @@ export default class SpecialVisualizations { return new SplitRoadWizard(id, state) } return undefined - }) + }), ) }, }, @@ -519,7 +515,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { if (feature.geometry.type !== "Point") { return undefined @@ -542,7 +538,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { if (!layer.deletion) { return undefined @@ -570,7 +566,7 @@ export default class SpecialVisualizations { state: SpecialVisualizationState, tagSource: UIEventSource>, argument: string[], - feature: Feature + feature: Feature, ): BaseUIElement { const [lon, lat] = GeoOperations.centerpointCoordinates(feature) return new SvelteUIElement(CreateNewNote, { @@ -634,7 +630,7 @@ export default class SpecialVisualizations { .map((tags) => tags[args[0]]) .map((wikidata) => { wikidata = Utils.NoEmpty( - wikidata?.split(";")?.map((wd) => wd.trim()) ?? [] + wikidata?.split(";")?.map((wd) => wd.trim()) ?? [], )[0] const entry = Wikidata.LoadWikidataEntry(wikidata) return new VariableUiElement( @@ -644,9 +640,9 @@ export default class SpecialVisualizations { } const response = e["success"] return Translation.fromMap(response.labels) - }) + }), ) - }) + }), ), }, new MapillaryLinkVis(), @@ -678,7 +674,7 @@ export default class SpecialVisualizations { AllImageProviders.LoadImagesFor(tags, imagePrefixes), tags, state, - feature + feature, ) }, }, @@ -734,7 +730,7 @@ export default class SpecialVisualizations { { nameKey: nameKey, fallbackName, - } + }, ) return new SvelteUIElement(StarsBarIcon, { score: reviews.average, @@ -767,7 +763,7 @@ export default class SpecialVisualizations { { nameKey: nameKey, fallbackName, - } + }, ) return new SvelteUIElement(ReviewForm, { reviews, state, tags, feature, layer }) }, @@ -799,7 +795,7 @@ export default class SpecialVisualizations { { nameKey: nameKey, fallbackName, - } + }, ) return new SvelteUIElement(AllReviews, { reviews, state, tags, feature, layer }) }, @@ -857,7 +853,7 @@ export default class SpecialVisualizations { tags: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): SvelteUIElement { const keyToUse = args[0] const prefix = args[1] @@ -894,10 +890,10 @@ export default class SpecialVisualizations { return undefined } const allUnits: Unit[] = [].concat( - ...(state?.layout?.layers?.map((lyr) => lyr.units) ?? []) + ...(state?.layout?.layers?.map((lyr) => lyr.units) ?? []), ) const unit = allUnits.filter((unit) => - unit.isApplicableToKey(key) + unit.isApplicableToKey(key), )[0] if (unit === undefined) { return value @@ -905,7 +901,7 @@ export default class SpecialVisualizations { const getCountry = () => tagSource.data._country const [v, denom] = unit.findDenomination(value, getCountry) return unit.asHumanLongValue(v, getCountry) - }) + }), ) }, }, @@ -922,7 +918,7 @@ export default class SpecialVisualizations { new Combine([ t.downloadFeatureAsGeojson.SetClass("font-bold text-lg"), t.downloadGeoJsonHelper.SetClass("subtle"), - ]).SetClass("flex flex-col") + ]).SetClass("flex flex-col"), ) .onClick(() => { console.log("Exporting as Geojson") @@ -935,7 +931,7 @@ export default class SpecialVisualizations { title + "_mapcomplete_export.geojson", { mimetype: "application/vnd.geo+json", - } + }, ) }) .SetClass("w-full") @@ -971,7 +967,7 @@ export default class SpecialVisualizations { constr: (state) => { return new SubtleButton( Svg.delete_icon_svg().SetStyle("height: 1.5rem"), - Translations.t.general.removeLocationHistory + Translations.t.general.removeLocationHistory, ).onClick(() => { state.historicalUserLocations.features.setData([]) state.selectedElement.setData(undefined) @@ -1009,10 +1005,10 @@ export default class SpecialVisualizations { .filter((c) => c.text !== "") .map( (c, i) => - new NoteCommentElement(c, state, i, comments.length) - ) + new NoteCommentElement(c, state, i, comments.length), + ), ).SetClass("flex flex-col") - }) + }), ), }, { @@ -1053,9 +1049,9 @@ export default class SpecialVisualizations { return undefined } return new SubstitutedTranslation(title, tagsSource, state).SetClass( - "px-1" + "px-1", ) - }) + }), ), }, { @@ -1071,8 +1067,8 @@ export default class SpecialVisualizations { let challenge = Stores.FromPromise( Utils.downloadJsonCached( `${Maproulette.defaultEndpoint}/challenge/${parentId}`, - 24 * 60 * 60 * 1000 - ) + 24 * 60 * 60 * 1000, + ), ) return new VariableUiElement( @@ -1097,7 +1093,7 @@ export default class SpecialVisualizations { } else { return [title, new List(listItems)] } - }) + }), ) }, docs: "Fetches the metadata of MapRoulette campaign that this task is part of and shows those details (namely `title`, `description` and `instruction`).\n\nThis reads the property `mr_challengeId` to detect the parent campaign.", @@ -1111,15 +1107,15 @@ export default class SpecialVisualizations { "\n" + "```json\n" + "{\n" + - ' "id": "mark_duplicate",\n' + - ' "render": {\n' + - ' "special": {\n' + - ' "type": "maproulette_set_status",\n' + - ' "message": {\n' + - ' "en": "Mark as not found or false positive"\n' + + " \"id\": \"mark_duplicate\",\n" + + " \"render\": {\n" + + " \"special\": {\n" + + " \"type\": \"maproulette_set_status\",\n" + + " \"message\": {\n" + + " \"en\": \"Mark as not found or false positive\"\n" + " },\n" + - ' "status": "2",\n' + - ' "image": "close"\n' + + " \"status\": \"2\",\n" + + " \"image\": \"close\"\n" + " }\n" + " }\n" + "}\n" + @@ -1185,8 +1181,8 @@ export default class SpecialVisualizations { const fsBboxed = new BBoxFeatureSourceForLayer(fs, bbox) return new StatisticsPanel(fsBboxed) }, - [state.mapProperties.bounds] - ) + [state.mapProperties.bounds], + ), ) }, }, @@ -1252,7 +1248,7 @@ export default class SpecialVisualizations { constr( state: SpecialVisualizationState, tagSource: UIEventSource>, - args: string[] + args: string[], ): BaseUIElement { let [text, href, classnames, download, ariaLabel] = args if (download === "") { @@ -1269,15 +1265,14 @@ export default class SpecialVisualizations { download: Utils.SubstituteKeys(download, tags), ariaLabel: Utils.SubstituteKeys(ariaLabel, tags), newTab, - }) - ) + }), + ), ) }, }, { funcName: "multi", docs: "Given an embedded tagRendering (read only) and a key, will read the keyname as a JSON-list. Every element of this list will be considered as tags and rendered with the tagRendering", - example: "```json\n" + JSON.stringify( @@ -1293,7 +1288,7 @@ export default class SpecialVisualizations { }, }, null, - " " + " ", ) + "\n```", args: [ @@ -1313,18 +1308,28 @@ export default class SpecialVisualizations { const translation = new Translation({ "*": tr }) return new VariableUiElement( featureTags.map((tags) => { - const properties: object[] = JSON.parse(tags[key]) - const elements = [] - for (const property of properties) { - const subsTr = new SubstitutedTranslation( - translation, - new UIEventSource(property), - state - ) - elements.push(subsTr) + try { + const data = tags[key] + const properties: object[] = typeof data === "string" ? JSON.parse(tags[key]) : data + const elements = [] + for (const property of properties) { + const subsTr = new SubstitutedTranslation( + translation, + new UIEventSource(property), + state, + ) + elements.push(subsTr) + } + return new List(elements) + } catch (e) { + console.log("Something went wrong while generating the elements for a multi", { + e, + tags, + key, + loaded: tags[key], + }) } - return new List(elements) - }) + }), ) }, }, @@ -1344,7 +1349,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { return new VariableUiElement( tagSource.map((tags) => { @@ -1356,7 +1361,7 @@ export default class SpecialVisualizations { console.error("Cannot create a translation for", v, "due to", e) return JSON.stringify(v) } - }) + }), ) }, }, @@ -1376,7 +1381,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { const key = argument[0] const validator = new FediverseValidator() @@ -1386,14 +1391,14 @@ export default class SpecialVisualizations { .map((fediAccount) => { fediAccount = validator.reformat(fediAccount) const [_, username, host] = fediAccount.match( - FediverseValidator.usernameAtServer + FediverseValidator.usernameAtServer, ) return new SvelteUIElement(Link, { text: fediAccount, url: "https://" + host + "/@" + username, newTab: true, }) - }) + }), ) }, }, @@ -1413,7 +1418,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { return new FixedUiElement("{" + args[0] + "}") }, @@ -1434,7 +1439,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { const key = argument[0] ?? "value" return new VariableUiElement( @@ -1452,12 +1457,12 @@ export default class SpecialVisualizations { } catch (e) { return new FixedUiElement( "Could not parse this tag: " + - JSON.stringify(value) + - " due to " + - e + JSON.stringify(value) + + " due to " + + e, ).SetClass("alert") } - }) + }), ) }, }, @@ -1478,7 +1483,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { const giggityUrl = argument[0] return new SvelteUIElement(Giggity, { tags: tagSource, state, giggityUrl }) @@ -1494,12 +1499,12 @@ export default class SpecialVisualizations { _: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { const tags = (( state )).geolocation.currentUserLocation.features.map( - (features) => features[0]?.properties + (features) => features[0]?.properties, ) return new Combine([ new SvelteUIElement(OrientationDebugPanel, {}), @@ -1521,7 +1526,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { return new SvelteUIElement(MarkAsFavourite, { tags: tagSource, @@ -1541,7 +1546,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { return new SvelteUIElement(MarkAsFavouriteMini, { tags: tagSource, @@ -1561,7 +1566,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { return new SvelteUIElement(DirectionIndicator, { state, feature }) }, @@ -1576,7 +1581,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { return new VariableUiElement( tagSource @@ -1598,9 +1603,9 @@ export default class SpecialVisualizations { `${window.location.protocol}//${window.location.host}${window.location.pathname}?${layout}lat=${lat}&lon=${lon}&z=15` + `#${id}` return new Img(new Qr(url).toImageElement(75)).SetStyle( - "width: 75px" + "width: 75px", ) - }) + }), ) }, }, @@ -1620,7 +1625,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { const key = args[0] === "" ? "_direction:centerpoint" : args[0] return new VariableUiElement( @@ -1631,41 +1636,55 @@ export default class SpecialVisualizations { }) .mapD((value) => { const dir = GeoOperations.bearingToHuman( - GeoOperations.parseBearing(value) + GeoOperations.parseBearing(value), ) console.log("Human dir", dir) return Translations.t.general.visualFeedback.directionsAbsolute[dir] - }) + }), ) }, }, { funcName: "compare_data", needsUrls: (args) => args[1].split(";"), - args:[ + args: [ { name: "url", required: true, - doc: "The attribute containing the url where to fetch more data" + doc: "The attribute containing the url where to fetch more data", }, { - name:"host", - required:true, - doc: "The domain name(s) where data might be fetched from - this is needed to set the CSP. A domain must include 'https', e.g. 'https://example.com'. For multiple domains, separate them with ';'. If you don't know the possible domains, use '*'. " + name: "host", + required: true, + doc: "The domain name(s) where data might be fetched from - this is needed to set the CSP. A domain must include 'https', e.g. 'https://example.com'. For multiple domains, separate them with ';'. If you don't know the possible domains, use '*'. ", }, { name: "postprocessing", required: false, - doc: "Apply some postprocessing. Currently, only 'velopark' is allowed as value" + doc: "Apply some postprocessing. Currently, only 'velopark' is allowed as value", + }, + { + name:"readonly", + required: false, + doc: "If 'yes', will not show 'apply'-buttons" } ], docs: "Gives an interactive element which shows a tag comparison between the OSM-object and the upstream object. This allows to copy some or all tags into OSM", constr(state: SpecialVisualizationState, tagSource: UIEventSource>, args: string[], feature: Feature, layer: LayerConfig): BaseUIElement { const url = args[0] const postprocessVelopark = args[2] === "velopark" - return new SvelteUIElement(ComparisonTool, {url, postprocessVelopark, state, tags: tagSource, layer, feature}) - } - } + const readonly = args[3] === "yes" + return new SvelteUIElement(ComparisonTool, { + url, + postprocessVelopark, + state, + tags: tagSource, + layer, + feature, + readonly + }) + }, + }, ] specialVisualizations.push(new AutoApplyButton(specialVisualizations)) @@ -1677,7 +1696,7 @@ export default class SpecialVisualizations { throw ( "Invalid special visualisation found: funcName is undefined for " + invalid.map((sp) => sp.i).join(", ") + - '. Did you perhaps type \n funcName: "funcname" // type declaration uses COLON\ninstead of:\n funcName = "funcName" // value definition uses EQUAL' + ". Did you perhaps type \n funcName: \"funcname\" // type declaration uses COLON\ninstead of:\n funcName = \"funcName\" // value definition uses EQUAL" ) } From b26ffaaf22ea9436b81b9d3dec1e54ed393085d8 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 04:21:11 +0100 Subject: [PATCH 025/195] SpecialVis: add readonly mode to ComparisonTable --- src/UI/Comparison/ComparisonAction.svelte | 30 +++++++------ src/UI/Comparison/ComparisonTable.svelte | 55 +++++++++++++++++------ src/UI/Comparison/ComparisonTool.svelte | 3 +- 3 files changed, 60 insertions(+), 28 deletions(-) diff --git a/src/UI/Comparison/ComparisonAction.svelte b/src/UI/Comparison/ComparisonAction.svelte index b16f216d4..3b6c747e0 100644 --- a/src/UI/Comparison/ComparisonAction.svelte +++ b/src/UI/Comparison/ComparisonAction.svelte @@ -16,6 +16,8 @@ export let feature: Feature export let layer: LayerConfig + export let readonly = false + let currentStep: "init" | "applying" | "done" = "init" /** @@ -49,17 +51,19 @@ {externalProperties[key]} {/if} - - {#if currentStep === "init"} - - {:else if currentStep === "applying"} - - {:else if currentStep === "done"} -
Done
- {:else } -
Error
- {/if} - + {#if !readonly} + + {#if currentStep === "init"} + + {:else if currentStep === "applying"} + + {:else if currentStep === "done"} +
Done
+ {:else } +
Error
+ {/if} + + {/if} diff --git a/src/UI/Comparison/ComparisonTable.svelte b/src/UI/Comparison/ComparisonTable.svelte index a749e8f83..36bfdc21d 100644 --- a/src/UI/Comparison/ComparisonTable.svelte +++ b/src/UI/Comparison/ComparisonTable.svelte @@ -12,6 +12,7 @@ import { Tag } from "../../Logic/Tags/Tag" import { And } from "../../Logic/Tags/And" import Loading from "../Base/Loading.svelte" + import AttributedImage from "../Image/AttributedImage.svelte" export let osmProperties: Record export let externalProperties: Record @@ -21,6 +22,8 @@ export let feature: Feature export let layer: LayerConfig + export let readonly = false + let externalKeys: string[] = (Object.keys(externalProperties)) .sort() @@ -59,12 +62,23 @@ {#if different.length > 0}

Conflicting items

- {JSON.stringify(different)} + + + + + + + {#each different as key} + + + + + + {/each} +
KeyOSMExternal
{key}{osmProperties[key]}{externalProperties[key]}
{/if} {#if missing.length > 0} -

Missing items

- {#if currentStep === "init"} @@ -73,11 +87,13 @@ {#each missing as key} - + {/each}
- + {#if !readonly} + + {/if} {:else if currentStep === "applying_all"} Applying all missing values {:else if currentStep === "all_applied"} @@ -96,12 +112,22 @@ {/if} {#if unknownImages.length > 0} -

Missing pictures

- {#each unknownImages as image} - + +
+ {#each unknownImages as image} + + {/each} +
+
+ {:else} + {#each unknownImages as image} + + - {/each} + {feature} + {layer} /> + {/each} + {/if} {/if} diff --git a/src/UI/Comparison/ComparisonTool.svelte b/src/UI/Comparison/ComparisonTool.svelte index 3acf386f0..9fa56915a 100644 --- a/src/UI/Comparison/ComparisonTool.svelte +++ b/src/UI/Comparison/ComparisonTool.svelte @@ -19,6 +19,7 @@ export let state: SpecialVisualizationState export let tags: UIEventSource export let layer: LayerConfig export let feature: Feature +export let readonly = false let data: any = undefined let error: any = undefined @@ -58,5 +59,5 @@ onMount(async () => { Loading {$tags[url]} {:else if data.properties !== undefined} - + {/if} From 76f6b1f21ff075dbd86706b6a94621b3e993dfe4 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 04:21:46 +0100 Subject: [PATCH 026/195] UX: some style tweaks to images --- src/UI/Image/AttributedImage.svelte | 2 +- src/UI/Image/ImagePreview.svelte | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/UI/Image/AttributedImage.svelte b/src/UI/Image/AttributedImage.svelte index 519e7cdb8..083f6dfd6 100644 --- a/src/UI/Image/AttributedImage.svelte +++ b/src/UI/Image/AttributedImage.svelte @@ -18,7 +18,7 @@ export let previewedImage: UIEventSource = undefined -
+
{ isLoaded?.setData(true) }} From 4c5ebf05f5d818abd622bf9d8c8a96926e36ddd6 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 04:23:06 +0100 Subject: [PATCH 027/195] Themes: add velopark import functionality --- assets/themes/velopark/velopark.json | 157 ++++++++++++++++++++++++++- 1 file changed, 152 insertions(+), 5 deletions(-) diff --git a/assets/themes/velopark/velopark.json b/assets/themes/velopark/velopark.json index 3da36a318..7050dd042 100644 --- a/assets/themes/velopark/velopark.json +++ b/assets/themes/velopark/velopark.json @@ -22,7 +22,139 @@ "startLat": 51.03753, "startLon": 3.71025, "startZoom": 18, + "defaultBackgroundId": "photo", "layers": [ + { + "builtin": "maproulette_challenge", + "override": { + "=name": { + "en": "Velopark data", + "nl": "Velopark data" + }, + "=filter": [ + { + "id": "created-only", + "options": [ + { + "question": { + "en": "Only unfinished tasks", + "nl": "Enkel onafgewerkte taken" + }, + "osmTags": "mr_taskStatus=Created", + "default": true + } + ] + } + ], + "calculatedTags+": [ + "mr_velopark_id=feat.properties['ref:velopark']?.split('/')?.at(-1)", + "_nearby_bicycle_parkings=closestn(feat)(['bike_parking','bike_parking_with_velopark_ref'], 100, undefined, 25)", + "_nearby_bicycle_parkings:count=get(feat)('_nearby_bicycle_parkings').length", + "_nearby_bicycle_parkings:props=get(feat)('_nearby_bicycle_parkings').map(f => ({_distance: Math.round(f.distance), _ref: feat.properties['ref:velopark'], _mr_id: feat.properties.id, '_velopark:id': (f.feat.properties['_velopark:id'] ?? 'unlinked') /*Explicit copy to trigger lazy loading*/, ...f.feat.properties}))" + ], + "=title": { + "render": "Velopark parking {mr_velopark_id}" + }, + "source": { + "geoJson": "https://maproulette.org/api/v2/challenge/view/43282" + }, + "=tagRenderings": [ + { + "id": "velopark-link", + "render": { + "special": { + "type": "link", + "href": "https://www.velopark.be/static/data/{mr_velopark_id}", + "text": { + "en": "See on velopark (webpage)", + "nl": "Bekijk op Velopark (webpagina)" + } + } + } + }, + { + "id": "velopark-data-link", + "render": { + "special": { + "type": "link", + "href": "{ref:velopark}", + "text": "Inspect raw data on velopark.be" + } + } + }, + { + "id": "show-data-velopark", + "render": { + "special": { + "type": "compare_data", + "url": "ref:velopark", + "host": "https://data.velopark.be", + "postprocessing": "velopark", + "readonly": "yes" + } + } + }, + { + "id": "closest_parkings", + "render": { + "*": "There are {_nearby_bicycle_parkings:count} bicycle parkings within 25m known in OpenStreetMap. " + } + }, + { + "id": "list_nearby_bike_parkings", + "render": { + "special": { + "type": "multi", + "key": "_nearby_bicycle_parkings:props", + "tagrendering": "{id} ({_distance}m, {_velopark:id}) {minimap(20)} {tag_apply(ref:velopark=$_ref,Link,link,id,_mr_id)}" + } + } + }, + { + "id": "import_point", + "render": { + "special": { + "type": "import_button", + "targetLayer": "bike_parking_with_velopark_ref bike_parking", + "tags": "amenity=bicycle_parking;ref:velopark=$ref:velopark", + "text": { + "en": "Create a new bicycle parking in OSM", + "nl": "Maak een nieuwe parking aan in OSM" + }, + "maproulette_id": "mr_taskId" + } + } + }, + { + "id": "close_mr", + "render": { + "special": { + "type": "maproulette_set_status", + "message": { + "en": "Mark this item as linked", + "nl": "Markeer als gelinkt" + } + } + } + }, + { + "id": "close_mr_incorrect", + "render": { + "special": { + "type": "maproulette_set_status", + "message": { + "en": "Mark this item as incorrect (duplicate, does not exist anymore, contradictory data)", + "nl": "Markeer dit object als incorrect (duplicaatin, incorrect of tegenstrijdige data, ...)" + }, + "image": "bug", + "status": 6 + } + } + }, + "{nearby_images()}" + ] + } + }, { "builtin": [ "bike_parking" @@ -64,13 +196,19 @@ } }, { - "builtin": ["bike_parking"], + "builtin": [ + "bike_parking" + ], "override": { "minzoom": 14 } }, { - "builtin": ["toilet","bike_repair_station","bicycle_rental"], + "builtin": [ + "toilet", + "bike_repair_station", + "bicycle_rental" + ], "override": { "minzoom": 18 } @@ -80,7 +218,11 @@ "+tagRenderings": [ { "id": "velopark-ref", - "condition": "amenity=bicycle_parking", + "condition": { + "and": [ + "amenity=bicycle_parking" + ] + }, "freeform": { "key": "ref:velopark", "inline": false, @@ -104,12 +246,17 @@ }, { "id": "comparison_tool", - "condition": "ref:velopark~https://data.velopark.be/data/.*" , + "condition": { + "and": [ + "ref:velopark~https://data.velopark.be/data/.*", + "id!~.*maproulette.org/.*" + ] + }, "render": { "special": { "type": "compare_data", "url": "ref:velopark", - "host":"https://data.velopark.be", + "host": "https://data.velopark.be", "postprocessing": "velopark" } } From e755b81699ae079d13698c90187c34f2c21989fe Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 04:57:33 +0100 Subject: [PATCH 028/195] Build: rename generate:velopark as not to trigger when setting up a repo --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 71f0b38be..a6f6b98d7 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "generate:cache:speelplekken": "npm run generate:layeroverview && vite-node scripts/generateCache.ts -- speelplekken 14 ../MapComplete-data/speelplekken_cache/ 51.20 4.35 51.09 4.56", "generate:cache:natuurpunt": "npm run generate:layeroverview && vite-node scripts/generateCache.ts -- natuurpunt 12 ../MapComplete-data/natuurpunt_cache/ 50.40 2.1 51.54 6.4 --generate-point-overview nature_reserve,visitor_information_centre", "generate:layeroverview": "export NODE_OPTIONS=\"--max-old-space-size=8192\" && vite-node scripts/generateLayerOverview.ts", - "generate:layeroverview:velopark": "export NODE_OPTIONS=\"--max-old-space-size=8192\" && vite-node scripts/generateLayerOverview.ts -- --force --themes=velopark --layers=bike_parking,maproulette_challenge", + "velopark": "export NODE_OPTIONS=\"--max-old-space-size=8192\" && vite-node scripts/generateLayerOverview.ts -- --force --themes=velopark --layers=bike_parking,maproulette_challenge", "generate:mapcomplete-changes-theme": "export NODE_OPTIONS=\"--max-old-space-size=8192\" && vite-node scripts/generateLayerOverview.ts -- --generate-change-map", "refresh:layeroverview": "export NODE_OPTIONS=\"--max-old-space-size=8192\" && vite-node scripts/generateLayerOverview.ts -- --force", "generate:licenses": "vite-node scripts/generateLicenseInfo.ts -- --no-fail", From 505747c60a92de556504d2f10f21829601152a28 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 13:43:46 +0100 Subject: [PATCH 029/195] assets/ Themes: update ATM.json for new maproulette-format --- assets/themes/atm/atm.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/themes/atm/atm.json b/assets/themes/atm/atm.json index d8148c7f1..75d249d10 100644 --- a/assets/themes/atm/atm.json +++ b/assets/themes/atm/atm.json @@ -132,7 +132,7 @@ "cs": "Přidání všech navrhovaných značek do nejbližšího bankomatu" }, "image": "./assets/svg/addSmall.svg", - "maproulette_task_id": "mr_taskId" + "maproulette_id": "mr_taskId" } } }, @@ -173,4 +173,4 @@ } } ] -} \ No newline at end of file +} From 5f627124e98e98f25e9e5a834f56e906237e0241 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 13:04:46 +0100 Subject: [PATCH 030/195] Themes: fix typo in soft-deletion tag --- assets/layers/defibrillator/defibrillator.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/layers/defibrillator/defibrillator.json b/assets/layers/defibrillator/defibrillator.json index 23fa17507..fc69fcbb8 100644 --- a/assets/layers/defibrillator/defibrillator.json +++ b/assets/layers/defibrillator/defibrillator.json @@ -752,7 +752,7 @@ "deletion": { "softDeletionTags": { "and": [ - "disused:emergency:=defibrillator}", + "disused:emergency:=defibrillator", "emergency=" ] }, From 38b9523b835b72bfe9f068b55015d8f7868951c9 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 17:37:40 +0100 Subject: [PATCH 031/195] Themes: phone validation now knows about some short-codes --- .../InputElement/Validators/PhoneValidator.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/UI/InputElement/Validators/PhoneValidator.ts b/src/UI/InputElement/Validators/PhoneValidator.ts index f8a76ff22..6ef6a5027 100644 --- a/src/UI/InputElement/Validators/PhoneValidator.ts +++ b/src/UI/InputElement/Validators/PhoneValidator.ts @@ -35,6 +35,10 @@ export default class PhoneValidator extends Validator { if (country !== undefined) { countryCode = country()?.toUpperCase() } + if (this.isShortCode(str, countryCode)) { + return true + } + return parsePhoneNumberFromString(str, countryCode)?.isValid() ?? false } @@ -46,9 +50,28 @@ export default class PhoneValidator extends Validator { if (country) { countryCode = country() } + if (this.isShortCode(str, countryCode?.toUpperCase())) { + return str + } return parsePhoneNumberFromString( str, countryCode?.toUpperCase() as any )?.formatInternational() } + + /** + * Indicates if the given string is a special 'short code' valid in the given country + * see https://nl.wikipedia.org/wiki/Short_code + * @param str a possible phone number + * @param country the upper case, two-letter code for a country + * @private + */ + private isShortCode(str: string, country: string) { + if (country == "BE" && str.length === 4 && str.match(/[0-9]{4}/)) { + return true + } + if (country == "NL" && str.length === 4 && str.match(/14[0-9]{3}/)) { + return true + } + } } From c62f7cce01da15ef3f123b95e08056ca5019b7c9 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 21:53:11 +0100 Subject: [PATCH 032/195] Themes: allow to add multiple changing table locations, see #1756 --- assets/layers/toilet/toilet.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/layers/toilet/toilet.json b/assets/layers/toilet/toilet.json index 068ba43aa..d4266476c 100644 --- a/assets/layers/toilet/toilet.json +++ b/assets/layers/toilet/toilet.json @@ -26,7 +26,7 @@ "source": { "osmTags": "amenity=toilets" }, - "minzoom": 12, + "minzoom": 10, "title": { "render": { "en": "Toilet", @@ -683,6 +683,7 @@ } } ], + "multiAnswer": true, "id": "toilet-changing_table:location" }, { From 76a8bfe4d4fa0b82bc839db5f010886626a67c73 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 21:56:16 +0100 Subject: [PATCH 033/195] Chore: translation sync --- assets/layers/artwork/artwork.json | 21 +- assets/layers/atm/atm.json | 63 +- .../charging_station/charging_station.json | 66 +- .../cycleways_and_roads.json | 12 +- .../layers/drinking_water/drinking_water.json | 45 +- assets/layers/hackerspace/hackerspace.json | 38 +- assets/layers/usersettings/usersettings.json | 33 +- assets/themes/cyclenodes/cyclenodes.json | 35 +- assets/themes/fritures/fritures.json | 9 +- assets/themes/hackerspaces/hackerspaces.json | 6 +- assets/themes/icecream/icecream.json | 6 +- .../mapcomplete-changes.json | 115 ++- langs/de.json | 2 +- langs/layers/de.json | 744 +++++++++--------- langs/layers/it.json | 104 +-- langs/layers/nl.json | 532 ++++++------- langs/themes/de.json | 78 +- langs/themes/en.json | 78 +- langs/themes/es.json | 64 +- 19 files changed, 1107 insertions(+), 944 deletions(-) diff --git a/assets/layers/artwork/artwork.json b/assets/layers/artwork/artwork.json index eed10093e..7ed51de84 100644 --- a/assets/layers/artwork/artwork.json +++ b/assets/layers/artwork/artwork.json @@ -560,7 +560,8 @@ "he": "גילוף בעץ", "eu": "Egur taila", "pl": "Rzeźbienie w drewnie", - "pt_BR": "Entalhe" + "pt_BR": "Entalhe", + "it": "Scultura in legno" } } ], @@ -755,7 +756,8 @@ "ca": "Què representa aquesta obra d'art?", "he": "מה מתארת היצירה הזו?", "pl": "Co przedstawia to dzieło sztuki?", - "pt_BR": "O que esta obra de arte representa?" + "pt_BR": "O que esta obra de arte representa?", + "it": "Che cosa rappresenta quest'opera d'arte?" }, "freeform": { "key": "subject:wikidata", @@ -772,7 +774,8 @@ "ca": "Aquesta obra d'art representa {wikidata_label(subject:wikidata)}{wikipedia(subject:wikidata)}", "fr": "Cette œuvre dépeint {wikidata_label(subject:wikidata)}{wikipedia(subject:wikidata)}", "pl": "To dzieło sztuki przedstawia {wikidata_label(subject:wikidata)}{wikipedia(subject:wikidata)}", - "pt_BR": "Essa arte representa {wikidata_label(subject:wikidata)}{wikipedia(subject:wikidata)}" + "pt_BR": "Essa arte representa {wikidata_label(subject:wikidata)}{wikipedia(subject:wikidata)}", + "it": "Quest'opera d'arte rappresenta {wikidata_label(subject:wikidata)}{wikipedia(subject:wikidata)}" }, "labels": [ "artwork-question" @@ -792,7 +795,8 @@ "pl": "Czy to dzieło sztuki pełni funkcję ławki?", "pt_BR": "Essa obra de arte serve como banco?", "es": "¿Sirve esta obra de arte como banco?", - "pt": "Esta obra de arte serve como banco?" + "pt": "Esta obra de arte serve como banco?", + "it": "Quest'opera d'arte funge da panchina?" }, "mappings": [ { @@ -808,7 +812,8 @@ "pl": "To dzieło sztuki pełni również funkcję ławki", "pt_BR": "Essa obra de arte também serve como banco", "es": "Esta obra de arte también sirve de banco", - "pt": "Esta obra de arte também serve como banco" + "pt": "Esta obra de arte também serve como banco", + "it": "Quest'opera d'arte funge anche da panchina" } }, { @@ -825,7 +830,8 @@ "pl": "To dzieło sztuki nie pełni funkcji ławki", "pt_BR": "Essa obra de arte não serve como banco", "es": "Esta obra no sirve de banco", - "pt": "Esta obra de arte não serve como banco" + "pt": "Esta obra de arte não serve como banco", + "it": "Quest'opera d'arte non funge anche da panchina" } }, { @@ -842,7 +848,8 @@ "pl": "To dzieło sztuki nie pełni funkcji ławki", "pt_BR": "Essa obra de arte não serve como banco", "es": "Esta obra no sirve de banco", - "pt": "Esta obra de arte não serve como banco" + "pt": "Esta obra de arte não serve como banco", + "it": "Quest'opera d'arte non funge anche da panchina" }, "hideInAnswer": true } diff --git a/assets/layers/atm/atm.json b/assets/layers/atm/atm.json index aaf06d8fe..9d247dea0 100644 --- a/assets/layers/atm/atm.json +++ b/assets/layers/atm/atm.json @@ -13,7 +13,8 @@ "pl": "Bankomaty", "pt_BR": "Caixas eletrônicos", "es": "Cajeros automáticos", - "pt": "Multibancos" + "pt": "Multibancos", + "it": "Sportelli Bancomat" }, "description": { "en": "ATMs to withdraw money", @@ -27,7 +28,8 @@ "he": "כספומטים למשיכת כסף", "pl": "Bankomaty do wypłacania pieniędzy", "pt_BR": "Caixas eletrônicos para sacar dinheiro", - "pt": "Multibancos para levantar dinheiro" + "pt": "Multibancos para levantar dinheiro", + "it": "Sportello Bancomat per prelevare denaro" }, "source": { "osmTags": "amenity=atm" @@ -103,7 +105,8 @@ "pl": "bankomat", "pt_BR": "um caixa eletrônico", "es": "Un cajero automático", - "pt": "um multibanco" + "pt": "um multibanco", + "it": "Uno sportello bancomat" } } ], @@ -123,7 +126,8 @@ "pl": "Nazwa tego bankomatu to {name}", "pt_BR": "O nome desse caixa eletrônico é {name}", "es": "El nombre del banco de este cajero automático es {name}", - "pt": "O nome deste multibanco é {name}" + "pt": "O nome deste multibanco é {name}", + "it": "Il nome di questo sportello bancomat è {name}" }, "condition": "name~*" }, @@ -206,7 +210,8 @@ "eu": "Operadorea", "pt_BR": "Operador", "es": "Operador", - "pt": "Operador" + "pt": "Operador", + "it": "Operatore" } }, "render": { @@ -237,7 +242,8 @@ "he": "האם אתה יכול למשוך מזומן מהכספומט הזה?", "pt_BR": "Você pode sacar dinheiro nesse caixa eletrônico?", "es": "¿Se puede sacar dinero de este cajero?", - "pt": "Pode levantar dinheiro neste multibanco?" + "pt": "Pode levantar dinheiro neste multibanco?", + "it": "Puoi prelevare dei contanti da questo sportello bancomat?" }, "mappings": [ { @@ -253,7 +259,8 @@ "pl": "Z tego bankomatu można wypłacić pieniądze", "pt_BR": "Você pode sacar dinheiro nesse caixa eletrônico", "es": "Puede retirar dinero de este cajero automático", - "pt": "Pode levantar dinheiro neste multibanco" + "pt": "Pode levantar dinheiro neste multibanco", + "it": "Puoi prelevare dei contanti da questo sportello bancomat" }, "hideInAnswer": true }, @@ -268,7 +275,8 @@ "he": "אתה יכול למשוך מזומן מהכספומט הזה", "pt_BR": "Você pode sacar dinheiro nesse caixa eletrônico", "es": "Puede retirar dinero de este cajero automático", - "pt": "Pode levantar dinheiro neste multibanco" + "pt": "Pode levantar dinheiro neste multibanco", + "it": "Puoi prelevare dei contanti da questo sportello bancomat" } }, { @@ -282,7 +290,8 @@ "he": "לא ניתן למשוך מזומן מכספומט זה", "pt_BR": "Você não pode sacar dinheiro nesse caixa eletrônico", "es": "No puede retirar dinero de este cajero automático", - "pt": "Não pode levantar dinheiro neste multibanco" + "pt": "Não pode levantar dinheiro neste multibanco", + "it": "Non puoi prelevare dei contanti da questo sportello bancomat" } } ] @@ -300,7 +309,8 @@ "pl": "Czy ten bankomat pozwala wpłacać pieniądze?", "pt_BR": "Você pode depositar dinheiro nesse caixa eletrônico?", "es": "¿Se puede ingresar dinero en efectivo en este cajero?", - "pt": "Pode depositar dinheiro neste multibanco?" + "pt": "Pode depositar dinheiro neste multibanco?", + "it": "Puoi depositare dei contanti in questo sportello bancomat?" }, "mappings": [ { @@ -317,7 +327,8 @@ "pl": "Prawdopodobnie ten bankomat nie pozwala wpłacać pieniędzy", "pt_BR": "Você provavelmente não pode depositar dinheiro nesse caixa eletrônico", "es": "Es probable que no pueda ingresar dinero en efectivo en este cajero automático", - "pt": "Provavelmente não pode depositar dinheiro neste multibanco" + "pt": "Provavelmente não pode depositar dinheiro neste multibanco", + "it": "Probabilmente non puoi depositare dei contanti in questo sportello bancomat" }, "hideInAnswer": true }, @@ -335,7 +346,8 @@ "pl": "Ten bankomat pozwala wpłacać pieniądze", "pt_BR": "Você pode depositar dinheiro nesse caixa eletrônico", "es": "Puede ingresar dinero en efectivo en este cajero automático", - "pt": "Pode depositar dinheiro neste mutibanco" + "pt": "Pode depositar dinheiro neste mutibanco", + "it": "Puoi depositare dei contanti in questo sportello bancomat" } }, { @@ -352,7 +364,8 @@ "pl": "Ten bankomat nie pozwala wpłacać pieniędzy", "pt_BR": "Você não pode depositar dinheiro nesse caixa eletrônico", "es": "No se puede ingresar dinero en efectivo en este cajero automático", - "pt": "Não pode depositar dinheiro neste multibanco" + "pt": "Não pode depositar dinheiro neste multibanco", + "it": "Non puoi depositare dei contanti in questo sportello bancomat" } } ] @@ -386,7 +399,8 @@ "pl": "Jakie banknoty można tutaj wypłacić?", "pt_BR": "Quais notas você pode sacar aqui?", "es": "¿Qué billetes puede retirar aquí?", - "pt": "Quais notas pode levantar aqui?" + "pt": "Quais notas pode levantar aqui?", + "it": "Quali banconote si possono ritirare qui?" }, "multiAnswer": true, "mappings": [ @@ -402,7 +416,8 @@ "fr": "On peut retirer des billets de 5 euros", "pt_BR": "Notas de 5 euros podem ser sacadas", "es": "Se pueden retirar billetes de 5 euros", - "pt": "Notas de 5 euros podem ser levantadas" + "pt": "Notas de 5 euros podem ser levantadas", + "it": "Si possono prelevare banconote da 5 euro" } }, { @@ -417,7 +432,8 @@ "fr": "On peut retirer des billets de 10 euros", "pt_BR": "Notas de 10 euros podem ser sacadas", "es": "Se pueden retirar billetes de 10 euros", - "pt": "Notas de 10 euros podem ser levantadas" + "pt": "Notas de 10 euros podem ser levantadas", + "it": "Si possono prelevare banconote da 10 euro" } }, { @@ -432,7 +448,8 @@ "fr": "On peut retirer des billets de 20 euros", "pt_BR": "Notas de 20 euros podem ser sacadas", "es": "Se pueden retirar billetes de 20 euros", - "pt": "Notas de 20 euros podem ser levantadas" + "pt": "Notas de 20 euros podem ser levantadas", + "it": "Si possono prelevare banconote da 20 euro" } }, { @@ -447,7 +464,8 @@ "fr": "On peut retirer des billets de 50 euros", "pt_BR": "Notas de 50 euros podem ser sacadas", "es": "Se pueden retirar billetes de 50 euros", - "pt": "Notas de 50 euros podem ser levantadas" + "pt": "Notas de 50 euros podem ser levantadas", + "it": "Si possono prelevare banconote da 50 euro" } }, { @@ -462,7 +480,8 @@ "fr": "On peut retirer des billets de 100 euros", "pt_BR": "Notas de 100 euros podem ser sacadas", "es": "Se pueden retirar billetes de 100 euros", - "pt": "Notas de 100 euros podem ser levantadas" + "pt": "Notas de 100 euros podem ser levantadas", + "it": "Si possono prelevare banconote da 100 euro" } }, { @@ -477,7 +496,8 @@ "fr": "On peut retirer des billets de 200 euros", "pt_BR": "Notas de 200 euros podem ser sacadas", "es": "Se pueden retirar billetes de 200 euros", - "pt": "Notas de 200 euros podem ser levantadas" + "pt": "Notas de 200 euros podem ser levantadas", + "it": "Non si possono prelevare banconote da 200 euro" } }, { @@ -492,7 +512,8 @@ "fr": "On peut retirer des billets de 500 euros", "pt_BR": "Notas de 500 euros podem ser sacadas", "es": "Se pueden retirar billetes de 500 euros", - "pt": "Notas de 500 euros podem ser levantadas" + "pt": "Notas de 500 euros podem ser levantadas", + "it": "Non si possono prelevare banconote da 500 euro" } } ] diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index 13128a61c..60193ab23 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -199,7 +199,7 @@ "then": { "en": "Not accessible to the general public (e.g. only accessible to the owners, employees, ...)", "nl": "Niet toegankelijk voor het publiek
Bv. enkel toegankelijk voor de eigenaar, medewerkers ,... ", - "de": "Die Station ist nicht für die Allgemeinheit zugänglich (z. B. nur für die Eigentümer, Mitarbeiter, …)", + "de": "Die Station ist nicht für die Allgemeinheit zugänglich (z. B. nur für die Eigentümer, Mitarbeiter, ...)", "ca": "No accessible per al públic general (p.e. només accessible pels propietaris, empleats, ...)" } }, @@ -1241,7 +1241,8 @@ "socket:schuko", { "en": "Schuko wall plug without ground pin (CEE7/4 type F)", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F)" + "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F)", + "de": "Schuko-Stecker ohne Erdungsstift (CEE7/4 Typ F)" }, "CEE7_4F.svg", [ @@ -1259,7 +1260,8 @@ "socket:typee", { "en": "European wall plug with ground pin (CEE7/4 type E)", - "nl": "Europese stekker met aardingspin (CEE7/4 type E)" + "nl": "Europese stekker met aardingspin (CEE7/4 type E)", + "de": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" }, "TypeE.svg", [ @@ -1278,7 +1280,8 @@ "socket:chademo", { "en": "Chademo", - "nl": "Chademo" + "nl": "Chademo", + "de": "Chademo-Stecker" }, "Chademo_type4.svg", [ @@ -1296,7 +1299,8 @@ "socket:type1_cable", { "en": "Type 1 with cable (J1772)", - "nl": "Type 1 met kabel (J1772)" + "nl": "Type 1 met kabel (J1772)", + "de": "Typ 1 mit Kabel (J1772)" }, "Type1_J1772.svg", [ @@ -1316,7 +1320,8 @@ "socket:type1", { "en": "Type 1 without cable (J1772)", - "nl": "Type 1 zonder kabel (J1772)" + "nl": "Type 1 zonder kabel (J1772)", + "de": " Typ 1 ohne Kabel (J1772)" }, "Type1_J1772.svg", [ @@ -1338,7 +1343,8 @@ "socket:type1_combo", { "en": "Type 1 CCS (aka Type 1 Combo)", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo)" + "nl": "Type 1 CCS (ook gekend als Type 1 Combo)", + "de": "Typ 1 CCS (Typ 1 Combo)" }, "Type1-ccs.svg", [ @@ -1361,7 +1367,8 @@ "socket:tesla_supercharger", { "en": "Tesla Supercharger", - "nl": "Tesla Supercharger" + "nl": "Tesla Supercharger", + "de": "Tesla Supercharger" }, "Tesla-hpwc-model-s.svg", [ @@ -1382,7 +1389,8 @@ "socket:type2", { "en": "Type 2 (mennekes)", - "nl": "Type 2 (mennekes)" + "nl": "Type 2 (mennekes)", + "de": "Typ 2 (Mennekes)" }, "Type2_socket.svg", [ @@ -1403,7 +1411,8 @@ "socket:type2_combo", { "en": "Type 2 CCS (mennekes)", - "nl": "Type 2 CCS (mennekes)" + "nl": "Type 2 CCS (mennekes)", + "de": "Typ 2 CCS (Mennekes)" }, "Type2_CCS.svg", [ @@ -1423,7 +1432,8 @@ "socket:type2_cable", { "en": "Type 2 with cable (mennekes)", - "nl": "Type 2 met kabel (J1772)" + "nl": "Type 2 met kabel (J1772)", + "de": "Typ 2 mit Kabel (Mennekes)" }, "Type2_tethered.svg", [ @@ -1444,7 +1454,8 @@ "socket:tesla_supercharger_ccs", { "en": "Tesla Supercharger CCS (a branded type2_css)", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo)", + "de": "Tesla Supercharger CCS (Typ 2 CSS von Tesla)" }, "Type2_CCS.svg", [ @@ -1464,7 +1475,8 @@ "socket:tesla_destination", { "en": "Tesla Supercharger (destination)", - "nl": "Tesla Supercharger (destination)" + "nl": "Tesla Supercharger (destination)", + "de": "Tesla Supercharger (Destination)" }, "Tesla-hpwc-model-s.svg", [ @@ -1485,7 +1497,8 @@ "socket:tesla_destination", { "en": "Tesla supercharger (destination) (A Type 2 with cable branded as tesla)", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)" + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)", + "de": "Tesla Supercharger (Destination) (Typ 2 mit Kabel von Tesla)" }, "Type2_tethered.svg", [ @@ -1506,7 +1519,8 @@ "socket:USB-A", { "en": "USB to charge phones and small electronics", - "nl": "USB om GSMs en kleine electronica op te laden" + "nl": "USB om GSMs en kleine electronica op te laden", + "de": "USB zum Aufladen von Handys und kleinen Elektrogeräten" }, "usb_port.svg", [ @@ -1526,7 +1540,8 @@ "socket:bosch_3pin", { "en": "Bosch Active Connect with 3 pins and cable", - "nl": "Bosch Active Connect met 3 pinnen aan een kabel" + "nl": "Bosch Active Connect met 3 pinnen aan een kabel", + "de": " Bosch Active Connect mit 3 Pins und Kabel" }, "bosch-3pin.svg", [], @@ -1538,7 +1553,8 @@ "socket:bosch_5pin", { "en": "Bosch Active Connect with 5 pins and cable", - "nl": "Bosch Active Connect met 5 pinnen aan een kabel" + "nl": "Bosch Active Connect met 5 pinnen aan een kabel", + "de": " Bosch Active Connect mit 5 Pins und Kabel" }, "bosch-5pin.svg", [], @@ -1630,7 +1646,8 @@ }, "render": { "en": "{{description}} outputs at most {canonical({{key}}:current)}", - "nl": "{{description}} levert een stroom van maximaal {canonical({{key}}:current)}" + "nl": "{{description}} levert een stroom van maximaal {canonical({{key}}:current)}", + "de": "{{description}} liefert maximal {canonical({{key}}:current)}" }, "freeform": { "key": "{{key}}:current", @@ -1658,11 +1675,13 @@ ], "question": { "en": "What power output does a single plug of type {{description}} offer?", - "nl": "Welk vermogen levert een enkele stekker van type {{description}}?" + "nl": "Welk vermogen levert een enkele stekker van type {{description}}?", + "de": "Welche Leistung liefert ein einzelner Stecker des Typs {{description}}?" }, "render": { "en": "{{description}} outputs at most {canonical({{key}}:output)}", - "nl": "{{description}} levert een vermogen van maximaal {canonical({{key}}:output)}" + "nl": "{{description}} levert een vermogen van maximaal {canonical({{key}}:output)}", + "de": "{{description}} liefert maximal {canonical({{key}}:output)}" }, "freeform": { "key": "{{key}}:output", @@ -1672,7 +1691,8 @@ "if": "{{key}}:output={{commonOutput}}", "then": { "en": "{{description}} outputs at most {{commonOutput}} A", - "nl": "{{description}} levert een vermogen van maximaal {{commonOutput}} A" + "nl": "{{description}} levert een vermogen van maximaal {{commonOutput}} A", + "de": "{{description}} liefert maximal {{commonOutput}} A" } }, "condition": { @@ -1761,7 +1781,7 @@ "then": { "nl": "Betalend te gebruiken, maar gratis voor klanten van het bijhorende hotel/café/ziekenhuis/...", "en": "Paid use, but free for customers of the hotel/pub/hospital/... who operates the charging station", - "de": "Die Nutzung ist kostenpflichtig, aber für Kunden des Betreibers der Einrichtung, wie Hotel, Krankenhaus, … kostenlos", + "de": "Die Nutzung ist kostenpflichtig, aber für Kunden des Betreibers der Einrichtung, wie Hotel, Krankenhaus, ... kostenlos", "ca": "De pagament, però gratuït per als clients de l'hotel/bar/hospital/... que gestiona l'estació de càrrega" } }, @@ -2498,7 +2518,7 @@ "question": { "en": "Has a
Type 1 CCS (aka Type 1 Combo)
connector", "nl": "Heeft een
Type 1 CCS (ook gekend als Type 1 Combo)
", - "de": "Verfügt über einen
Typ 1 CCS (Typ 1 Combo)
Stecker" + "de": "Verfügt über einen
Typ 1 CCS (Typ 1 Combo)
Stecker" }, "osmTags": "socket:type1_combo~*" }, diff --git a/assets/layers/cycleways_and_roads/cycleways_and_roads.json b/assets/layers/cycleways_and_roads/cycleways_and_roads.json index e502f75c3..edddd13a4 100644 --- a/assets/layers/cycleways_and_roads/cycleways_and_roads.json +++ b/assets/layers/cycleways_and_roads/cycleways_and_roads.json @@ -840,10 +840,12 @@ { "id": "incline", "question": { - "en": "Does {title()} have an incline?" + "en": "Does {title()} have an incline?", + "de": "Hat {title()} eine Steigung?" }, "render": { - "en": "This road has an slope of {incline}" + "en": "This road has an slope of {incline}", + "de": "Die Straße hat {incline} Steigung" }, "freeform": { "key": "incline", @@ -853,7 +855,8 @@ { "if": "incline=", "then": { - "en": "There is (probably) no incline here" + "en": "There is (probably) no incline here", + "de": "Hier gibt es (wahrscheinlich) keine Steigung" }, "hideInAnswer": true }, @@ -866,7 +869,8 @@ ] }, "then": { - "en": "This road has a slope" + "en": "This road has a slope", + "de": "Die Straße hat eine Steigung" }, "hideInAnswer": true } diff --git a/assets/layers/drinking_water/drinking_water.json b/assets/layers/drinking_water/drinking_water.json index 37926fc17..61b309cbd 100644 --- a/assets/layers/drinking_water/drinking_water.json +++ b/assets/layers/drinking_water/drinking_water.json @@ -302,21 +302,24 @@ "id": "fee", "question": { "en": "Is this drinking water point free to use?", - "nl": "Is dit drinkwaterpunt gratis te gebruiken?" + "nl": "Is dit drinkwaterpunt gratis te gebruiken?", + "de": "Kann diese Trinkwasserstelle kostenlos genutzt werden?" }, "mappings": [ { "if": "fee=no", "then": { "en": "Free to use", - "nl": "Gratis te gebruiken" + "nl": "Gratis te gebruiken", + "de": "Die Nutzung ist kostenlos" } }, { "if": "fee=yes", "then": { "en": "One needs to pay to use this drinking water point", - "nl": "Men moet betalen om dit drinkwaterpunt te gebruiken" + "nl": "Men moet betalen om dit drinkwaterpunt te gebruiken", + "de": "Die Nutzung ist kostenpflichtig" } } ] @@ -325,21 +328,24 @@ "id": "seasonal", "question": { "en": "Is this drinking water point available all year round?", - "nl": "Is dit drinkwaterpunt heel het jaar door beschikbaar?" + "nl": "Is dit drinkwaterpunt heel het jaar door beschikbaar?", + "de": "Ist die Trinkwasserstelle ganzjährig in Betrieb?" }, "mappings": [ { "if": "seasonal=no", "then": { "en": "This drinking water point is available all around the year", - "nl": "Dit drinkwaterpunt is heel het jaar door beschikbaar" + "nl": "Dit drinkwaterpunt is heel het jaar door beschikbaar", + "de": "Die Trinkwasserstelle ist ganzjährig in Betrieb" } }, { "if": "seasonal=summer", "then": { "en": "This drinking water point is only available in summer", - "nl": "Dit drinkwaterpunt is enkel in de zomer beschikbaar" + "nl": "Dit drinkwaterpunt is enkel in de zomer beschikbaar", + "de": "Die Trinkwasserstelle ist nur im Sommer in Betrieb" } }, { @@ -347,7 +353,8 @@ "icon": "./assets/layers/drinking_water/no_winter.svg", "then": { "en": "This drinking water point is closed during the winter", - "nl": "Dit drinkwaterpunt is gesloten in de winter" + "nl": "Dit drinkwaterpunt is gesloten in de winter", + "de": "Die Trinkwasserstelle ist im Winter nicht in Betrieb" } } ] @@ -357,7 +364,8 @@ "override": { "questionHint": { "en": "These are the opening hours if the drinking water fountain is operational.", - "nl": "Tijdens deze openingsuren is dit drinkwaterpunt bruikbaar." + "nl": "Tijdens deze openingsuren is dit drinkwaterpunt bruikbaar.", + "de": "Dies sind die Öffnungszeiten des Trinkwasserbrunnens, wenn dieser in Betrieb ist." }, "+mappings": [ { @@ -397,7 +405,8 @@ }, "then": { "en": "This drinking water fountain is closed this season. As such, the opening hours are not shown.", - "nl": "Dit drinkwaterpunt is dit seizoen gesloten. De openingsuren worden dus niet weergegeven." + "nl": "Dit drinkwaterpunt is dit seizoen gesloten. De openingsuren worden dus niet weergegeven.", + "de": "Der Trinkwasserbrunnen ist derzeit nicht in Betrieb. Deshalb werden keine Öffnungszeiten angezeigt." }, "hideInAnswer": true } @@ -408,7 +417,8 @@ "id": "bench-artwork", "question": { "en": "Does this drinking water fountain have an artistic element?", - "nl": "Heeft dit drinkwaterpunt een geintegreerd kunstwerk?" + "nl": "Heeft dit drinkwaterpunt een geintegreerd kunstwerk?", + "de": "Verfügt der Trinkwasserbrunnen über ein künstlerisches Element?" }, "mappings": [ { @@ -418,14 +428,16 @@ ], "then": { "en": "This drinking water point has an integrated artwork", - "nl": "Dit drinkwaterpunt heeft een geintegreerd kunstwerk" + "nl": "Dit drinkwaterpunt heeft een geintegreerd kunstwerk", + "de": "Die Trinkwasserstelle hat ein integriertes Kunstwerk" } }, { "if": "not:tourism:artwork=yes", "then": { "en": "This drinking water point does not have an integrated artwork", - "nl": "Dit drinkwaterpunt heeft geen geïntegreerd kunstwerk" + "nl": "Dit drinkwaterpunt heeft geen geïntegreerd kunstwerk", + "de": "Die Trinkwasserstelle hat kein integriertes Kunstwerk" }, "addExtraTags": [ "tourism=" @@ -435,14 +447,16 @@ "if": "tourism=", "then": { "en": "This drinking water point probably doesn't have an integrated artwork", - "nl": "Dit drinkwaterpunt heeft waarschijnlijk geen geïntegreerd kunstwerk" + "nl": "Dit drinkwaterpunt heeft waarschijnlijk geen geïntegreerd kunstwerk", + "de": "Die Trinkwasserstelle hat wahrscheinlich kein integriertes Kunstwerk" }, "hideInAnswer": true } ], "questionHint": { "en": "E.g. it has an integrated statue or other non-trivial, creative work", - "nl": "Bijvoorbeeld een standbeeld of ander, niet-triviaal kunstwerk" + "nl": "Bijvoorbeeld een standbeeld of ander, niet-triviaal kunstwerk", + "de": "Z.B. eine integrierte Statue oder andere künstlerische Werke" } }, { @@ -519,7 +533,8 @@ }, "then": { "en": "This is a historic, manual water pump where no drinking water can be found", - "nl": "Dit is een historische, manuele waterpomp waar geen drinkwater uitkomt" + "nl": "Dit is een historische, manuele waterpomp waar geen drinkwater uitkomt", + "de": "Dies ist eine historische, manuelle Wasserpumpe, an der kein Trinkwasser zu finden ist" } } ] diff --git a/assets/layers/hackerspace/hackerspace.json b/assets/layers/hackerspace/hackerspace.json index 3b0bc76c9..fe2d17279 100644 --- a/assets/layers/hackerspace/hackerspace.json +++ b/assets/layers/hackerspace/hackerspace.json @@ -304,60 +304,74 @@ [ "media_studio", { - "en": "a multimedia studio" + "en": "a multimedia studio", + "nl": "een multimedia-studio" }, { - "en": "multimedia studio" + "en": "multimedia studio", + "nl": "multimedia-studio" }, "./assets/layers/hackerspace/media_studio.svg" ], [ "sewing_machine", { - "en": "a sewing machine" + "en": "a sewing machine", + "de": "eine Nähmaschine", + "nl": "een naaimachine" }, { - "en": "sewing machine" + "en": "sewing machine", + "de": "Nähmaschine", + "nl": "naaimachine" }, "./assets/layers/hackerspace/sewing_machine.svg" ], [ "workshop:wood", { - "en": "a woodworking workshop" + "en": "a woodworking workshop", + "nl": "een houtbewerkingsatelier" }, { - "en": "woodworking workshop" + "en": "woodworking workshop", + "nl": "houtbewerkingsatelier" }, "./assets/layers/hackerspace/woodworking.svg" ], [ "workshop:ceramics", { - "en": "a ceramics workshop" + "en": "a ceramics workshop", + "nl": "een keramiekatelier" }, { - "en": "ceramics workshop" + "en": "ceramics workshop", + "nl": "keramiekatelier" }, "./assets/layers/hackerspace/ceramics.svg" ], [ "workshop:metal", { - "en": "a metal workshop" + "en": "a metal workshop", + "nl": "een metaalatelier" }, { - "en": "metal workshop" + "en": "metal workshop", + "nl": "metaalatelier" }, "./assets/layers/hackerspace/metal.svg" ], [ "bicycle:diy", { - "en": "a bicycle repair workshop" + "en": "a bicycle repair workshop", + "nl": "een fietsherstelplaats" }, { - "en": "bicycle repair workshop" + "en": "bicycle repair workshop", + "nl": "fietsherstelplaats" }, "./assets/layers/hackerspace/bicycle.svg" ] diff --git a/assets/layers/usersettings/usersettings.json b/assets/layers/usersettings/usersettings.json index baaaa9bce..d68e87d5a 100644 --- a/assets/layers/usersettings/usersettings.json +++ b/assets/layers/usersettings/usersettings.json @@ -51,7 +51,8 @@ "en": "The language was set via an URL-parameter and cannot be set by the user.", "de": "Die Sprache wurde über einen URL-Parameter gesetzt und kann nicht vom Benutzer eingestellt werden.²", "ca": "L'idioma es va establir mitjançant un paràmetre d'URL i l'usuari no pot definir-lo.", - "cs": "Jazyk byl nastaven pomocí parametru URL a uživatel jej nemůže nastavit.²" + "cs": "Jazyk byl nastaven pomocí parametru URL a uživatel jej nemůže nastavit.²", + "nl": "De taal werd ingesteld via een URL-parameter en kan niet manueel ingesteld worden." } } ] @@ -84,7 +85,8 @@ "en": "You have {_unreadMessages} messages
Open your inbox", "de": "Du hast {_unreadMessages}
Öffne Deinen Posteingang", "ca": "Tens {_unreadMessages} missatges
Obri la safata d'entrada", - "cs": "Máte {_unreadMessages}
Otevřít schránku" + "cs": "Máte {_unreadMessages}
Otevřít schránku", + "nl": "Je hebt {_unreadMessages} ongelezen berichten
Ga naar je inbox" }, "href": "{_backend}/messages/inbox" } @@ -101,7 +103,8 @@ "en": "Open your settings on OpenStreetMap.org", "de": "Öffne Deine Einstellungen auf OpenStreetMap.org", "ca": "Obriu la vostra configuració a OpenStreetMap.org", - "cs": "Otevřít vaše nastavení na OpenStreetMap.org" + "cs": "Otevřít vaše nastavení na OpenStreetMap.org", + "nl": "Open je instellingen op OpenStreetMap.org" }, "href": "{_backend}/account/edit" } @@ -116,7 +119,8 @@ { "id": "a11y-features", "question": { - "en": "What accessibility features should be applied?" + "en": "What accessibility features should be applied?", + "nl": "Wanneer moet de toegankelijkheidsmode ingeschakeld worden?" }, "mappings": [ { @@ -124,20 +128,23 @@ "alsoShowIf": "mapcomplete-a11y=", "then": { "en": "Enable accessibility features when arrow keys are used to navigate the map", - "ca": "Activar les funcions d'accessibilitat quan s'utilitzen les tecles de fletxa per navegar pel mapa" + "ca": "Activar les funcions d'accessibilitat quan s'utilitzen les tecles de fletxa per navegar pel mapa", + "nl": "Schakel toegankelijkheidsmode aan wanneer op de pijltjestoetsen wordt geduwd om de kaart te bewegen" } }, { "if": "mapcomplete-a11y=always", "then": { "en": "Always enable accessibility features", - "ca": "Sempre habilita les característiques d'accessibilitat" + "ca": "Sempre habilita les característiques d'accessibilitat", + "nl": "Schakel de toegankelijkheidsmode altijd aan" } }, { "if": "mapcomplete-a11y=never", "then": { - "en": "Never enable accessibility features" + "en": "Never enable accessibility features", + "nl": "Gebruik geen toegankelijkheidsmode" } } ] @@ -407,7 +414,8 @@ "question": { "en": "Should a crosshair be shown in the center of the display?", "cs": "Měl by se uprostřed displeje zobrazovat kříž?", - "de": "Soll ein Fadenkreuz in der Mitte des Bildschirms angezeigt werden?" + "de": "Soll ein Fadenkreuz in der Mitte des Bildschirms angezeigt werden?", + "nl": "Moet er een kruisje getoond worden in het centrum van je display?" }, "questionHint": { "en": "This can help to accurately position a new element", @@ -442,7 +450,8 @@ "en": "Should north always be up?", "de": "Soll Norden immer oben sein?", "ca": "El nord hauria d'estar sempre amunt?", - "cs": "Měl by být sever vždy nahoře?" + "cs": "Měl by být sever vždy nahoře?", + "nl": "Moet het noorden altijd naar boven getoond worden?" }, "mappings": [ { @@ -464,7 +473,8 @@ "de": "Norden immer nach oben zeigen lassen", "fr": "Toujours garder le nord en haut", "ca": "Mantingueu sempre el nord apuntant cap amunt", - "cs": "Sever vždy směřujte nahoru" + "cs": "Sever vždy směřujte nahoru", + "nl": "Hou het noorden altijd naar boven" } } ] @@ -481,7 +491,8 @@ "de": "Laden Sie den privaten Schlüssel für Ihr Mangrove-Konto herunter", "da": "Hent den private nøgle til din Mangrove-konto", "ca": "Baixeu la clau privada del vostre compte de Mangrove", - "cs": "Stáhnout soukromý klíč pro Mangrove účet" + "cs": "Stáhnout soukromý klíč pro Mangrove účet", + "nl": "Download de private sleutel van je Mangrove-account" } }, "after": { diff --git a/assets/themes/cyclenodes/cyclenodes.json b/assets/themes/cyclenodes/cyclenodes.json index 9e2d3ba35..f77308652 100644 --- a/assets/themes/cyclenodes/cyclenodes.json +++ b/assets/themes/cyclenodes/cyclenodes.json @@ -36,7 +36,7 @@ "name": { "en": "Node to node links", "de": "Knotenpunktverbindungen", - "es": "enlaces nodo a nodo", + "es": "Vínculos entre nodos", "nl": "Verbindingen van node naar node", "fr": "liens noeud à noeud", "ca": "Enllaços node a node", @@ -57,7 +57,7 @@ "render": { "en": "Node to node link", "de": "Knotenpunktverbindung", - "es": "enlace nodo a nodo", + "es": "Vínculos entre nodos", "nl": "Node-naar-node verbinding", "fr": "lien noeud à noeud", "ca": "Enllaç node a node", @@ -70,7 +70,7 @@ "then": { "en": "Node to node link {ref}", "de": "Knotenpunktverbindung {ref}", - "es": "enlace nodo a nodo {ref}", + "es": "Vínculos entre nodos {ref}", "nl": "Node-naar-node verbinding {ref}", "fr": "lien noeud à noeud {ref}", "ca": "Enllaç node a node {ref}", @@ -131,7 +131,7 @@ "en": "Nodes", "de": "Knotenpunkte", "ca": "Nodes", - "es": "nodos", + "es": "Nodos", "nb_NO": "noder", "nl": "Knooppunten", "fr": "noeuds", @@ -202,7 +202,8 @@ "then": { "en": "Proposed cycle node {proposed:rcn_ref}", "nl": "Voorgesteld fietsknooppunt {proposed:rcn_ref}", - "de": "Vorgeschlagener Radknoten {proposed:rcn_ref}" + "de": "Vorgeschlagener Radknoten {proposed:rcn_ref}", + "es": "Nodo de ciclo propuesto {proposed:rcn_ref}" } } ] @@ -215,7 +216,8 @@ "nl": "Wat is het referentienummer van dit fietsknooppunt?", "de": "Wie lautet die Nummer des Knotenpunkts im Fahrradknotenpunktnetzwerk?", "cs": "Jaké je referenční číslo tohoto cyklistického uzlu?", - "ca": "Quin és el número de referència d'aquest node ciclista?" + "ca": "Quin és el número de referència d'aquest node ciclista?", + "es": "¿Cuál es el número de referencia de este nodo cíclico?" }, "freeform": { "key": "rcn_ref", @@ -224,7 +226,8 @@ "en": "e.g. 1", "nl": "bijv. 1", "de": "z.B. 1", - "cs": "e.g. 1" + "cs": "e.g. 1", + "es": "Por ejemplo, 1" } }, "render": { @@ -232,7 +235,8 @@ "nl": "Dit fietsknooppunt heeft referentienummer {rcn_ref}", "de": "Knotenpunktnummer {rcn_ref} des Fahrradknotenpunktnetzwerks", "cs": "Tento cyklistický uzel má referenční číslo {rcn_ref}", - "ca": "Aquest node ciclista té la referència número {rcn_ref}" + "ca": "Aquest node ciclista té la referència número {rcn_ref}", + "es": "Este nodo cíclico tiene el número de referencia {rcn_ref}" }, "condition": "rcn_ref~*" }, @@ -290,7 +294,8 @@ "en": "e.g. 3", "nl": "bijv. 3", "de": "z.B. 3", - "cs": "e.g. 3" + "cs": "e.g. 3", + "es": "Por ejemplo, 3" } }, "id": "node-expected_rcn_route_relations" @@ -307,7 +312,8 @@ "en": "a cycling node", "nl": "een fietsknooppunt", "de": "ein Knoten eines Fahrradknotenpunktnetzwerks", - "ca": "un node ciclista" + "ca": "un node ciclista", + "es": "un nodo cíclico" }, "snapToLayer": [ "cycleways_and_roads" @@ -341,13 +347,15 @@ "name": { "en": "Cycling guideposts", "de": "Fahrrad-Wegweiser", - "cs": "Cyklistické ukazatele" + "cs": "Cyklistické ukazatele", + "es": "Indicadores de ciclismo" }, "title": { "render": { "en": "Cycling guidepost", "de": "Fahrrad-Wegweiser", - "cs": "Cyklistický ukazatel" + "cs": "Cyklistický ukazatel", + "es": "Hito ciclista" } } }, @@ -375,7 +383,8 @@ "title": { "en": "a route marker for a node to node link", "de": "Eine Routenmarkierung für eine Verbindung von Knoten zu Knoten", - "cs": "značka trasy pro spojení mezi uzlem" + "cs": "značka trasy pro spojení mezi uzlem", + "es": "Un marcador de ruta para un enlace de nodo a nodo" }, "=exampleImages": [ "./assets/layers/route_marker/bicycle_route_marker.jpg" diff --git a/assets/themes/fritures/fritures.json b/assets/themes/fritures/fritures.json index 7fe6e095a..1543f2b9a 100644 --- a/assets/themes/fritures/fritures.json +++ b/assets/themes/fritures/fritures.json @@ -64,14 +64,16 @@ { "question": { "en": "No oil type preference", - "de": "Kein Öltyp bevorzugt" + "de": "Kein Öltyp bevorzugt", + "es": "No se prefiere ningún tipo de aceite" } }, { "question": { "en": "Only show fritures using vegetable oil", "de": "Nur Friteusen mit Pflanzenöl anzeigen", - "ca": "Només mostra freiduries que utilitzen oli vegetal" + "ca": "Només mostra freiduries que utilitzen oli vegetal", + "es": "Solo muestra freiduras que utilizan aceite vegetal" }, "osmTags": "friture:oil=vegetable" }, @@ -79,7 +81,8 @@ "question": { "en": "Only show fritures using animal oil", "de": "Nur Friteusen mit tierischem Öl anzeigen", - "ca": "Només mostra freiduries que utilitzen oli animal" + "ca": "Només mostra freiduries que utilitzen oli animal", + "es": "Solo muestra freiduras que utilizan aceite animal" }, "osmTags": "friture:oil=animal" } diff --git a/assets/themes/hackerspaces/hackerspaces.json b/assets/themes/hackerspaces/hackerspaces.json index a8bf4a070..48ed9347b 100644 --- a/assets/themes/hackerspaces/hackerspaces.json +++ b/assets/themes/hackerspaces/hackerspaces.json @@ -1,8 +1,8 @@ { "id": "hackerspaces", "title": { - "en": "Hackerspaces", - "de": "Hackerspaces", + "en": "Hackerspaces and makerspaces", + "de": "Hackerspaces und Makerspaces", "it": "Hackerspace", "ru": "Хакерспейсы", "zh_Hant": "駭客空間", @@ -13,7 +13,7 @@ "ca": "Espai per a hackers", "pa_PK": "ہیکر دے تھاں", "cs": "Hackerspaces", - "es": "Hackerspaces", + "es": "Hackerspaces and makerspaces", "eu": "Hackerspace", "pl": "Hackerspace'y" }, diff --git a/assets/themes/icecream/icecream.json b/assets/themes/icecream/icecream.json index cea2d3476..509fd9e86 100644 --- a/assets/themes/icecream/icecream.json +++ b/assets/themes/icecream/icecream.json @@ -4,13 +4,15 @@ "en": "Icecream", "de": "Eiscreme", "cs": "Zmrzlina", - "ca": "Gelat" + "ca": "Gelat", + "es": "Helado" }, "description": { "en": "A map showing ice cream parlors and ice cream vending machines", "de": "Eine Karte, die Eisdielen und Eisautomaten zeigt", "cs": "Mapa zobrazující prodej zmrzliny a automaty na zmrzlinu", - "ca": "Un mapa que mostra les gelateries i les màquines expenedores de gelats" + "ca": "Un mapa que mostra les gelateries i les màquines expenedores de gelats", + "es": "Mapa de heladerías y máquinas expendedoras de helados" }, "icon": "./assets/layers/ice_cream/ice_cream.svg", "layers": [ diff --git a/assets/themes/mapcomplete-changes/mapcomplete-changes.json b/assets/themes/mapcomplete-changes/mapcomplete-changes.json index 6c7376596..743e2ab97 100644 --- a/assets/themes/mapcomplete-changes/mapcomplete-changes.json +++ b/assets/themes/mapcomplete-changes/mapcomplete-changes.json @@ -1,13 +1,20 @@ { "id": "mapcomplete-changes", "title": { - "en": "Changes made with MapComplete" + "en": "Changes made with MapComplete", + "ca": "Canvis fets amb MapComplete", + "de": "Mit MapComplete vorgenommene Änderungen" }, "shortDescription": { - "en": "Shows changes made by MapComplete" + "en": "Shows changes made by MapComplete", + "ca": "Mostra els canvis fets amb MapComplete", + "de": "Zeigt die von MapComplete vorgenommenen Änderungen an" }, "description": { - "en": "This maps shows all the changes made with MapComplete" + "en": "This maps shows all the changes made with MapComplete", + "ca": "Aquest mapa mostra tots els canvis fets amb MapComplete", + "de": "Diese Karte zeigt alle mit MapComplete vorgenommenen Änderungen", + "es": "Este mapa muestra todos los cambios realizados con MapComplete" }, "icon": "./assets/svg/logo.svg", "hideFromOverview": true, @@ -20,7 +27,8 @@ { "id": "mapcomplete-changes", "name": { - "en": "Changeset centers" + "en": "Changeset centers", + "de": "Zentrum der Änderungssätze" }, "minzoom": 0, "source": { @@ -31,41 +39,56 @@ }, "title": { "render": { - "en": "Changeset for {theme}" + "en": "Changeset for {theme}", + "ca": "Conjunt de canvis per a {theme}", + "de": "Änderungssatz für {theme}" } }, "description": { - "en": "Shows all MapComplete changes" + "en": "Shows all MapComplete changes", + "ca": "Mostra tots els canvis de MapComplete", + "de": "Zeigt alle MapComplete-Änderungen", + "es": "Muestra todos los cambios de MapComplete" }, "tagRenderings": [ { "id": "show_changeset_id", "render": { - "en": "Changeset {id}" + "en": "Changeset {id}", + "ca": "Conjunt de canvi {id}", + "de": "Änderungssatz {id}" } }, { "id": "contributor", "question": { - "en": "What contributor did make this change?" + "en": "What contributor did make this change?", + "ca": "Quin col·laborador va fer aquest canvi?", + "de": "Wer hat diese Änderung vorgenommen?" }, "freeform": { "key": "user" }, "render": { - "en": "Change made by {user}" + "en": "Change made by {user}", + "ca": "Canvi fet per {user}", + "de": "Änderung von {user}" } }, { "id": "theme-id", "question": { - "en": "What theme was used to make this change?" + "en": "What theme was used to make this change?", + "ca": "Quin tema es va utilitzar per fer aquest canvi?", + "de": "Welches Theme wurde für diese Änderung verwendet?" }, "freeform": { "key": "theme" }, "render": { - "en": "Change with theme {theme}" + "en": "Change with theme {theme}", + "ca": "Canvi amb el tema {theme}", + "de": "Geändert mit Thema {theme}" } }, { @@ -74,19 +97,27 @@ "key": "locale" }, "question": { - "en": "What locale (language) was this change made in?" + "en": "What locale (language) was this change made in?", + "ca": "Amb quina configuració regional (idioma) s'ha fet aquest canvi?", + "de": "In welcher Benutzersprache wurde diese Änderung vorgenommen?" }, "render": { - "en": "User locale is {locale}" + "en": "User locale is {locale}", + "ca": "La configuració regional de l'usuari és {locale}", + "de": "Benutzersprache {locale}" } }, { "id": "host", "render": { - "en": "Change with with {host}" + "en": "Change with with {host}", + "ca": "Canviat amb {host}", + "de": "Geändert über {host}" }, "question": { - "en": "What host (website) was this change made with?" + "en": "What host (website) was this change made with?", + "ca": "Amb quin amfitrió (lloc web) es va fer aquest canvi?", + "de": "Über welchen Host (Webseite) wurde diese Änderung vorgenommen?" }, "freeform": { "key": "host" @@ -107,10 +138,14 @@ { "id": "version", "question": { - "en": "What version of MapComplete was used to make this change?" + "en": "What version of MapComplete was used to make this change?", + "ca": "Quina versió de MapComplete es va utilitzar per fer aquest canvi?", + "de": "Welche Version von MapComplete wurde verwendet, um diese Änderung vorzunehmen?" }, "render": { - "en": "Made with {editor}" + "en": "Made with {editor}", + "ca": "Fet amb {editor}", + "de": "Erstellt mit {editor}" }, "freeform": { "key": "editor" @@ -460,7 +495,10 @@ } ], "question": { - "en": "Themename contains {search}" + "en": "Themename contains {search}", + "ca": "El nom del tema conté {search}", + "de": "Themename enthält {search}", + "es": "El nombre del tema contiene {search}" } } ] @@ -476,7 +514,8 @@ } ], "question": { - "en": "Themename does not contain {search}" + "en": "Themename does not contain {search}", + "de": "Der Name enthält nicht {search}" } } ] @@ -492,7 +531,9 @@ } ], "question": { - "en": "Made by contributor {search}" + "en": "Made by contributor {search}", + "ca": "Fet pel col·laborador {search}", + "de": "Der Name enthält nicht {search}" } } ] @@ -508,7 +549,9 @@ } ], "question": { - "en": "Not made by contributor {search}" + "en": "Not made by contributor {search}", + "ca": "No fet pel col·laborador {search}", + "de": "Nicht erstellt von {search}" } } ] @@ -525,7 +568,9 @@ } ], "question": { - "en": "Made before {search}" + "en": "Made before {search}", + "ca": "Fet abans de {search}", + "de": "Erstellt vor {search}" } } ] @@ -542,7 +587,9 @@ } ], "question": { - "en": "Made after {search}" + "en": "Made after {search}", + "ca": "Fet després de {search}", + "de": "Erstellt nach {search}" } } ] @@ -558,7 +605,9 @@ } ], "question": { - "en": "User language (iso-code) {search}" + "en": "User language (iso-code) {search}", + "ca": "Idioma de l'usuari (codi iso) {search}", + "de": "Benutzersprache (ISO-Code) {search}" } } ] @@ -574,7 +623,9 @@ } ], "question": { - "en": "Made with host {search}" + "en": "Made with host {search}", + "ca": "Fet amb l'amfitrió {search}", + "de": "Erstellt mit Host {search}" } } ] @@ -585,7 +636,9 @@ { "osmTags": "add-image>0", "question": { - "en": "Changeset added at least one image" + "en": "Changeset added at least one image", + "ca": "El conjunt de canvis ha afegit almenys una imatge", + "de": "Änderungssatz hat mindestens ein Bild hinzugefügt" } } ] @@ -596,7 +649,8 @@ { "osmTags": "theme!=grb", "question": { - "en": "Exclude GRB theme" + "en": "Exclude GRB theme", + "de": "GRB-Theme ausschließen" } } ] @@ -607,7 +661,8 @@ { "osmTags": "theme!=etymology", "question": { - "en": "Exclude etymology theme" + "en": "Exclude etymology theme", + "de": "Etymologie-Thema ausschließen" } } ] @@ -622,7 +677,9 @@ { "id": "link_to_more", "render": { - "en": "More statistics can be found here" + "en": "More statistics can be found here", + "ca": "Es pot trobar més estadística aquí", + "de": "Mehr Statistiken gibt es hier" } }, { diff --git a/langs/de.json b/langs/de.json index d9a6a85a6..aa59c887b 100644 --- a/langs/de.json +++ b/langs/de.json @@ -467,7 +467,7 @@ "selectItem": "Objekt auswählen, das dem Kartenmittelpunkt (Fadenkreuz) am nächsten liegt. Nur wenn die Tastaturnavigation verwendet wird", "selectItem2": "Objekt auswählen, das dem Kartenmittelpunkt (Fadenkreuz) am zweitnächsten liegt. Nur wenn die Tastaturnavigation verwendet wird", "selectItem3": "Objekt auswählen, das dem Kartenmittelpunkt (Fadenkreuz) am drittnächsten liegt. Nur wenn die Tastaturnavigation verwendet wird", - "selectItemI": "Objekt auswählen, das dem Kartenmittelpunkt (Fadenkreuz) am viertnächsten liegt. Nur wenn die Tastaturnavigation verwendet wird", + "selectItemI": "Objekt auswählen, das dem Kartenmittelpunkt (Fadenkreuz) am {i} nächsten liegt. Nur wenn die Tastaturnavigation verwendet wird", "selectMap": "Karte aus externer Quelle als Hintergrund wählen. Wechselt zwischen den zwei besten verfügbaren Ebenen", "selectMapnik": "OpenStreetMap-carto als Hintergrundebene wählen", "selectOsmbasedmap": "OpenStreetMap-basierte Karte als Hintergrund auswählen (oder Hintergrundebene deaktivieren)", diff --git a/langs/layers/de.json b/langs/layers/de.json index ce42f1fb8..3df954e04 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -35,16 +35,6 @@ "1": { "title": "eine freistehende Posterbox" }, - "10": { - "description": "Verwendet für Werbeschilder, Leuchtreklamen, Logos und institutionelle Eingangsschilder", - "title": "ein Schild" - }, - "11": { - "title": "eine Skulptur" - }, - "12": { - "title": "eine Wandmalerei" - }, "2": { "title": "eine wandmontierte Posterbox" }, @@ -71,6 +61,16 @@ }, "9": { "title": "ein Totem" + }, + "10": { + "description": "Verwendet für Werbeschilder, Leuchtreklamen, Logos und institutionelle Eingangsschilder", + "title": "ein Schild" + }, + "11": { + "title": "eine Skulptur" + }, + "12": { + "title": "eine Wandmalerei" } }, "tagRenderings": { @@ -165,9 +165,6 @@ "1": { "then": "Dies ist ein Brett" }, - "10": { - "then": "Dies ist eine Wandmalerei" - }, "2": { "then": "Dies ist eine Litfaßsäule" }, @@ -191,6 +188,9 @@ }, "9": { "then": "Dies ist ein Totem" + }, + "10": { + "then": "Dies ist eine Wandmalerei" } }, "question": "Welche Art von Werbung ist das?", @@ -205,9 +205,6 @@ "1": { "then": "Brett" }, - "10": { - "then": "Wandmalerei" - }, "2": { "then": "Posterbox" }, @@ -231,6 +228,9 @@ }, "9": { "then": "Totem" + }, + "10": { + "then": "Wandmalerei" } } } @@ -353,15 +353,6 @@ "1": { "then": "Wandbild" }, - "10": { - "then": "Azulejo (spanische dekorative Fliesenarbeit)" - }, - "11": { - "then": "Fliesenarbeit" - }, - "12": { - "then": "Holzschnitzerei" - }, "2": { "then": "Malerei" }, @@ -385,6 +376,15 @@ }, "9": { "then": "Relief" + }, + "10": { + "then": "Azulejo (spanische dekorative Fliesenarbeit)" + }, + "11": { + "then": "Fliesenarbeit" + }, + "12": { + "then": "Holzschnitzerei" } }, "question": "Um welche Art Kunstwerk handelt es sich?", @@ -1942,27 +1942,6 @@ "1": { "question": "Verfügt über einen
Schuko-Stecker ohne Erdungsstift (CEE7/4 Typ F)
" }, - "10": { - "question": "Hat einen
Typ 2 (Mennekes)
Anschluss mit Kabel" - }, - "11": { - "question": "Hat einen
Tesla Supercharger CCS (Typ 2 CSS vonTesla)
Anschluss" - }, - "12": { - "question": "Hat einen
Tesla Supercharger (Destination)
Anschluss" - }, - "13": { - "question": "Hat einen
Tesla Supercharger (Destination) (Typ 2 von Tesla)
Anschluss mit Kabel" - }, - "14": { - "question": "Hat einen
USB-Anschluss zum Aufladen von Telefonen und kleinen Elektrogeräten
" - }, - "15": { - "question": "Hat einen
Bosch Active Connect Anschluss mit 3 Pins
und Kabel" - }, - "16": { - "question": "Hat einen
Bosch Active Connect Anschluss mit 5 Pins
und Kabel" - }, "2": { "question": "Verfügt über einen
europäischen Netzstecker mit Erdungsstift (CEE7/4 Typ E)
Anschluss" }, @@ -1986,6 +1965,27 @@ }, "9": { "question": "Hat einen
Typ 2 CCS (Mennekes)
Anschluss" + }, + "10": { + "question": "Hat einen
Typ 2 (Mennekes)
Anschluss mit Kabel" + }, + "11": { + "question": "Hat einen
Tesla Supercharger CCS (Typ 2 CSS vonTesla)
Anschluss" + }, + "12": { + "question": "Hat einen
Tesla Supercharger (Destination)
Anschluss" + }, + "13": { + "question": "Hat einen
Tesla Supercharger (Destination) (Typ 2 von Tesla)
Anschluss mit Kabel" + }, + "14": { + "question": "Hat einen
USB-Anschluss zum Aufladen von Telefonen und kleinen Elektrogeräten
" + }, + "15": { + "question": "Hat einen
Bosch Active Connect Anschluss mit 3 Pins
und Kabel" + }, + "16": { + "question": "Hat einen
Bosch Active Connect Anschluss mit 5 Pins
und Kabel" } } } @@ -2041,6 +2041,30 @@ "1": { "then": "Schuko-Stecker ohne Erdungsstift (CEE7/4 Typ F)" }, + "2": { + "then": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" + }, + "3": { + "then": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" + }, + "4": { + "then": "Chademo-Anschluss" + }, + "5": { + "then": "Chademo-Anschluss" + }, + "6": { + "then": "Typ 1 mit Kabel (J1772)" + }, + "7": { + "then": "Typ 1 mit Kabel (J1772)" + }, + "8": { + "then": "Typ 1 ohne Kabel (J1772)" + }, + "9": { + "then": " Typ 1 ohne Kabel (J1772)" + }, "10": { "then": "Typ 1 CCS (Typ 1 Combo)" }, @@ -2071,9 +2095,6 @@ "19": { "then": "Typ 2 mit Kabel (mennekes)" }, - "2": { - "then": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" - }, "20": { "then": "Tesla Supercharger CCS (Typ 2 CSS von Tesla)" }, @@ -2104,32 +2125,11 @@ "29": { "then": " Bosch Active Connect mit 3 Pins und Kabel" }, - "3": { - "then": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" - }, "30": { "then": "Bosch Active Connect mit 5 Pins und Kabel" }, "31": { "then": " Bosch Active Connect mit 5 Pins und Kabel" - }, - "4": { - "then": "Chademo-Anschluss" - }, - "5": { - "then": "Chademo-Anschluss" - }, - "6": { - "then": "Typ 1 mit Kabel (J1772)" - }, - "7": { - "then": "Typ 1 mit Kabel (J1772)" - }, - "8": { - "then": "Typ 1 ohne Kabel (J1772)" - }, - "9": { - "then": " Typ 1 ohne Kabel (J1772)" } }, "question": "Welche Ladeanschlüsse gibt es hier?" @@ -2323,24 +2323,6 @@ "1": { "2": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" }, - "10": { - "2": "Tesla Supercharger CCS (Typ 2 CSS von Tesla)" - }, - "11": { - "2": "Tesla Supercharger (Destination)" - }, - "12": { - "2": "Tesla Supercharger (Destination) (Typ 2 mit Kabel von Tesla)" - }, - "13": { - "2": "USB zum Aufladen von Handys und kleinen Elektrogeräten" - }, - "14": { - "2": " Bosch Active Connect mit 3 Pins und Kabel" - }, - "15": { - "2": " Bosch Active Connect mit 5 Pins und Kabel" - }, "2": { "2": "Chademo-Stecker" }, @@ -2364,6 +2346,24 @@ }, "9": { "2": "Typ 2 mit Kabel (Mennekes)" + }, + "10": { + "2": "Tesla Supercharger CCS (Typ 2 CSS von Tesla)" + }, + "11": { + "2": "Tesla Supercharger (Destination)" + }, + "12": { + "2": "Tesla Supercharger (Destination) (Typ 2 mit Kabel von Tesla)" + }, + "13": { + "2": "USB zum Aufladen von Handys und kleinen Elektrogeräten" + }, + "14": { + "2": " Bosch Active Connect mit 3 Pins und Kabel" + }, + "15": { + "2": " Bosch Active Connect mit 5 Pins und Kabel" } } } @@ -3141,15 +3141,6 @@ "1": { "then": "Dieser Radweg hat einen festen Belag" }, - "10": { - "then": "Dieser Radweg besteht aus feinem Schotter" - }, - "11": { - "then": "Der Radweg ist aus Kies" - }, - "12": { - "then": "Dieser Radweg besteht aus Rohboden" - }, "2": { "then": "Der Radweg ist aus Asphalt" }, @@ -3173,6 +3164,15 @@ }, "9": { "then": "Der Radweg ist aus Schotter" + }, + "10": { + "then": "Dieser Radweg besteht aus feinem Schotter" + }, + "11": { + "then": "Der Radweg ist aus Kies" + }, + "12": { + "then": "Dieser Radweg besteht aus Rohboden" } }, "question": "Was ist der Belag dieses Radwegs?", @@ -3221,15 +3221,6 @@ "1": { "then": "Dieser Radweg hat einen festen Belag" }, - "10": { - "then": "Dieser Radweg besteht aus feinem Schotter" - }, - "11": { - "then": "Der Radweg ist aus Kies" - }, - "12": { - "then": "Dieser Radweg besteht aus Rohboden" - }, "2": { "then": "Der Radweg ist aus Asphalt" }, @@ -3253,6 +3244,15 @@ }, "9": { "then": "Der Radweg ist aus Schotter" + }, + "10": { + "then": "Dieser Radweg besteht aus feinem Schotter" + }, + "11": { + "then": "Der Radweg ist aus Kies" + }, + "12": { + "then": "Dieser Radweg besteht aus Rohboden" } }, "question": "Was ist der Belag dieser Straße?", @@ -4193,54 +4193,6 @@ } } }, - "10": { - "options": { - "0": { - "question": "Keine Bevorzugung von Hunden" - }, - "1": { - "question": "Hunde erlaubt" - }, - "2": { - "question": "Keine Hunde erlaubt" - } - } - }, - "11": { - "options": { - "0": { - "question": "Internetzugang vorhanden" - } - } - }, - "12": { - "options": { - "0": { - "question": "Stromanschluss vorhanden" - } - } - }, - "13": { - "options": { - "0": { - "question": "Hat zuckerfreie Angebote" - } - } - }, - "14": { - "options": { - "0": { - "question": "Hat glutenfreie Angebote" - } - } - }, - "15": { - "options": { - "0": { - "question": "Hat laktosefreie Angebote" - } - } - }, "2": { "options": { "0": { @@ -4311,6 +4263,54 @@ "question": "Nutzung kostenlos" } } + }, + "10": { + "options": { + "0": { + "question": "Keine Bevorzugung von Hunden" + }, + "1": { + "question": "Hunde erlaubt" + }, + "2": { + "question": "Keine Hunde erlaubt" + } + } + }, + "11": { + "options": { + "0": { + "question": "Internetzugang vorhanden" + } + } + }, + "12": { + "options": { + "0": { + "question": "Stromanschluss vorhanden" + } + } + }, + "13": { + "options": { + "0": { + "question": "Hat zuckerfreie Angebote" + } + } + }, + "14": { + "options": { + "0": { + "question": "Hat glutenfreie Angebote" + } + } + }, + "15": { + "options": { + "0": { + "question": "Hat laktosefreie Angebote" + } + } } } }, @@ -4430,6 +4430,30 @@ "1": { "then": "Die Fitness-Station hat ein Schild mit Anweisungen für eine bestimmte Übung." }, + "2": { + "then": "Die Fitness-Station hat eine Einrichtung für Sit-ups." + }, + "3": { + "then": "Die Fitness-Station hat eine Vorrichtung für Liegestütze. In der Regel eine oder mehrere niedrige Reckstangen." + }, + "4": { + "then": "Die Fitness-Station hat Stangen zum Dehnen." + }, + "5": { + "then": "Die Fitness-Station hat eine Vorrichtung für Rückenstrecker (Hyperextensions)." + }, + "6": { + "then": "Die Fitness-Station hat Ringe für Gymnastikübungen." + }, + "7": { + "then": "Die Fitness-Station hat eine horizontale Leiter (Monkey Bars)." + }, + "8": { + "then": "Die Fitness-Station hat eine Sprossenwand zum Klettern." + }, + "9": { + "then": "Die Fitness-Station hat Pfosten für Slalomübungen." + }, "10": { "then": "Die Fitness-Station hat Trittsteine." }, @@ -4460,9 +4484,6 @@ "19": { "then": "Die Fitness-Station hat Kampfseile (battle ropes)." }, - "2": { - "then": "Die Fitness-Station hat eine Einrichtung für Sit-ups." - }, "20": { "then": "Die Fitness-Station hat ein Fahrradergometer." }, @@ -4477,27 +4498,6 @@ }, "24": { "then": "Die Fitness-Station hat eine Slackline." - }, - "3": { - "then": "Die Fitness-Station hat eine Vorrichtung für Liegestütze. In der Regel eine oder mehrere niedrige Reckstangen." - }, - "4": { - "then": "Die Fitness-Station hat Stangen zum Dehnen." - }, - "5": { - "then": "Die Fitness-Station hat eine Vorrichtung für Rückenstrecker (Hyperextensions)." - }, - "6": { - "then": "Die Fitness-Station hat Ringe für Gymnastikübungen." - }, - "7": { - "then": "Die Fitness-Station hat eine horizontale Leiter (Monkey Bars)." - }, - "8": { - "then": "Die Fitness-Station hat eine Sprossenwand zum Klettern." - }, - "9": { - "then": "Die Fitness-Station hat Pfosten für Slalomübungen." } }, "question": "Welche Übungsgeräte gibt es an dieser Fitness-Station?" @@ -4617,21 +4617,6 @@ "1": { "then": "Dies ist eine Pommesbude" }, - "10": { - "then": "Hier werden chinesische Gerichte serviert" - }, - "11": { - "then": "Hier werden griechische Gerichte serviert" - }, - "12": { - "then": "Hier werden indische Gerichte serviert" - }, - "13": { - "then": "Hier werden türkische Gerichte serviert" - }, - "14": { - "then": "Hier werden thailändische Gerichte serviert" - }, "2": { "then": "Bietet vorwiegend Pastagerichte an" }, @@ -4655,6 +4640,21 @@ }, "9": { "then": "Hier werden französische Gerichte serviert" + }, + "10": { + "then": "Hier werden chinesische Gerichte serviert" + }, + "11": { + "then": "Hier werden griechische Gerichte serviert" + }, + "12": { + "then": "Hier werden indische Gerichte serviert" + }, + "13": { + "then": "Hier werden türkische Gerichte serviert" + }, + "14": { + "then": "Hier werden thailändische Gerichte serviert" } }, "question": "Was für Essen gibt es hier?", @@ -5286,6 +5286,30 @@ "1": { "then": "Dies ist ein Auditorium" }, + "2": { + "then": "Dies ist ein Schlafzimmer" + }, + "3": { + "then": "Dies ist eine Kapelle" + }, + "4": { + "then": "Dies ist ein Klassenzimmer" + }, + "5": { + "then": "Dies ist ein Klassenzimmer" + }, + "6": { + "then": "Dies ist ein Computerraum" + }, + "7": { + "then": "Dies ist ein Konferenzraum" + }, + "8": { + "then": "Dies ist eine Krypta" + }, + "9": { + "then": "Dies ist eine Küche" + }, "10": { "then": "Dies ist ein Labor" }, @@ -5316,9 +5340,6 @@ "19": { "then": "Dies ist ein Lagerraum" }, - "2": { - "then": "Dies ist ein Schlafzimmer" - }, "20": { "then": "Dies ist ein Technikraum" }, @@ -5327,27 +5348,6 @@ }, "22": { "then": "Dies ist ein Wartezimmer" - }, - "3": { - "then": "Dies ist eine Kapelle" - }, - "4": { - "then": "Dies ist ein Klassenzimmer" - }, - "5": { - "then": "Dies ist ein Klassenzimmer" - }, - "6": { - "then": "Dies ist ein Computerraum" - }, - "7": { - "then": "Dies ist ein Konferenzraum" - }, - "8": { - "then": "Dies ist eine Krypta" - }, - "9": { - "then": "Dies ist eine Küche" } }, "question": "Wie wird dieser Raum genutzt?" @@ -5974,19 +5974,6 @@ } } }, - "10": { - "options": { - "0": { - "question": "Alle Notizen" - }, - "1": { - "question": "Importnotizen ausblenden" - }, - "2": { - "question": "Nur Importnotizen anzeigen" - } - } - }, "2": { "options": { "0": { @@ -6042,6 +6029,19 @@ "question": "Nur offene Notizen anzeigen" } } + }, + "10": { + "options": { + "0": { + "question": "Alle Notizen" + }, + "1": { + "question": "Importnotizen ausblenden" + }, + "2": { + "question": "Nur Importnotizen anzeigen" + } + } } }, "name": "OpenStreetMap-Hinweise", @@ -6361,21 +6361,6 @@ "1": { "then": "Dies ist ein normaler Stellplatz." }, - "10": { - "then": "Dies ist ein Stellplatz, der für Eltern mit Kindern reserviert ist." - }, - "11": { - "then": "Dies ist ein Stellplatz, der für das Personal reserviert ist." - }, - "12": { - "then": "Dies ist ein Stellplatz, der für Taxis reserviert ist." - }, - "13": { - "then": "Dies ist ein Stellplatz, der für Fahrzeuge mit Anhänger reserviert ist." - }, - "14": { - "then": "Dies ist ein Stellplatz, der für Carsharing reserviert ist." - }, "2": { "then": "Dies ist ein Behindertenstellplatz." }, @@ -6399,6 +6384,21 @@ }, "9": { "then": "Dies ist ein Stellplatz, der für Motorräder reserviert ist." + }, + "10": { + "then": "Dies ist ein Stellplatz, der für Eltern mit Kindern reserviert ist." + }, + "11": { + "then": "Dies ist ein Stellplatz, der für das Personal reserviert ist." + }, + "12": { + "then": "Dies ist ein Stellplatz, der für Taxis reserviert ist." + }, + "13": { + "then": "Dies ist ein Stellplatz, der für Fahrzeuge mit Anhänger reserviert ist." + }, + "14": { + "then": "Dies ist ein Stellplatz, der für Carsharing reserviert ist." } }, "question": "Welche Art von Stellplatz ist dies?" @@ -6990,21 +6990,6 @@ "1": { "then": "2-Cent-Münzen werden akzeptiert" }, - "10": { - "then": "20-Centime-Münzen werden akzeptiert" - }, - "11": { - "then": "½-Schweizer Franken-Münzen werden akzeptiert" - }, - "12": { - "then": "1-Schweizer Franken-Münzen werden akzeptiert" - }, - "13": { - "then": "2-Schweizer Franken-Münzen werden akzeptiert" - }, - "14": { - "then": "5-Schweizer Franken-Münzen werden akzeptiert" - }, "2": { "then": "5-Cent-Münzen werden akzeptiert" }, @@ -7028,6 +7013,21 @@ }, "9": { "then": "10-Centime-Münzen werden akzeptiert" + }, + "10": { + "then": "20-Centime-Münzen werden akzeptiert" + }, + "11": { + "then": "½-Schweizer Franken-Münzen werden akzeptiert" + }, + "12": { + "then": "1-Schweizer Franken-Münzen werden akzeptiert" + }, + "13": { + "then": "2-Schweizer Franken-Münzen werden akzeptiert" + }, + "14": { + "then": "5-Schweizer Franken-Münzen werden akzeptiert" } }, "question": "Mit welchen Münzen kann man hier bezahlen?" @@ -7040,15 +7040,6 @@ "1": { "then": "10-Euro-Scheine werden angenommen" }, - "10": { - "then": "100-Schweizer Franken-Scheine werden akzeptiert" - }, - "11": { - "then": "200-Schweizer Franken-Scheine werden akzeptiert" - }, - "12": { - "then": "1000-Schweizer Franken-Scheine werden akzeptiert" - }, "2": { "then": "20-Euro-Scheine werden angenommen" }, @@ -7072,6 +7063,15 @@ }, "9": { "then": "50-Schweizer Franken-Scheine werden akzeptiert" + }, + "10": { + "then": "100-Schweizer Franken-Scheine werden akzeptiert" + }, + "11": { + "then": "200-Schweizer Franken-Scheine werden akzeptiert" + }, + "12": { + "then": "1000-Schweizer Franken-Scheine werden akzeptiert" } }, "question": "Mit welchen Banknoten kann man hier bezahlen?" @@ -7510,6 +7510,30 @@ "1": { "question": "Recycling von Batterien" }, + "2": { + "question": "Recycling von Getränkekartons" + }, + "3": { + "question": "Recycling von Dosen" + }, + "4": { + "question": "Recycling von Kleidung" + }, + "5": { + "question": "Recycling von Speiseöl" + }, + "6": { + "question": "Recycling von Motoröl" + }, + "7": { + "question": "Recycling von Leuchtstoffröhren" + }, + "8": { + "question": "Recycling von Grünabfällen" + }, + "9": { + "question": "Recycling von Glasflaschen" + }, "10": { "question": "Recycling von Glas" }, @@ -7540,35 +7564,11 @@ "19": { "question": "Recycling von Restabfällen" }, - "2": { - "question": "Recycling von Getränkekartons" - }, "20": { "question": "Recycling von Druckerpatronen" }, "21": { "question": "Recycling von Fahrrädern" - }, - "3": { - "question": "Recycling von Dosen" - }, - "4": { - "question": "Recycling von Kleidung" - }, - "5": { - "question": "Recycling von Speiseöl" - }, - "6": { - "question": "Recycling von Motoröl" - }, - "7": { - "question": "Recycling von Leuchtstoffröhren" - }, - "8": { - "question": "Recycling von Grünabfällen" - }, - "9": { - "question": "Recycling von Glasflaschen" } } }, @@ -7636,6 +7636,30 @@ "1": { "then": "Getränkekartons können hier recycelt werden" }, + "2": { + "then": "Dosen können hier recycelt werden" + }, + "3": { + "then": "Kleidung kann hier recycelt werden" + }, + "4": { + "then": "Speiseöl kann hier recycelt werden" + }, + "5": { + "then": "Motoröl kann hier recycelt werden" + }, + "6": { + "then": "Hier können Leuchtstoffröhren recycelt werden" + }, + "7": { + "then": "Grünabfälle können hier recycelt werden" + }, + "8": { + "then": "Bio-Abfall kann hier recycelt werden" + }, + "9": { + "then": "Glasflaschen können hier recycelt werden" + }, "10": { "then": "Glas kann hier recycelt werden" }, @@ -7666,9 +7690,6 @@ "19": { "then": "Schuhe können hier recycelt werden" }, - "2": { - "then": "Dosen können hier recycelt werden" - }, "20": { "then": "Elektrokleingeräte können hier recycelt werden" }, @@ -7683,27 +7704,6 @@ }, "24": { "then": "Fahrräder können hier recycelt werden" - }, - "3": { - "then": "Kleidung kann hier recycelt werden" - }, - "4": { - "then": "Speiseöl kann hier recycelt werden" - }, - "5": { - "then": "Motoröl kann hier recycelt werden" - }, - "6": { - "then": "Hier können Leuchtstoffröhren recycelt werden" - }, - "7": { - "then": "Grünabfälle können hier recycelt werden" - }, - "8": { - "then": "Bio-Abfall kann hier recycelt werden" - }, - "9": { - "then": "Glasflaschen können hier recycelt werden" } }, "question": "Was kann hier recycelt werden?" @@ -8596,12 +8596,6 @@ "1": { "then": "Diese Straßenlaterne verwendet LEDs" }, - "10": { - "then": "Diese Straßenlaterne verwendet Hochdruck-Natriumdampflampen (orange mit weiß)" - }, - "11": { - "then": "Diese Straßenlaterne wird mit Gas beleuchtet" - }, "2": { "then": "Diese Straßenlaterne verwendet Glühlampenlicht" }, @@ -8625,6 +8619,12 @@ }, "9": { "then": "Diese Straßenlaterne verwendet Niederdruck-Natriumdampflampen (einfarbig orange)" + }, + "10": { + "then": "Diese Straßenlaterne verwendet Hochdruck-Natriumdampflampen (orange mit weiß)" + }, + "11": { + "then": "Diese Straßenlaterne wird mit Gas beleuchtet" } }, "question": "Mit welcher Art von Beleuchtung arbeitet diese Straßenlaterne?" @@ -9828,6 +9828,30 @@ "1": { "question": "Verkauf von Getränken" }, + "2": { + "question": "Verkauf von Süßigkeiten" + }, + "3": { + "question": "Verkauf von Lebensmitteln" + }, + "4": { + "question": "Verkauf von Zigaretten" + }, + "5": { + "question": "Verkauf von Kondomen" + }, + "6": { + "question": "Verkauf von Kaffee" + }, + "7": { + "question": "Verkauf von Trinkwasser" + }, + "8": { + "question": "Verkauf von Zeitungen" + }, + "9": { + "question": "Verkauf von Fahrradschläuchen" + }, "10": { "question": "Verkauf von Milch" }, @@ -9858,9 +9882,6 @@ "19": { "question": "Verkauf von Blumen" }, - "2": { - "question": "Verkauf von Süßigkeiten" - }, "20": { "question": "Verkauf von Parkscheinen" }, @@ -9884,27 +9905,6 @@ }, "27": { "question": "Verkauf von Fahrradschlössern" - }, - "3": { - "question": "Verkauf von Lebensmitteln" - }, - "4": { - "question": "Verkauf von Zigaretten" - }, - "5": { - "question": "Verkauf von Kondomen" - }, - "6": { - "question": "Verkauf von Kaffee" - }, - "7": { - "question": "Verkauf von Trinkwasser" - }, - "8": { - "question": "Verkauf von Zeitungen" - }, - "9": { - "question": "Verkauf von Fahrradschläuchen" } } } @@ -9951,6 +9951,30 @@ "1": { "then": "Süßigkeiten werden verkauft" }, + "2": { + "then": "Lebensmittel werden verkauft" + }, + "3": { + "then": "Zigaretten werden verkauft" + }, + "4": { + "then": "Kondome werden verkauft" + }, + "5": { + "then": "Kaffee wird verkauft" + }, + "6": { + "then": "Trinkwasser wird verkauft" + }, + "7": { + "then": "Zeitungen werden verkauft" + }, + "8": { + "then": "Fahrradschläuche werden verkauft" + }, + "9": { + "then": "Milch wird verkauft" + }, "10": { "then": "Brot wird verkauft" }, @@ -9981,9 +10005,6 @@ "19": { "then": "Parkscheine werden verkauft" }, - "2": { - "then": "Lebensmittel werden verkauft" - }, "20": { "then": "Souvenirmünzen werden verkauft" }, @@ -10004,27 +10025,6 @@ }, "26": { "then": "Fahrradschlösser werden verkauft" - }, - "3": { - "then": "Zigaretten werden verkauft" - }, - "4": { - "then": "Kondome werden verkauft" - }, - "5": { - "then": "Kaffee wird verkauft" - }, - "6": { - "then": "Trinkwasser wird verkauft" - }, - "7": { - "then": "Zeitungen werden verkauft" - }, - "8": { - "then": "Fahrradschläuche werden verkauft" - }, - "9": { - "then": "Milch wird verkauft" } }, "question": "Was wird in diesem Automaten verkauft?", @@ -10316,4 +10316,4 @@ "render": "Windrad" } } -} +} \ No newline at end of file diff --git a/langs/layers/it.json b/langs/layers/it.json index a50419e9d..8652c3e0c 100644 --- a/langs/layers/it.json +++ b/langs/layers/it.json @@ -69,15 +69,6 @@ "1": { "then": "Murale" }, - "10": { - "then": "Azulejo (ornamento decorativo piastrellato spagnolo)" - }, - "11": { - "then": "Mosaico di piastrelle" - }, - "12": { - "then": "Scultura in legno" - }, "2": { "then": "Dipinto" }, @@ -101,6 +92,15 @@ }, "9": { "then": "Rilievo" + }, + "10": { + "then": "Azulejo (ornamento decorativo piastrellato spagnolo)" + }, + "11": { + "then": "Mosaico di piastrelle" + }, + "12": { + "then": "Scultura in legno" } }, "question": "Che tipo di opera d’arte è questo?", @@ -1992,6 +1992,27 @@ "1": { "question": "Riciclo di batterie" }, + "2": { + "question": "Riciclo di confezioni per bevande" + }, + "3": { + "question": "Riciclo di lattine" + }, + "4": { + "question": "Riciclo di abiti" + }, + "5": { + "question": "Riciclo di olio da cucina" + }, + "6": { + "question": "Riciclo di olio da motore" + }, + "8": { + "question": "Riciclo di umido" + }, + "9": { + "question": "Riciclo di bottiglie di vetro" + }, "10": { "question": "Riciclo di vetro" }, @@ -2019,29 +2040,8 @@ "19": { "question": "Riciclo di secco" }, - "2": { - "question": "Riciclo di confezioni per bevande" - }, "20": { "question": "Riciclo di secco" - }, - "3": { - "question": "Riciclo di lattine" - }, - "4": { - "question": "Riciclo di abiti" - }, - "5": { - "question": "Riciclo di olio da cucina" - }, - "6": { - "question": "Riciclo di olio da motore" - }, - "8": { - "question": "Riciclo di umido" - }, - "9": { - "question": "Riciclo di bottiglie di vetro" } } }, @@ -2094,6 +2094,27 @@ "1": { "then": "Cartoni per bevande" }, + "2": { + "then": "Lattine" + }, + "3": { + "then": "Abiti" + }, + "4": { + "then": "Olio da cucina" + }, + "5": { + "then": "Olio di motore" + }, + "7": { + "then": "Verde" + }, + "8": { + "then": "Umido" + }, + "9": { + "then": "Bottiglie di vetro" + }, "10": { "then": "Vetro" }, @@ -2118,9 +2139,6 @@ "19": { "then": "Scarpe" }, - "2": { - "then": "Lattine" - }, "20": { "then": "Piccoli elettrodomestici" }, @@ -2132,24 +2150,6 @@ }, "23": { "then": "Secco" - }, - "3": { - "then": "Abiti" - }, - "4": { - "then": "Olio da cucina" - }, - "5": { - "then": "Olio di motore" - }, - "7": { - "then": "Verde" - }, - "8": { - "then": "Umido" - }, - "9": { - "then": "Bottiglie di vetro" } }, "question": "Cosa si può riciclare qui?" @@ -2950,4 +2950,4 @@ "render": "pala eolica" } } -} +} \ No newline at end of file diff --git a/langs/layers/nl.json b/langs/layers/nl.json index c7045b56b..3d513ebcc 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -27,9 +27,6 @@ "advertising": { "name": "Reclame", "presets": { - "12": { - "title": "een muurschildering" - }, "3": { "description": "Een klein uithangbord voor buurtadvertenties, meestal gericht op voetgangers", "title": "een uithangbord" @@ -50,6 +47,9 @@ "8": { "description": "Een stuk groot, weerbestendig textiel met opgedrukte reclameboodschap die permanent aan de muur hangt", "title": "een spandoek" + }, + "12": { + "title": "een muurschildering" } }, "tagRenderings": { @@ -107,9 +107,6 @@ }, "title": { "mappings": { - "10": { - "then": "Muurschildering" - }, "3": { "then": "Aanplakzuil" }, @@ -127,6 +124,9 @@ }, "9": { "then": "Aanplakzuil" + }, + "10": { + "then": "Muurschildering" } } } @@ -208,15 +208,6 @@ "1": { "then": "Muurschildering" }, - "10": { - "then": "Azulejo (Spaanse siertegels)" - }, - "11": { - "then": "Tegelwerk" - }, - "12": { - "then": "Houtsculptuur" - }, "2": { "then": "Schilderij" }, @@ -240,6 +231,15 @@ }, "9": { "then": "Reliëf" + }, + "10": { + "then": "Azulejo (Spaanse siertegels)" + }, + "11": { + "then": "Tegelwerk" + }, + "12": { + "then": "Houtsculptuur" } }, "question": "Wat voor soort kunstwerk is dit?", @@ -1791,27 +1791,6 @@ "1": { "question": "Heeft een
Schuko stekker zonder aardingspin (CEE7/4 type F)
" }, - "10": { - "question": "Heeft een
Type 2 met kabel (J1772)
" - }, - "11": { - "question": "Heeft een
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "12": { - "question": "Heeft een
Tesla Supercharger (destination)
" - }, - "13": { - "question": "Heeft een
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "14": { - "question": "Heeft een
USB om GSMs en kleine electronica op te laden
" - }, - "15": { - "question": "Heeft een
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "16": { - "question": "Heeft een
Bosch Active Connect met 5 pinnen aan een kabel
" - }, "2": { "question": "Heeft een
Europese stekker met aardingspin (CEE7/4 type E)
" }, @@ -1835,6 +1814,27 @@ }, "9": { "question": "Heeft een
Type 2 CCS (mennekes)
" + }, + "10": { + "question": "Heeft een
Type 2 met kabel (J1772)
" + }, + "11": { + "question": "Heeft een
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "12": { + "question": "Heeft een
Tesla Supercharger (destination)
" + }, + "13": { + "question": "Heeft een
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "14": { + "question": "Heeft een
USB om GSMs en kleine electronica op te laden
" + }, + "15": { + "question": "Heeft een
Bosch Active Connect met 3 pinnen aan een kabel
" + }, + "16": { + "question": "Heeft een
Bosch Active Connect met 5 pinnen aan een kabel
" } } } @@ -1890,6 +1890,30 @@ "1": { "then": "Schuko stekker zonder aardingspin (CEE7/4 type F)" }, + "2": { + "then": "Europese stekker met aardingspin (CEE7/4 type E)" + }, + "3": { + "then": "Europese stekker met aardingspin (CEE7/4 type E)" + }, + "4": { + "then": "Chademo" + }, + "5": { + "then": "Chademo" + }, + "6": { + "then": "Type 1 met kabel (J1772)" + }, + "7": { + "then": "Type 1 met kabel (J1772)" + }, + "8": { + "then": "Type 1 zonder kabel (J1772)" + }, + "9": { + "then": "Type 1 zonder kabel (J1772)" + }, "10": { "then": "Type 1 CCS (ook gekend als Type 1 Combo)" }, @@ -1920,9 +1944,6 @@ "19": { "then": "Type 2 met kabel (J1772)" }, - "2": { - "then": "Europese stekker met aardingspin (CEE7/4 type E)" - }, "20": { "then": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" }, @@ -1953,32 +1974,11 @@ "29": { "then": "Bosch Active Connect met 3 pinnen aan een kabel" }, - "3": { - "then": "Europese stekker met aardingspin (CEE7/4 type E)" - }, "30": { "then": "Bosch Active Connect met 5 pinnen aan een kabel" }, "31": { "then": "Bosch Active Connect met 5 pinnen aan een kabel" - }, - "4": { - "then": "Chademo" - }, - "5": { - "then": "Chademo" - }, - "6": { - "then": "Type 1 met kabel (J1772)" - }, - "7": { - "then": "Type 1 met kabel (J1772)" - }, - "8": { - "then": "Type 1 zonder kabel (J1772)" - }, - "9": { - "then": "Type 1 zonder kabel (J1772)" } }, "question": "Welke aansluitingen zijn hier beschikbaar?" @@ -2172,24 +2172,6 @@ "1": { "2": "Europese stekker met aardingspin (CEE7/4 type E)" }, - "10": { - "2": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" - }, - "11": { - "2": "Tesla Supercharger (destination)" - }, - "12": { - "2": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)" - }, - "13": { - "2": "USB om GSMs en kleine electronica op te laden" - }, - "14": { - "2": "Bosch Active Connect met 3 pinnen aan een kabel" - }, - "15": { - "2": "Bosch Active Connect met 5 pinnen aan een kabel" - }, "2": { "2": "Chademo" }, @@ -2213,6 +2195,24 @@ }, "9": { "2": "Type 2 met kabel (J1772)" + }, + "10": { + "2": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" + }, + "11": { + "2": "Tesla Supercharger (destination)" + }, + "12": { + "2": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)" + }, + "13": { + "2": "USB om GSMs en kleine electronica op te laden" + }, + "14": { + "2": "Bosch Active Connect met 3 pinnen aan een kabel" + }, + "15": { + "2": "Bosch Active Connect met 5 pinnen aan een kabel" } } } @@ -2978,15 +2978,6 @@ "1": { "then": "Dit fietspad is geplaveid" }, - "10": { - "then": "Dit fietspad is gemaakt van fijn grind" - }, - "11": { - "then": "Dit fietspad is gemaakt van kiezelsteentjes" - }, - "12": { - "then": "Dit fietspad is gemaakt van aarde" - }, "2": { "then": "Dit fietspad is gemaakt van asfalt" }, @@ -3010,6 +3001,15 @@ }, "9": { "then": "Dit fietspad is gemaakt van grind" + }, + "10": { + "then": "Dit fietspad is gemaakt van fijn grind" + }, + "11": { + "then": "Dit fietspad is gemaakt van kiezelsteentjes" + }, + "12": { + "then": "Dit fietspad is gemaakt van aarde" } }, "question": "Waaruit is het oppervlak van het fietspad van gemaakt?", @@ -3058,15 +3058,6 @@ "1": { "then": "Dit fietspad is geplaveid" }, - "10": { - "then": "Dit fietspad is gemaakt van fijn grind" - }, - "11": { - "then": "Dit fietspad is gemaakt van kiezelsteentjes" - }, - "12": { - "then": "Dit fietspad is gemaakt van aarde" - }, "2": { "then": "Dit fietspad is gemaakt van asfalt" }, @@ -3090,6 +3081,15 @@ }, "9": { "then": "Dit fietspad is gemaakt van grind" + }, + "10": { + "then": "Dit fietspad is gemaakt van fijn grind" + }, + "11": { + "then": "Dit fietspad is gemaakt van kiezelsteentjes" + }, + "12": { + "then": "Dit fietspad is gemaakt van aarde" } }, "question": "Waaruit is het oppervlak van de straat gemaakt?", @@ -4138,21 +4138,6 @@ "1": { "then": "Dit is een frituur" }, - "10": { - "then": "Dit is een Chinees restaurant" - }, - "11": { - "then": "Dit is een Grieks restaurant" - }, - "12": { - "then": "Dit is een Indisch restaurant" - }, - "13": { - "then": "Dit is een Turks restaurant (dat meer dan enkel kebab verkoopt)" - }, - "14": { - "then": "Dit is een Thaïs restaurant" - }, "2": { "then": "Dit is een pastazaak" }, @@ -4176,6 +4161,21 @@ }, "9": { "then": "Dit is een Frans restaurant" + }, + "10": { + "then": "Dit is een Chinees restaurant" + }, + "11": { + "then": "Dit is een Grieks restaurant" + }, + "12": { + "then": "Dit is een Indisch restaurant" + }, + "13": { + "then": "Dit is een Turks restaurant (dat meer dan enkel kebab verkoopt)" + }, + "14": { + "then": "Dit is een Thaïs restaurant" } }, "question": "Welk soort gerechten worden hier geserveerd?", @@ -5346,19 +5346,6 @@ } } }, - "10": { - "options": { - "0": { - "question": "Alle Notes" - }, - "1": { - "question": "Verberg import Notes" - }, - "2": { - "question": "Toon enkel import Notes" - } - } - }, "2": { "options": { "0": { @@ -5414,6 +5401,19 @@ "question": "Toon enkel open Notes" } } + }, + "10": { + "options": { + "0": { + "question": "Alle Notes" + }, + "1": { + "question": "Verberg import Notes" + }, + "2": { + "question": "Toon enkel import Notes" + } + } } }, "name": "OpenStreetMap Notes", @@ -5705,21 +5705,6 @@ "1": { "then": "Dit is een normale parkeerplek." }, - "10": { - "then": "Deze parkeerplek is gereserveerd voor ouders met kinderen." - }, - "11": { - "then": "Deze parkeerplek is gereserveerd voor personeel." - }, - "12": { - "then": "Deze parkeerplek is gereserveerd voor taxis." - }, - "13": { - "then": "Deze parkeerplek is gereserveerd voor voertuigen met een aanhanger." - }, - "14": { - "then": "Deze parkeerplek is gereserveerd voor autodelen." - }, "2": { "then": "Dit is een gehandicaptenparkeerplaats." }, @@ -5743,6 +5728,21 @@ }, "9": { "then": "Deze parkeerplek is gereserveerd voor motoren." + }, + "10": { + "then": "Deze parkeerplek is gereserveerd voor ouders met kinderen." + }, + "11": { + "then": "Deze parkeerplek is gereserveerd voor personeel." + }, + "12": { + "then": "Deze parkeerplek is gereserveerd voor taxis." + }, + "13": { + "then": "Deze parkeerplek is gereserveerd voor voertuigen met een aanhanger." + }, + "14": { + "then": "Deze parkeerplek is gereserveerd voor autodelen." } }, "question": "Wat voor parkeerplek is dit?" @@ -6309,21 +6309,6 @@ "1": { "then": "Munten van 2 cent worden geaccepteerd" }, - "10": { - "then": "Munten van 20 rappen worden geaccepteerd" - }, - "11": { - "then": "Munten van ½ frank worden geaccepteerd" - }, - "12": { - "then": "Munten van 1 frank worden geaccepteerd" - }, - "13": { - "then": "Munten van 2 frank worden geaccepteerd" - }, - "14": { - "then": "Munten van 5 frank worden geaccepteerd" - }, "2": { "then": "Munten van 5 cent worden geaccepteerd" }, @@ -6347,6 +6332,21 @@ }, "9": { "then": "Munten van 10 rappen worden geaccepteerd" + }, + "10": { + "then": "Munten van 20 rappen worden geaccepteerd" + }, + "11": { + "then": "Munten van ½ frank worden geaccepteerd" + }, + "12": { + "then": "Munten van 1 frank worden geaccepteerd" + }, + "13": { + "then": "Munten van 2 frank worden geaccepteerd" + }, + "14": { + "then": "Munten van 5 frank worden geaccepteerd" } }, "question": "Met welke munten kan je hier betalen?" @@ -6359,15 +6359,6 @@ "1": { "then": "Biljetten van 10 euro worden geaccepteerd" }, - "10": { - "then": "Biljetten van 100 frank worden geaccepteerd" - }, - "11": { - "then": "Biljetten van 200 frank worden geaccepteerd" - }, - "12": { - "then": "Biljetten van 1000 frank worden geaccepteerd" - }, "2": { "then": "Biljetten van 20 euro worden geaccepteerd" }, @@ -6391,6 +6382,15 @@ }, "9": { "then": "Biljetten van 50 frank worden geaccepteerd" + }, + "10": { + "then": "Biljetten van 100 frank worden geaccepteerd" + }, + "11": { + "then": "Biljetten van 200 frank worden geaccepteerd" + }, + "12": { + "then": "Biljetten van 1000 frank worden geaccepteerd" } }, "question": "Met welke bankbiljetten kan je hier betalen?" @@ -6709,6 +6709,30 @@ "1": { "question": "Recycling van batterijen" }, + "2": { + "question": "Recycling van drankpakken" + }, + "3": { + "question": "Recycling van blikken" + }, + "4": { + "question": "Recycling van kleding" + }, + "5": { + "question": "Recycling van frituurvet" + }, + "6": { + "question": "Recycling van motorolie" + }, + "7": { + "question": "Recycling van tl-buizen" + }, + "8": { + "question": "Recycling van groen afval" + }, + "9": { + "question": "Recycling van glazen flessen" + }, "10": { "question": "Recycling van glas" }, @@ -6739,35 +6763,11 @@ "19": { "question": "Recycling van restafval" }, - "2": { - "question": "Recycling van drankpakken" - }, "20": { "question": "Recycling van inktpatronen" }, "21": { "question": "Recycling van fietsen" - }, - "3": { - "question": "Recycling van blikken" - }, - "4": { - "question": "Recycling van kleding" - }, - "5": { - "question": "Recycling van frituurvet" - }, - "6": { - "question": "Recycling van motorolie" - }, - "7": { - "question": "Recycling van tl-buizen" - }, - "8": { - "question": "Recycling van groen afval" - }, - "9": { - "question": "Recycling van glazen flessen" } } }, @@ -6835,6 +6835,30 @@ "1": { "then": "Drankpakken kunnen hier gerecycled worden" }, + "2": { + "then": "Blikken kunnen hier gerecycled worden" + }, + "3": { + "then": "Kleren kunnen hier gerecycled worden" + }, + "4": { + "then": "Frituurvet kan hier gerecycled worden" + }, + "5": { + "then": "Motorolie kan hier gerecycled worden" + }, + "6": { + "then": "TL-buizen kunnen hier gerecycled worden" + }, + "7": { + "then": "Groen afval kan hier gerecycled worden" + }, + "8": { + "then": "Organisch afval kan hier gerecycled worden" + }, + "9": { + "then": "Glazen flessen kunnen hier gerecycled worden" + }, "10": { "then": "Glas kan hier gerecycled worden" }, @@ -6865,9 +6889,6 @@ "19": { "then": "Schoenen kunnen hier gerecycled worden" }, - "2": { - "then": "Blikken kunnen hier gerecycled worden" - }, "20": { "then": "Kleine elektrische apparaten kunnen hier gerecycled worden" }, @@ -6882,27 +6903,6 @@ }, "24": { "then": "Fietsen (en fietswrakken) kunnen hier gerecycled worden" - }, - "3": { - "then": "Kleren kunnen hier gerecycled worden" - }, - "4": { - "then": "Frituurvet kan hier gerecycled worden" - }, - "5": { - "then": "Motorolie kan hier gerecycled worden" - }, - "6": { - "then": "TL-buizen kunnen hier gerecycled worden" - }, - "7": { - "then": "Groen afval kan hier gerecycled worden" - }, - "8": { - "then": "Organisch afval kan hier gerecycled worden" - }, - "9": { - "then": "Glazen flessen kunnen hier gerecycled worden" } }, "question": "Wat kan hier gerecycled worden?" @@ -7624,12 +7624,6 @@ "1": { "then": "Deze lantaarn gebruikt LEDs" }, - "10": { - "then": "Deze lantaarn gebruikt hogedruknatriumlampen (oranje met wit)" - }, - "11": { - "then": "Deze lantaarn wordt verlicht met gas" - }, "2": { "then": "Deze lantaarn gebruikt gloeilampen" }, @@ -7653,6 +7647,12 @@ }, "9": { "then": "Deze lantaarn gebruikt lagedruknatriumlampen (monochroom oranje)" + }, + "10": { + "then": "Deze lantaarn gebruikt hogedruknatriumlampen (oranje met wit)" + }, + "11": { + "then": "Deze lantaarn wordt verlicht met gas" } }, "question": "Wat voor verlichting gebruikt deze lantaarn?" @@ -8796,6 +8796,30 @@ "1": { "question": "Verkoop van dranken" }, + "2": { + "question": "Verkoop van snoep" + }, + "3": { + "question": "Verkoop van eten" + }, + "4": { + "question": "Verkoop van sigaretten" + }, + "5": { + "question": "Verkoop van condooms" + }, + "6": { + "question": "Verkoop van koffie" + }, + "7": { + "question": "Verkoop van water" + }, + "8": { + "question": "Verkoop van kranten" + }, + "9": { + "question": "Verkoop van fietsbinnenbanden" + }, "10": { "question": "Verkoop van melk" }, @@ -8826,9 +8850,6 @@ "19": { "question": "Verkoop van bloemen" }, - "2": { - "question": "Verkoop van snoep" - }, "23": { "question": "Verkoop van fietslampjes" }, @@ -8843,27 +8864,6 @@ }, "27": { "question": "Verkoop van fietssloten" - }, - "3": { - "question": "Verkoop van eten" - }, - "4": { - "question": "Verkoop van sigaretten" - }, - "5": { - "question": "Verkoop van condooms" - }, - "6": { - "question": "Verkoop van koffie" - }, - "7": { - "question": "Verkoop van water" - }, - "8": { - "question": "Verkoop van kranten" - }, - "9": { - "question": "Verkoop van fietsbinnenbanden" } } } @@ -8904,6 +8904,30 @@ "1": { "then": "Snoep wordt verkocht" }, + "2": { + "then": "Eten wordt verkocht" + }, + "3": { + "then": "Sigaretten worden verkocht" + }, + "4": { + "then": "Condooms worden verkocht" + }, + "5": { + "then": "Koffie wordt verkocht" + }, + "6": { + "then": "Drinkwater wordt verkocht" + }, + "7": { + "then": "Kranten worden verkocht" + }, + "8": { + "then": "Binnenbanden voor fietsen worden verkocht" + }, + "9": { + "then": "Melk wordt verkocht" + }, "10": { "then": "Brood wordt verkocht" }, @@ -8934,9 +8958,6 @@ "19": { "then": "Parkeerkaarten worden verkocht" }, - "2": { - "then": "Eten wordt verkocht" - }, "21": { "then": "Openbaar vervoerkaartjes worden verkocht" }, @@ -8954,27 +8975,6 @@ }, "26": { "then": "Fietssloten worden verkocht" - }, - "3": { - "then": "Sigaretten worden verkocht" - }, - "4": { - "then": "Condooms worden verkocht" - }, - "5": { - "then": "Koffie wordt verkocht" - }, - "6": { - "then": "Drinkwater wordt verkocht" - }, - "7": { - "then": "Kranten worden verkocht" - }, - "8": { - "then": "Binnenbanden voor fietsen worden verkocht" - }, - "9": { - "then": "Melk wordt verkocht" } }, "question": "Wat verkoopt deze verkoopautomaat?", @@ -9267,4 +9267,4 @@ "render": "windturbine" } } -} +} \ No newline at end of file diff --git a/langs/themes/de.json b/langs/themes/de.json index 672a46b9c..50b41becd 100644 --- a/langs/themes/de.json +++ b/langs/themes/de.json @@ -918,13 +918,6 @@ } } }, - "10": { - "options": { - "0": { - "question": "Etymologie-Thema ausschließen" - } - } - }, "2": { "options": { "0": { @@ -980,6 +973,13 @@ "question": "GRB-Theme ausschließen" } } + }, + "10": { + "options": { + "0": { + "question": "Etymologie-Thema ausschließen" + } + } } }, "name": "Zentrum der Änderungssätze", @@ -1056,33 +1056,6 @@ "onwheels": { "description": "Auf dieser Karte können Sie öffentlich zugängliche Orte für Rollstuhlfahrer ansehen, bearbeiten oder hinzufügen", "layers": { - "19": { - "override": { - "=title": { - "render": "Statistik" - } - } - }, - "20": { - "override": { - "+tagRenderings": { - "0": { - "render": { - "special": { - "text": "Import" - } - } - }, - "1": { - "render": { - "special": { - "message": "Alle vorgeschlagenen Tags hinzufügen" - } - } - } - } - } - }, "4": { "override": { "filter": { @@ -1125,6 +1098,33 @@ "override": { "name": "Barrierefreie Parkplätze" } + }, + "19": { + "override": { + "=title": { + "render": "Statistik" + } + } + }, + "20": { + "override": { + "+tagRenderings": { + "0": { + "render": { + "special": { + "text": "Import" + } + } + }, + "1": { + "render": { + "special": { + "message": "Alle vorgeschlagenen Tags hinzufügen" + } + } + } + } + } } }, "title": "Auf Rädern" @@ -1285,6 +1285,10 @@ "stations": { "description": "Bahnhofsdetails ansehen, bearbeiten und hinzufügen", "layers": { + "3": { + "description": "Ebene mit Bahnhöfen", + "name": "Bahnhöfe" + }, "16": { "description": "Anzeigen der Züge, die von diesem Bahnhof abfahren", "name": "Abfahrtstafeln", @@ -1316,10 +1320,6 @@ "title": { "render": "Abfahrtstafel" } - }, - "3": { - "description": "Ebene mit Bahnhöfen", - "name": "Bahnhöfe" } }, "title": "Bahnhöfe" @@ -1498,4 +1498,4 @@ "shortDescription": "Eine Karte mit Abfalleimern", "title": "Abfalleimer" } -} +} \ No newline at end of file diff --git a/langs/themes/en.json b/langs/themes/en.json index 477cfc170..391755f29 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -918,13 +918,6 @@ } } }, - "10": { - "options": { - "0": { - "question": "Exclude etymology theme" - } - } - }, "2": { "options": { "0": { @@ -980,6 +973,13 @@ "question": "Exclude GRB theme" } } + }, + "10": { + "options": { + "0": { + "question": "Exclude etymology theme" + } + } } }, "name": "Changeset centers", @@ -1056,33 +1056,6 @@ "onwheels": { "description": "On this map, publicly weelchair accessible places are shown and can be easily added", "layers": { - "19": { - "override": { - "=title": { - "render": "Statistics" - } - } - }, - "20": { - "override": { - "+tagRenderings": { - "0": { - "render": { - "special": { - "text": "Import" - } - } - }, - "1": { - "render": { - "special": { - "message": "Add all the suggested tags" - } - } - } - } - } - }, "4": { "override": { "filter": { @@ -1125,6 +1098,33 @@ "override": { "name": "Disabled parking spaces" } + }, + "19": { + "override": { + "=title": { + "render": "Statistics" + } + } + }, + "20": { + "override": { + "+tagRenderings": { + "0": { + "render": { + "special": { + "text": "Import" + } + } + }, + "1": { + "render": { + "special": { + "message": "Add all the suggested tags" + } + } + } + } + } } }, "title": "OnWheels" @@ -1285,6 +1285,10 @@ "stations": { "description": "View, edit and add details to a train station", "layers": { + "3": { + "description": "Layer showing train stations", + "name": "Train Stations" + }, "16": { "description": "Displays showing the trains that will leave from this station", "name": "Departures boards", @@ -1316,10 +1320,6 @@ "title": { "render": "Departures board" } - }, - "3": { - "description": "Layer showing train stations", - "name": "Train Stations" } }, "title": "Train Stations" @@ -1498,4 +1498,4 @@ "shortDescription": "A map with waste baskets", "title": "Waste Basket" } -} +} \ No newline at end of file diff --git a/langs/themes/es.json b/langs/themes/es.json index e1a6aa357..04f00ebcd 100644 --- a/langs/themes/es.json +++ b/langs/themes/es.json @@ -946,33 +946,6 @@ "onwheels": { "description": "En este mapa se muestran los lugares accesibles al público en silla de ruedas, que pueden añadirse fácilmente", "layers": { - "19": { - "override": { - "=title": { - "render": "Estadísticas" - } - } - }, - "20": { - "override": { - "+tagRenderings": { - "0": { - "render": { - "special": { - "text": "Importar" - } - } - }, - "1": { - "render": { - "special": { - "message": "Añadir todas las etiquetas sugeridas" - } - } - } - } - } - }, "4": { "override": { "filter": { @@ -1015,6 +988,33 @@ "override": { "name": "Plazas de aparcamiento para discapacitados" } + }, + "19": { + "override": { + "=title": { + "render": "Estadísticas" + } + } + }, + "20": { + "override": { + "+tagRenderings": { + "0": { + "render": { + "special": { + "text": "Importar" + } + } + }, + "1": { + "render": { + "special": { + "message": "Añadir todas las etiquetas sugeridas" + } + } + } + } + } } }, "title": "Sobre ruedas" @@ -1175,6 +1175,10 @@ "stations": { "description": "Ver, editar y añadir detalles a una estación de tren", "layers": { + "3": { + "description": "Capa que muestra las estaciones de tren", + "name": "Estación de Tren" + }, "16": { "description": "Pantallas que muestran los trenes que saldrán de esta estación", "name": "Tableros de salidas", @@ -1206,10 +1210,6 @@ "title": { "render": "Tablero de salidas" } - }, - "3": { - "description": "Capa que muestra las estaciones de tren", - "name": "Estación de Tren" } }, "title": "Estaciones de tren" @@ -1331,4 +1331,4 @@ "shortDescription": "Un mapa con papeleras", "title": "Papeleras" } -} +} \ No newline at end of file From 1b5a0a2a853237e9d51298f5e1b17c69afaafa71 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 22:20:22 +0100 Subject: [PATCH 034/195] Themes: add baby icon to changing table question --- assets/layers/toilet/baby.svg | 1 + assets/layers/toilet/baby.svg.license | 2 ++ assets/layers/toilet/license_info.json | 10 ++++++++++ assets/layers/toilet/toilet.json | 3 ++- 4 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 assets/layers/toilet/baby.svg create mode 100644 assets/layers/toilet/baby.svg.license diff --git a/assets/layers/toilet/baby.svg b/assets/layers/toilet/baby.svg new file mode 100644 index 000000000..6a711ce21 --- /dev/null +++ b/assets/layers/toilet/baby.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/layers/toilet/baby.svg.license b/assets/layers/toilet/baby.svg.license new file mode 100644 index 000000000..e02432610 --- /dev/null +++ b/assets/layers/toilet/baby.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Bianca Teixeira +SPDX-License-Identifier: CC0-1.0 \ No newline at end of file diff --git a/assets/layers/toilet/license_info.json b/assets/layers/toilet/license_info.json index 11318c592..af127dea4 100644 --- a/assets/layers/toilet/license_info.json +++ b/assets/layers/toilet/license_info.json @@ -1,4 +1,14 @@ [ + { + "path": "baby.svg", + "license": "CC0-1.0", + "authors": [ + "Bianca Teixeira" + ], + "sources": [ + "https://commons.wikimedia.org/wiki/File:Baby_(75158)_-_The_Noun_Project.svg" + ] + }, { "path": "toilets.svg", "license": "CC0-1.0", diff --git a/assets/layers/toilet/toilet.json b/assets/layers/toilet/toilet.json index d4266476c..463286896 100644 --- a/assets/layers/toilet/toilet.json +++ b/assets/layers/toilet/toilet.json @@ -576,7 +576,8 @@ "ca": "Hi ha un canviador per a nadons", "cs": "Přebalovací pult je k dispozici" }, - "if": "changing_table=yes" + "if": "changing_table=yes", + "icon": "./assets/layers/toilet/baby.svg" }, { "if": "changing_table=no", From 9923f849e09382532399f4dfda249a09fa1d7d3d Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 22:21:35 +0100 Subject: [PATCH 035/195] Hotkeys: add hotkey to quickly switch translation mode on or off --- langs/en.json | 3 ++- src/Models/ThemeConfig/TagRenderingConfig.ts | 3 ++- src/Models/ThemeViewState.ts | 11 +++++++++++ src/UI/Popup/TagRendering/TagRenderingAnswer.svelte | 3 +-- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/langs/en.json b/langs/en.json index 00f75c850..f1a5e5c1f 100644 --- a/langs/en.json +++ b/langs/en.json @@ -500,7 +500,8 @@ "selectOsmbasedmap": "Set the background layer to on OpenStreetMap-based map (or disable the background raster layer)", "selectSearch": "Select the search bar to search locations", "shakePhone": "Shaking your phone", - "title": "Hotkeys" + "title": "Hotkeys", + "translationMode": "Toggle translation mode on or off" }, "image": { "addPicture": "Add picture", diff --git a/src/Models/ThemeConfig/TagRenderingConfig.ts b/src/Models/ThemeConfig/TagRenderingConfig.ts index 8a10a6348..67e3cd808 100644 --- a/src/Models/ThemeConfig/TagRenderingConfig.ts +++ b/src/Models/ThemeConfig/TagRenderingConfig.ts @@ -496,7 +496,8 @@ export default class TagRenderingConfig { for (const leftover of leftovers) { applicableMappings.push({ then: new TypedTranslation( - this.render.replace("{" + this.freeform.key + "}", leftover).translations + this.render.replace("{" + this.freeform.key + "}", leftover).translations, + this.render.context ), }) } diff --git a/src/Models/ThemeViewState.ts b/src/Models/ThemeViewState.ts index 011cf335a..538c98c50 100644 --- a/src/Models/ThemeViewState.ts +++ b/src/Models/ThemeViewState.ts @@ -62,6 +62,7 @@ import FavouritesFeatureSource from "../Logic/FeatureSource/Sources/FavouritesFe import { ProvidedImage } from "../Logic/ImageProviders/ImageProvider" import { GeolocationControlState } from "../UI/BigComponents/GeolocationControl" import Zoomcontrol from "../UI/Zoomcontrol" +import Locale from "../UI/i18n/Locale" /** * @@ -654,6 +655,16 @@ export default class ThemeViewState implements SpecialVisualizationState { ) return true }) + + Hotkeys.RegisterHotkey( + { + shift: "T", + }, + Translations.t.hotkeyDocumentation.translationMode, + () => { + Locale.showLinkToWeblate.setData(!Locale.showLinkToWeblate.data) + } + ) } /** diff --git a/src/UI/Popup/TagRendering/TagRenderingAnswer.svelte b/src/UI/Popup/TagRendering/TagRenderingAnswer.svelte index 12a53e0bd..4a5f64cf5 100644 --- a/src/UI/Popup/TagRendering/TagRenderingAnswer.svelte +++ b/src/UI/Popup/TagRendering/TagRenderingAnswer.svelte @@ -6,7 +6,6 @@ import type { SpecialVisualizationState } from "../../SpecialVisualization" import type { Feature } from "geojson" import { Store, UIEventSource } from "../../../Logic/UIEventSource" - import { onDestroy } from "svelte" import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" import { twMerge } from "tailwind-merge" @@ -24,7 +23,7 @@ throw "Config is undefined in tagRenderingAnswer" } let trs: Store<{ then: Translation; icon?: string; iconClass?: string }[]> = tags.mapD((tags) => - Utils.NoNull(config?.GetRenderValues(tags)) + Utils.NoNull(config?.GetRenderValues(tags)), ) From a50620a8ba4339f4025d95ce8ecbdef856f8625d Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 22:54:17 +0100 Subject: [PATCH 036/195] Refactoring: more cleanup of Svg.ts, remove a few old, unused classes --- src/UI/Base/AsyncLazy.ts | 26 ------------------ src/UI/Base/FilteredCombine.ts | 50 ---------------------------------- 2 files changed, 76 deletions(-) delete mode 100644 src/UI/Base/AsyncLazy.ts delete mode 100644 src/UI/Base/FilteredCombine.ts diff --git a/src/UI/Base/AsyncLazy.ts b/src/UI/Base/AsyncLazy.ts deleted file mode 100644 index 71c6ab34e..000000000 --- a/src/UI/Base/AsyncLazy.ts +++ /dev/null @@ -1,26 +0,0 @@ -import BaseUIElement from "../BaseUIElement" -import { VariableUiElement } from "./VariableUIElement" -import { Stores } from "../../Logic/UIEventSource" -import Loading from "./Loading" - -export default class AsyncLazy extends BaseUIElement { - private readonly _f: () => Promise - - constructor(f: () => Promise) { - super() - this._f = f - } - - protected InnerConstructElement(): HTMLElement { - // The caching of the BaseUIElement will guarantee that _f will only be called once - - return new VariableUiElement( - Stores.FromPromise(this._f()).map((el) => { - if (el === undefined) { - return new Loading() - } - return el - }) - ).ConstructElement() - } -} diff --git a/src/UI/Base/FilteredCombine.ts b/src/UI/Base/FilteredCombine.ts deleted file mode 100644 index 6ed315298..000000000 --- a/src/UI/Base/FilteredCombine.ts +++ /dev/null @@ -1,50 +0,0 @@ -import BaseUIElement from "../BaseUIElement" -import { UIEventSource } from "../../Logic/UIEventSource" -import { VariableUiElement } from "./VariableUIElement" -import Combine from "./Combine" -import Locale from "../i18n/Locale" -import { Utils } from "../../Utils" - -export default class FilteredCombine extends VariableUiElement { - /** - * Only shows item matching the search - * If predicate of an item is undefined, it will be filtered out as soon as a non-null or non-empty search term is given - * @param entries - * @param searchedValue - * @param options - */ - constructor( - entries: { - element: BaseUIElement | string - predicate?: (s: string) => boolean - }[], - searchedValue: UIEventSource, - options?: { - onEmpty?: BaseUIElement | string - innerClasses: string - } - ) { - entries = Utils.NoNull(entries) - super( - searchedValue.map( - (searchTerm) => { - if (searchTerm === undefined || searchTerm === "") { - return new Combine(entries.map((e) => e.element)).SetClass( - options?.innerClasses ?? "" - ) - } - const kept = entries.filter( - (entry) => entry?.predicate !== undefined && entry.predicate(searchTerm) - ) - if (kept.length === 0) { - return options?.onEmpty - } - return new Combine(kept.map((entry) => entry.element)).SetClass( - options?.innerClasses ?? "" - ) - }, - [Locale.language] - ) - ) - } -} From 114b72f6f79c95c5e581d64d79fce42872791dc7 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 23:03:33 +0100 Subject: [PATCH 037/195] Refactoring: more cleanup of Svg.ts, remove a few old, unused classes --- scripts/generateIncludedImages.ts | 1 + .../ImageProviders/WikimediaImageProvider.ts | 1 - src/Models/ThemeConfig/LayerConfig.ts | 5 ++-- src/UI/Base/Loading.svelte | 3 ++- src/UI/Base/SubtleButton.ts | 3 --- src/UI/Popup/Notes/NoteCommentElement.ts | 11 +++++--- src/index.ts | 26 ++++++++++++------- 7 files changed, 30 insertions(+), 20 deletions(-) diff --git a/scripts/generateIncludedImages.ts b/scripts/generateIncludedImages.ts index 0798c7fe3..32fd3d917 100644 --- a/scripts/generateIncludedImages.ts +++ b/scripts/generateIncludedImages.ts @@ -80,6 +80,7 @@ function genImages(dryrun = false) { "ring", "robot", "SocialImageForeground", + "speech_bubble", "speech_bubble_black_outline", "square", "star", diff --git a/src/Logic/ImageProviders/WikimediaImageProvider.ts b/src/Logic/ImageProviders/WikimediaImageProvider.ts index cc0d1dadd..e53f5d9dc 100644 --- a/src/Logic/ImageProviders/WikimediaImageProvider.ts +++ b/src/Logic/ImageProviders/WikimediaImageProvider.ts @@ -1,6 +1,5 @@ import ImageProvider, { ProvidedImage } from "./ImageProvider" import BaseUIElement from "../../UI/BaseUIElement" -import Svg from "../../Svg" import { Utils } from "../../Utils" import { LicenseInfo } from "./LicenseInfo" import Wikimedia from "../Web/Wikimedia" diff --git a/src/Models/ThemeConfig/LayerConfig.ts b/src/Models/ThemeConfig/LayerConfig.ts index 7b2feacd0..caf7d4393 100644 --- a/src/Models/ThemeConfig/LayerConfig.ts +++ b/src/Models/ThemeConfig/LayerConfig.ts @@ -24,11 +24,12 @@ import Table from "../../UI/Base/Table" import FilterConfigJson from "./Json/FilterConfigJson" import { Overpass } from "../../Logic/Osm/Overpass" import { FixedUiElement } from "../../UI/Base/FixedUiElement" -import Svg from "../../Svg" import { ImmutableStore } from "../../Logic/UIEventSource" import { OsmTags } from "../OsmFeature" import Constants from "../Constants" import { QuestionableTagRenderingConfigJson } from "./Json/QuestionableTagRenderingConfigJson" +import SvelteUIElement from "../../UI/Base/SvelteUIElement" +import Statistics from "../../assets/svg/Statistics.svelte" export default class LayerConfig extends WithContextLoader { public static readonly syncSelectionAllowed = ["no", "local", "theme-only", "global"] as const @@ -466,7 +467,7 @@ export default class LayerConfig extends WithContextLoader { new Link( Utils.runningFromConsole ? "" - : Svg.statistics_svg().SetClass("w-4 h-4 mr-2"), + : new SvelteUIElement(Statistics, {class: "w-4 h-4 mr-2"}), "https://taginfo.openstreetmap.org/keys/" + values.key + "#values", true ), diff --git a/src/UI/Base/Loading.svelte b/src/UI/Base/Loading.svelte index 1f592312e..57cdd2adc 100644 --- a/src/UI/Base/Loading.svelte +++ b/src/UI/Base/Loading.svelte @@ -2,13 +2,14 @@ import ToSvelte from "./ToSvelte.svelte" import Svg from "../../Svg" import { twMerge } from "tailwind-merge" + import Loading from "../../assets/svg/Loading.svelte" export let cls: string = undefined
- +
diff --git a/src/UI/Base/SubtleButton.ts b/src/UI/Base/SubtleButton.ts index 02db03ab2..9934e80d1 100644 --- a/src/UI/Base/SubtleButton.ts +++ b/src/UI/Base/SubtleButton.ts @@ -1,9 +1,6 @@ import BaseUIElement from "../BaseUIElement" import { Store, UIEventSource } from "../../Logic/UIEventSource" import { UIElement } from "../UIElement" -import { VariableUiElement } from "./VariableUIElement" -import Lazy from "./Lazy" -import Loading from "./Loading" import SvelteUIElement from "./SvelteUIElement" import SubtleLink from "./SubtleLink.svelte" import Translations from "../i18n/Translations" diff --git a/src/UI/Popup/Notes/NoteCommentElement.ts b/src/UI/Popup/Notes/NoteCommentElement.ts index 5abaa52f9..4dc3c8990 100644 --- a/src/UI/Popup/Notes/NoteCommentElement.ts +++ b/src/UI/Popup/Notes/NoteCommentElement.ts @@ -1,6 +1,5 @@ import Combine from "../../Base/Combine" import BaseUIElement from "../../BaseUIElement" -import Svg from "../../../Svg" import Link from "../../Base/Link" import { FixedUiElement } from "../../Base/FixedUiElement" import Translations from "../../i18n/Translations" @@ -11,6 +10,10 @@ import { Stores, UIEventSource } from "../../../Logic/UIEventSource" import { OsmConnection } from "../../../Logic/Osm/OsmConnection" import { VariableUiElement } from "../../Base/VariableUIElement" import { SpecialVisualizationState } from "../../SpecialVisualization" +import SvelteUIElement from "../../Base/SvelteUIElement" +import Note from "../../../assets/svg/Note.svelte" +import Resolved from "../../../assets/svg/Resolved.svelte" +import Speech_bubble from "../../../assets/svg/Speech_bubble.svelte" export default class NoteCommentElement extends Combine { constructor( @@ -32,11 +35,11 @@ export default class NoteCommentElement extends Combine { let actionIcon: BaseUIElement if (comment.action === "opened" || comment.action === "reopened") { - actionIcon = Svg.note_svg() + actionIcon = new SvelteUIElement(Note) } else if (comment.action === "closed") { - actionIcon = Svg.resolved_svg() + actionIcon = new SvelteUIElement(Resolved) } else { - actionIcon = Svg.speech_bubble_svg() + actionIcon = new SvelteUIElement(Speech_bubble) } let user: BaseUIElement diff --git a/src/index.ts b/src/index.ts index 2a5b2600c..3d2e6cd3a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,11 +5,12 @@ import ThemeViewGUI from "./UI/ThemeViewGUI.svelte" import { FixedUiElement } from "./UI/Base/FixedUiElement" import Combine from "./UI/Base/Combine" import { SubtleButton } from "./UI/Base/SubtleButton" -import Svg from "./Svg" import { Utils } from "./Utils" +import Download from "./assets/svg/Download.svelte" + function webgl_support() { try { - var canvas = document.createElement("canvas") + const canvas = document.createElement("canvas") return ( !!window.WebGLRenderingContext && (canvas.getContext("webgl") || canvas.getContext("experimental-webgl")) @@ -18,6 +19,7 @@ function webgl_support() { return false } } + // @ts-ignore try { if (!webgl_support()) { @@ -31,16 +33,22 @@ try { }) .catch((err) => { console.error("Error while initializing: ", err, err.stack) + const customDefinition = DetermineLayout.getCustomDefinition() new Combine([ new FixedUiElement(err).SetClass("block alert"), - new SubtleButton(Svg.download_svg(), "Download the raw file").onClick(() => - Utils.offerContentsAsDownloadableFile( - DetermineLayout.getCustomDefinition(), - "mapcomplete-theme.json", - { mimetype: "application/json" } - ) - ), + customDefinition?.length > 0 + ? new SubtleButton( + new SvelteUIElement(Download), + "Download the raw file" + ).onClick(() => + Utils.offerContentsAsDownloadableFile( + DetermineLayout.getCustomDefinition(), + "mapcomplete-theme.json", + { mimetype: "application/json" } + ) + ) + : undefined, ]).AttachTo("maindiv") }) } catch (err) { From cdb1bb23b8e5df8bd1f2e0732ff14f3bd55935f5 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 23:08:29 +0100 Subject: [PATCH 038/195] Fix: use correct relocation icon again --- src/UI/Popup/MoveWizardState.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/UI/Popup/MoveWizardState.ts b/src/UI/Popup/MoveWizardState.ts index 709638660..a4faa6085 100644 --- a/src/UI/Popup/MoveWizardState.ts +++ b/src/UI/Popup/MoveWizardState.ts @@ -12,6 +12,7 @@ import { Feature, Point } from "geojson" import SvelteUIElement from "../Base/SvelteUIElement" import Confirm from "../../assets/svg/Confirm.svelte" import Relocation from "../../assets/svg/Relocation.svelte" +import Location from "../../assets/svg/Location.svelte" export interface MoveReason { text: Translation | string @@ -62,7 +63,7 @@ export class MoveWizardState { reasons.push({ text: t.reasons.reasonInaccurate, invitingText: t.inviteToMove.reasonInaccurate, - icon: new SvelteUIElement(Confirm), + icon: new SvelteUIElement(Location), changesetCommentValue: "improve_accuracy", lockBounds: true, includeSearch: false, From 5136d20477b81534658125c9d69f14ab3cad2a42 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 23:10:47 +0100 Subject: [PATCH 039/195] Remove debugging output --- src/UI/Zoomcontrol.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/UI/Zoomcontrol.ts b/src/UI/Zoomcontrol.ts index 5d3687ecd..898cd7d35 100644 --- a/src/UI/Zoomcontrol.ts +++ b/src/UI/Zoomcontrol.ts @@ -29,9 +29,6 @@ export default class Zoomcontrol { this._allowZooming.addCallback((allowed) => { this.apply(allowed ? Zoomcontrol.initialValue : Zoomcontrol.noZoom) }) - Stores.Chronic(1000).addCallback((_) => - console.log(this.viewportElement.getAttribute("content")) - ) } private _resetZoom() { From 286fe729359db1ec36328768a6a7662a9e951ce8 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 23:55:18 +0100 Subject: [PATCH 040/195] Fix: allow to link multiple mapillary-images --- package.json | 2 +- src/Logic/ImageProviders/Mapillary.ts | 1 - src/Logic/Osm/Actions/LinkImageAction.ts | 3 +-- src/Logic/Osm/Changes.ts | 2 +- src/UI/Image/LinkableImage.svelte | 15 +++++++-------- src/UI/Image/NearbyImages.svelte | 2 +- 6 files changed, 11 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 71f0b38be..8eaa8e838 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mapcomplete", - "version": "0.36.12", + "version": "0.36.13", "repository": "https://github.com/pietervdvn/MapComplete", "description": "A small website to edit OSM easily", "bugs": "https://github.com/pietervdvn/MapComplete/issues", diff --git a/src/Logic/ImageProviders/Mapillary.ts b/src/Logic/ImageProviders/Mapillary.ts index 85f1bd1ac..a508263fe 100644 --- a/src/Logic/ImageProviders/Mapillary.ts +++ b/src/Logic/ImageProviders/Mapillary.ts @@ -155,7 +155,6 @@ export class Mapillary extends ImageProvider { Constants.mapillary_client_token_v4 const response = await Utils.downloadJsonCached(metadataUrl, 60 * 60) const url = response["thumb_1024_url"] - console.log(response) const url_hd = response["thumb_original_url"] return { id: "" + mapillaryId, diff --git a/src/Logic/Osm/Actions/LinkImageAction.ts b/src/Logic/Osm/Actions/LinkImageAction.ts index 908ce1583..9755fa724 100644 --- a/src/Logic/Osm/Actions/LinkImageAction.ts +++ b/src/Logic/Osm/Actions/LinkImageAction.ts @@ -1,7 +1,6 @@ import ChangeTagAction from "./ChangeTagAction" import { Tag } from "../../Tags/Tag" import OsmChangeAction from "./OsmChangeAction" -import { Changes } from "../Changes" import { ChangeDescription } from "./ChangeDescription" import { Store } from "../../UIEventSource" @@ -40,7 +39,7 @@ export default class LinkImageAction extends OsmChangeAction { protected CreateChangeDescriptions(): Promise { let key = this._proposedKey let i = 0 - const currentTags = this._currentTags.data + const currentTags: Record = this._currentTags.data const url = this._url while (currentTags[key] !== undefined && currentTags[key] !== url) { key = this._proposedKey + ":" + i diff --git a/src/Logic/Osm/Changes.ts b/src/Logic/Osm/Changes.ts index 935bb80b3..3f2611edb 100644 --- a/src/Logic/Osm/Changes.ts +++ b/src/Logic/Osm/Changes.ts @@ -539,7 +539,7 @@ export class Changes { openChangeset ) - console.log("Upload successfull!") + console.log("Upload successful!") return true } diff --git a/src/UI/Image/LinkableImage.svelte b/src/UI/Image/LinkableImage.svelte index 572cc6776..ea692b083 100644 --- a/src/UI/Image/LinkableImage.svelte +++ b/src/UI/Image/LinkableImage.svelte @@ -15,18 +15,15 @@ import SpecialTranslation from "../Popup/TagRendering/SpecialTranslation.svelte" export let tags: UIEventSource - export let lon: number - export let lat: number export let state: SpecialVisualizationState export let image: P4CPicture export let feature: Feature export let layer: LayerConfig export let linkable = true - let isLinked = Object.values(tags.data).some((v) => image.pictureUrl === v) - + let targetValue = Object.values(image.osmTags)[0] + let isLinked = new UIEventSource(Object.values(tags.data).some((v) => targetValue === v)) const t = Translations.t.image.nearby - const c = [lon, lat] const providedImage: ProvidedImage = { url: image.thumbUrl ?? image.pictureUrl, url_hd: image.pictureUrl, @@ -36,10 +33,11 @@ id: Object.values(image.osmTags)[0], } - $: { + function applyLink(isLinked :boolean) { + console.log("Applying linked image", isLinked, targetValue) const currentTags = tags.data const key = Object.keys(image.osmTags)[0] - const url = image.osmTags[key] + const url = targetValue if (isLinked) { const action = new LinkImageAction(currentTags.id, key, url, tags, { theme: tags.data._orig_theme ?? state.layout.id, @@ -59,6 +57,7 @@ } } } + isLinked.addCallback(isLinked => applyLink(isLinked))
@@ -71,7 +70,7 @@
{#if linkable} {/if} diff --git a/src/UI/Image/NearbyImages.svelte b/src/UI/Image/NearbyImages.svelte index 375b1b449..11e5c4d0c 100644 --- a/src/UI/Image/NearbyImages.svelte +++ b/src/UI/Image/NearbyImages.svelte @@ -55,7 +55,7 @@
{#each $images as image (image.pictureUrl)} - + {/each}
From 5a48a2e19cdd6449756b982a2b92f433869852db Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Wed, 17 Jan 2024 16:30:02 +0100 Subject: [PATCH 041/195] More improvements to velopark --- assets/svg/license_info.json | 6 ++++++ assets/svg/square_rounded.svg | 22 ++++++++++++++++++++++ assets/svg/square_rounded.svg.license | 2 ++ 3 files changed, 30 insertions(+) create mode 100644 assets/svg/square_rounded.svg create mode 100644 assets/svg/square_rounded.svg.license diff --git a/assets/svg/license_info.json b/assets/svg/license_info.json index 80f667176..e93a3469f 100644 --- a/assets/svg/license_info.json +++ b/assets/svg/license_info.json @@ -1171,6 +1171,12 @@ "authors": [], "sources": [] }, + { + "path": "square_rounded.svg", + "license": "TRIVIAL", + "authors": [], + "sources": [] + }, { "path": "star.svg", "license": "TRIVIAL", diff --git a/assets/svg/square_rounded.svg b/assets/svg/square_rounded.svg new file mode 100644 index 000000000..4009ed00b --- /dev/null +++ b/assets/svg/square_rounded.svg @@ -0,0 +1,22 @@ + + + + + diff --git a/assets/svg/square_rounded.svg.license b/assets/svg/square_rounded.svg.license new file mode 100644 index 000000000..22b8ca7a5 --- /dev/null +++ b/assets/svg/square_rounded.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: +SPDX-License-Identifier: LicenseRef-TRIVIAL \ No newline at end of file From 8bcc8820acf8cc6d3e311f3ee53d04db2b2df187 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Wed, 17 Jan 2024 18:08:14 +0100 Subject: [PATCH 042/195] Furhter improvements to velopark: better icons, improvements to loading --- assets/themes/velopark/velopark.json | 293 ++++++++++-------- scripts/generateIncludedImages.ts | 1 + scripts/velopark/veloParkToGeojson.ts | 49 +-- src/Logic/Web/VeloparkLoader.ts | 234 +++++++++----- src/Models/Constants.ts | 4 +- src/UI/Image/NearbyImages.svelte | 2 +- src/UI/Map/Icon.svelte | 91 +++--- .../MapRoulette/MaprouletteSetStatus.svelte | 2 +- src/UI/SpecialVisualizations.ts | 261 +++++++++------- src/assets/svg/Square_rounded.svelte | 4 + 10 files changed, 560 insertions(+), 381 deletions(-) create mode 100644 src/assets/svg/Square_rounded.svelte diff --git a/assets/themes/velopark/velopark.json b/assets/themes/velopark/velopark.json index 7050dd042..be17bb702 100644 --- a/assets/themes/velopark/velopark.json +++ b/assets/themes/velopark/velopark.json @@ -25,135 +25,186 @@ "defaultBackgroundId": "photo", "layers": [ { - "builtin": "maproulette_challenge", - "override": { - "=name": { - "en": "Velopark data", - "nl": "Velopark data" - }, - "=filter": [ - { - "id": "created-only", - "options": [ - { - "question": { - "en": "Only unfinished tasks", - "nl": "Enkel onafgewerkte taken" - }, - "osmTags": "mr_taskStatus=Created", - "default": true - } - ] + "id": "velopark_maproulette", + "description": "Maproulette challenge containing velopark data", + "source": { + "osmTags": "mr_taskId~*", + "geoJson": "https://maproulette.org/api/v2/challenge/view/43282", + "isOsmCache": false + }, + "title": { + "render": "Velopark parking {mr_velopark_id}" + }, + "name": { + "en": "Velopark data", + "nl": "Velopark data" + }, + "titleIcons": [ + { + "id": "maproulette", + "render": "" + } + ], + "tagRenderings": [ + { + "id": "velopark-id-display", + "render": { + "*": "{ref:velopark}" } - ], - "calculatedTags+": [ - "mr_velopark_id=feat.properties['ref:velopark']?.split('/')?.at(-1)", - "_nearby_bicycle_parkings=closestn(feat)(['bike_parking','bike_parking_with_velopark_ref'], 100, undefined, 25)", - "_nearby_bicycle_parkings:count=get(feat)('_nearby_bicycle_parkings').length", - "_nearby_bicycle_parkings:props=get(feat)('_nearby_bicycle_parkings').map(f => ({_distance: Math.round(f.distance), _ref: feat.properties['ref:velopark'], _mr_id: feat.properties.id, '_velopark:id': (f.feat.properties['_velopark:id'] ?? 'unlinked') /*Explicit copy to trigger lazy loading*/, ...f.feat.properties}))" - ], - "=title": { - "render": "Velopark parking {mr_velopark_id}" }, - "source": { - "geoJson": "https://maproulette.org/api/v2/challenge/view/43282" + { + "id": "velopark-link", + "render": { + "special": { + "type": "link", + "href": "https://www.velopark.be/static/data/{mr_velopark_id}", + "text": { + "en": "See on velopark (webpage)", + "nl": "Bekijk op Velopark (webpagina)" + } + } + } }, - "=tagRenderings": [ - { - "id": "velopark-link", - "render": { - "special": { - "type": "link", - "href": "https://www.velopark.be/static/data/{mr_velopark_id}", - "text": { - "en": "See on velopark (webpage)", - "nl": "Bekijk op Velopark (webpagina)" - } + { + "id": "velopark-data-link", + "render": { + "special": { + "type": "link", + "href": "{ref:velopark}", + "text": "Inspect raw data on velopark.be" + } + } + }, + { + "id": "show-data-velopark", + "render": { + "special": { + "type": "compare_data", + "url": "ref:velopark", + "host": "https://data.velopark.be", + "postprocessing": "velopark", + "readonly": "yes" + } + } + }, + { + "id": "closest_parkings", + "render": { + "*": "There are {_nearby_bicycle_parkings:count} bicycle parkings within 25m known in OpenStreetMap. " + } + }, + { + "id": "list_nearby_bike_parkings", + "render": { + "special": { + "type": "multi", + "key": "_nearby_bicycle_parkings:props", + "tagrendering": "{id} ({_distance}m, {_velopark:id}) {minimap(20)} {tag_apply(ref:velopark=$_ref,Link,link,id,_mr_id)}" + } + } + }, + { + "id": "import_point", + "render": { + "special": { + "type": "import_button", + "targetLayer": "bike_parking_with_velopark_ref bike_parking", + "tags": "amenity=bicycle_parking;ref:velopark=$ref:velopark", + "text": { + "en": "Create a new bicycle parking in OSM", + "nl": "Maak een nieuwe parking aan in OSM" + }, + "maproulette_id": "mr_taskId" + } + } + }, + { + "id": "close_mr", + "render": { + "special": { + "type": "maproulette_set_status", + "message": { + "en": "Mark this item as linked", + "nl": "Markeer als gelinkt" } } - }, - { - "id": "velopark-data-link", - "render": { - "special": { - "type": "link", - "href": "{ref:velopark}", - "text": "Inspect raw data on velopark.be" - } + } + }, + { + "id": "close_mr_incorrect", + "render": { + "special": { + "type": "maproulette_set_status", + "message": { + "en": "Mark this item as incorrect (duplicate, does not exist anymore, contradictory data)", + "nl": "Markeer dit object als incorrect (duplicaatin, incorrect of tegenstrijdige data, ...)" + }, + "image": "invalid", + "status": 6 } - }, - { - "id": "show-data-velopark", - "render": { - "special": { - "type": "compare_data", - "url": "ref:velopark", - "host": "https://data.velopark.be", - "postprocessing": "velopark", - "readonly": "yes" - } + } + }, + "{nearby_images(open,readonly)}" + ], + "lineRendering": [], + "filter": [ + { + "id": "created-only", + "options": [ + { + "question": { + "en": "Only unfinished tasks", + "nl": "Enkel onafgewerkte taken" + }, + "osmTags": "mr_taskStatus=Created", + "default": true } - }, - { - "id": "closest_parkings", - "render": { - "*": "There are {_nearby_bicycle_parkings:count} bicycle parkings within 25m known in OpenStreetMap. " + ] + }, + { + "id": "too-hard-only", + "options": [ + { + "question": { + "en": "Only too-hard tasks", + "nl": "Enkel foutieve taken" + }, + "osmTags": "mr_taskStatus=Too_hard" } + ] + } + ], + "calculatedTags": [ + "mr_velopark_id=feat.properties['ref:velopark']?.split('/')?.at(-1)", + "_nearby_bicycle_parkings=closestn(feat)(['bike_parking','bike_parking_with_velopark_ref'], 100, undefined, 25)", + "_nearby_bicycle_parkings:count=get(feat)('_nearby_bicycle_parkings').length", + "_nearby_bicycle_parkings:props=get(feat)('_nearby_bicycle_parkings').map(f => ({_distance: Math.round(f.distance), _ref: feat.properties['ref:velopark'], _mr_id: feat.properties.id, '_velopark:id': (f.feat.properties['_velopark:id'] ?? 'unlinked') /*Explicit copy to trigger lazy loading*/, ...f.feat.properties}))" + ], + "pointRendering": [ + { + "location": [ + "point", + "centroid" + ], + "marker": [ + { + "icon": "square_rounded", + "color": "#ffffff88" + }, + { + "icon": "./assets/themes/velopark/velopark.svg" } - }, - { - "id": "list_nearby_bike_parkings", - "render": { - "special": { - "type": "multi", - "key": "_nearby_bicycle_parkings:props", - "tagrendering": "{id} ({_distance}m, {_velopark:id}) {minimap(20)} {tag_apply(ref:velopark=$_ref,Link,link,id,_mr_id)}" - } - } - }, - { - "id": "import_point", - "render": { - "special": { - "type": "import_button", - "targetLayer": "bike_parking_with_velopark_ref bike_parking", - "tags": "amenity=bicycle_parking;ref:velopark=$ref:velopark", - "text": { - "en": "Create a new bicycle parking in OSM", - "nl": "Maak een nieuwe parking aan in OSM" - }, - "maproulette_id": "mr_taskId" - } - } - }, - { - "id": "close_mr", - "render": { - "special": { - "type": "maproulette_set_status", - "message": { - "en": "Mark this item as linked", - "nl": "Markeer als gelinkt" - } - } - } - }, - { - "id": "close_mr_incorrect", - "render": { - "special": { - "type": "maproulette_set_status", - "message": { - "en": "Mark this item as incorrect (duplicate, does not exist anymore, contradictory data)", - "nl": "Markeer dit object als incorrect (duplicaatin, incorrect of tegenstrijdige data, ...)" - }, - "image": "bug", - "status": 6 - } - } - }, - "{nearby_images()}" - ] - } + ], + "iconSize": "40,40", + "anchor": "bottom", + "iconBadges": [{ + "if": "mr_taskStatus=Too_Hard", + "then": "invalid" + },{ + "if": "mr_taskStatus=Fixed", + "then": "confirm" + }] + } + ] }, { "builtin": [ diff --git a/scripts/generateIncludedImages.ts b/scripts/generateIncludedImages.ts index 0798c7fe3..08044203b 100644 --- a/scripts/generateIncludedImages.ts +++ b/scripts/generateIncludedImages.ts @@ -82,6 +82,7 @@ function genImages(dryrun = false) { "SocialImageForeground", "speech_bubble_black_outline", "square", + "square_rounded", "star", "star_half", "star_outline", diff --git a/scripts/velopark/veloParkToGeojson.ts b/scripts/velopark/veloParkToGeojson.ts index a3226cf39..0157e9b1f 100644 --- a/scripts/velopark/veloParkToGeojson.ts +++ b/scripts/velopark/veloParkToGeojson.ts @@ -2,15 +2,17 @@ import Script from "../Script" import { Utils } from "../../src/Utils" import VeloparkLoader, { VeloparkData } from "../../src/Logic/Web/VeloparkLoader" import fs from "fs" -import OverpassFeatureSource from "../../src/Logic/FeatureSource/Sources/OverpassFeatureSource" import { Overpass } from "../../src/Logic/Osm/Overpass" import { RegexTag } from "../../src/Logic/Tags/RegexTag" import Constants from "../../src/Models/Constants" import { ImmutableStore } from "../../src/Logic/UIEventSource" import { BBox } from "../../src/Logic/BBox" + class VeloParkToGeojson extends Script { constructor() { - super("Downloads the latest Velopark data and converts it to a geojson, which will be saved at the current directory") + super( + "Downloads the latest Velopark data and converts it to a geojson, which will be saved at the current directory" + ) } async main(args: string[]): Promise { @@ -19,37 +21,48 @@ class VeloParkToGeojson extends Script { const url = "https://www.velopark.be/api/parkings/1000" const data = await Utils.downloadJson(url) - const bboxBelgium = new BBox([[2.51357303225, 49.5294835476],[ 6.15665815596, 51.4750237087]]) - const alreadyLinkedQuery = new Overpass(new RegexTag("ref:velopark", /.+/), + const bboxBelgium = new BBox([ + [2.51357303225, 49.5294835476], + [6.15665815596, 51.4750237087], + ]) + const alreadyLinkedQuery = new Overpass( + new RegexTag("ref:velopark", /.+/), [], Constants.defaultOverpassUrls[0], - new ImmutableStore(60*5), + new ImmutableStore(60 * 5), false - ) + ) const alreadyLinkedFeatures = await alreadyLinkedQuery.queryGeoJson(bboxBelgium) - const seenIds = new Set(alreadyLinkedFeatures[0].features.map(f => f.properties["ref:velopark"])) - const features = data.map(f => VeloparkLoader.convert(f)) - .filter(f => !seenIds.has(f.properties["ref:velopark"])) + const seenIds = new Set( + alreadyLinkedFeatures[0].features.map((f) => f.properties["ref:velopark"]) + ) + console.log("OpenStreetMap contains", seenIds.size, "bicycle parkings with a velopark ref") + const allVelopark = data.map((f) => VeloparkLoader.convert(f)) + const features = allVelopark.filter((f) => !seenIds.has(f.properties["ref:velopark"])) const allProperties = new Set() for (const feature of features) { - Object.keys(feature.properties).forEach(k => allProperties.add(k)) + Object.keys(feature.properties).forEach((k) => allProperties.add(k)) } allProperties.delete("ref:velopark") for (const feature of features) { - allProperties.forEach(k => { + allProperties.forEach((k) => { delete feature.properties[k] }) } - fs.writeFileSync("velopark_id_only_export_" + new Date().toISOString() + ".geojson", JSON.stringify({ - "type": "FeatureCollection", - features, - }, null, " ")) - + fs.writeFileSync( + "velopark_id_only_export_" + new Date().toISOString() + ".geojson", + JSON.stringify( + { + type: "FeatureCollection", + features, + }, + null, + " " + ) + ) } - - } new VeloParkToGeojson().run() diff --git a/src/Logic/Web/VeloparkLoader.ts b/src/Logic/Web/VeloparkLoader.ts index 1101aaaaf..4b0cf8ef0 100644 --- a/src/Logic/Web/VeloparkLoader.ts +++ b/src/Logic/Web/VeloparkLoader.ts @@ -1,4 +1,4 @@ -import { Feature, Geometry, Point } from "geojson" +import { Feature, Geometry } from "geojson" import { OH } from "../../UI/OpeningHours/OpeningHours" import EmailValidator from "../../UI/InputElement/Validators/EmailValidator" import PhoneValidator from "../../UI/InputElement/Validators/PhoneValidator" @@ -12,39 +12,49 @@ import { Utils } from "../../Utils" * Reads a velopark-json, converts it to a geojson */ export default class VeloparkLoader { - private static readonly emailReformatting = new EmailValidator() private static readonly phoneValidator = new PhoneValidator() private static readonly coder = new CountryCoder( Constants.countryCoderEndpoint, - Utils.downloadJson, + Utils.downloadJson ) public static convert(veloparkData: VeloparkData): Feature { - + console.log("Converting", veloparkData) const properties: { - "ref:velopark":string, - "operator:email"?: string, - "operator:phone"?: string, - fee?: string, + "ref:velopark": string + "operator:email"?: string + "operator:phone"?: string + fee?: string opening_hours?: string access?: string maxstay?: string operator?: string } = { - "ref:velopark": veloparkData["id"] ?? veloparkData["@id"] + "ref:velopark": veloparkData["id"] ?? veloparkData["@id"], } + for (const k of ["_id", "url", "dateModified", "name", "address"]) { + delete veloparkData[k] + } + + VeloparkLoader.cleanup(veloparkData["properties"]) + VeloparkLoader.cleanupEmtpy(veloparkData) + properties.operator = veloparkData.operatedBy?.companyName if (veloparkData.contactPoint?.email) { - properties["operator:email"] = VeloparkLoader.emailReformatting.reformat(veloparkData.contactPoint?.email) + properties["operator:email"] = VeloparkLoader.emailReformatting.reformat( + veloparkData.contactPoint?.email + ) } - if (veloparkData.contactPoint?.telephone) { - properties["operator:phone"] = VeloparkLoader.phoneValidator.reformat(veloparkData.contactPoint?.telephone, () => "be") + properties["operator:phone"] = VeloparkLoader.phoneValidator.reformat( + veloparkData.contactPoint?.telephone, + () => "be" + ) } veloparkData.photos?.forEach((p, i) => { @@ -52,130 +62,198 @@ export default class VeloparkLoader { properties["image"] = p.image } else { properties["image:" + i] = p.image - } }) let geometry = veloparkData.geometry for (const g of veloparkData["@graph"]) { + VeloparkLoader.cleanup(g) + VeloparkLoader.cleanupEmtpy(g) if (g.geo[0]) { geometry = { type: "Point", coordinates: [g.geo[0].longitude, g.geo[0].latitude] } } - if (g.maximumParkingDuration?.endsWith("D") && g.maximumParkingDuration?.startsWith("P")) { - const duration = g.maximumParkingDuration.substring(1, g.maximumParkingDuration.length - 1) + if ( + g.maximumParkingDuration?.endsWith("D") && + g.maximumParkingDuration?.startsWith("P") + ) { + const duration = g.maximumParkingDuration.substring( + 1, + g.maximumParkingDuration.length - 1 + ) properties.maxstay = duration + " days" } properties.access = g.publicAccess ? "yes" : "no" const prefix = "http://schema.org/" if (g.openingHoursSpecification) { - const oh = OH.simplify(g.openingHoursSpecification.map(spec => { - const dayOfWeek = spec.dayOfWeek.substring(prefix.length, prefix.length + 2).toLowerCase() - const startHour = spec.opens - const endHour = spec.closes === "23:59" ? "24:00" : spec.closes - const merged = OH.MergeTimes(OH.ParseRule(dayOfWeek + " " + startHour + "-" + endHour)) - return OH.ToString(merged) - }).join("; ")) + const oh = OH.simplify( + g.openingHoursSpecification + .map((spec) => { + const dayOfWeek = spec.dayOfWeek + .substring(prefix.length, prefix.length + 2) + .toLowerCase() + const startHour = spec.opens + const endHour = spec.closes === "23:59" ? "24:00" : spec.closes + const merged = OH.MergeTimes( + OH.ParseRule(dayOfWeek + " " + startHour + "-" + endHour) + ) + return OH.ToString(merged) + }) + .join("; ") + ) properties.opening_hours = oh } if (g.priceSpecification?.[0]) { properties.fee = g.priceSpecification[0].freeOfCharge ? "no" : "yes" } + const types = { + "https://data.velopark.be/openvelopark/terms#RegularBicycle": "_", + "https://data.velopark.be/openvelopark/terms#ElectricBicycle": + "capacity:electric_bicycle", + "https://data.velopark.be/openvelopark/terms#CargoBicycle": "capacity:cargo_bike", + } + let totalCapacity = 0 + for (let i = (g.allows ?? []).length - 1; i >= 0; i--) { + const capacity = g.allows[i] + const type: string = capacity["@type"] + if (type === undefined) { + console.warn("No type found for", capacity.bicycleType) + continue + } + const count = capacity["amount"] + if (!isNaN(count)) { + totalCapacity += Number(count) + } else { + console.warn("Not a valid number while loading velopark data:", count) + } + if (type !== "_") { + // properties[type] = count + } + g.allows.splice(i, 1) + } + if (totalCapacity > 0) { + properties["capacity"] = totalCapacity + } } + console.log(JSON.stringify(properties, null, " ")) + return { type: "Feature", properties, geometry } } + private static cleanup(data: any) { + if (!data?.attributes) { + return + } + for (const k of ["NIS_CODE", "name_NL", "name_DE", "name_EN", "name_FR"]) { + delete data.attributes[k] + } + VeloparkLoader.cleanupEmtpy(data) + } + + private static cleanupEmtpy(data: any) { + for (const key in data) { + if (data[key] === null) { + delete data[key] + continue + } + if (Object.keys(data[key]).length === 0) { + delete data[key] + } + } + } } export interface VeloparkData { geometry?: Geometry - "@context": any, + "@context": any "@id": string // "https://data.velopark.be/data/NMBS_541", - "@type": "BicycleParkingStation", - "dateModified": string, - "identifier": number, - "name": [ + "@type": "BicycleParkingStation" + dateModified: string + identifier: number + name: [ { - "@value": string, + "@value": string "@language": "nl" } - ], - "ownedBy": { - "@id": string, - "@type": "BusinessEntity", - "companyName": string - }, - "operatedBy": { - "@type": "BusinessEntity", - "companyName": string - }, - "address": any, - "hasMap": any, - "contactPoint": { - "@type": "ContactPoint", - "email": string, - "telephone": string - }, - "photos": { - "@type": "Photograph", - "image": string - }[], - "interactionService": { - "@type": "WebSite", - "url": string - }, + ] + ownedBy: { + "@id": string + "@type": "BusinessEntity" + companyName: string + } + operatedBy: { + "@type": "BusinessEntity" + companyName: string + } + address: any + hasMap: any + contactPoint: { + "@type": "ContactPoint" + email: string + telephone: string + } + photos: { + "@type": "Photograph" + image: string + }[] + interactionService: { + "@type": "WebSite" + url: string + } /** * Contains various extra pieces of data, e.g. services or opening hours */ "@graph": [ { - "@type": "https://data.velopark.be/openvelopark/terms#PublicBicycleParking", - "openingHoursSpecification": { - "@type": "OpeningHoursSpecification", + "@type": "https://data.velopark.be/openvelopark/terms#PublicBicycleParking" + openingHoursSpecification: { + "@type": "OpeningHoursSpecification" /** * Ends with 'Monday', 'Tuesday', ... */ - "dayOfWeek": "http://schema.org/Monday" + dayOfWeek: + | "http://schema.org/Monday" | "http://schema.org/Tuesday" | "http://schema.org/Wednesday" | "http://schema.org/Thursday" | "http://schema.org/Friday" | "http://schema.org/Saturday" - | "http://schema.org/Sunday", + | "http://schema.org/Sunday" /** * opens: 00:00 and closes 23:59 for the entire day */ - "opens": string, - "closes": string - }[], + opens: string + closes: string + }[] /** * P30D = 30 days */ - "maximumParkingDuration": "P30D", - "publicAccess": true, - "totalCapacity": 110, - "allows": [ + maximumParkingDuration: "P30D" + publicAccess: true + totalCapacity: 110 + allows: [ { - "@type": "AllowedBicycle", + "@type": "AllowedBicycle" /* TODO is cargo bikes etc also available?*/ - "bicycleType": "https://data.velopark.be/openvelopark/terms#RegularBicycle", - "bicyclesAmount": number + bicycleType: + | string + | "https://data.velopark.be/openvelopark/terms#RegularBicycle" + bicyclesAmount: number } - ], - "geo": [ + ] + geo: [ { - "@type": "GeoCoordinates", - "latitude": number, - "longitude": number + "@type": "GeoCoordinates" + latitude: number + longitude: number } - ], - "priceSpecification": [ + ] + priceSpecification: [ { - "@type": "PriceSpecification", - "freeOfCharge": boolean + "@type": "PriceSpecification" + freeOfCharge: boolean } ] } ] - } diff --git a/src/Models/Constants.ts b/src/Models/Constants.ts index f2c058c66..1b3b71937 100644 --- a/src/Models/Constants.ts +++ b/src/Models/Constants.ts @@ -36,7 +36,7 @@ export default class Constants { "import_candidate", "usersettings", "icons", - "filters" + "filters", ] as const /** * Layer IDs of layers which have special properties through built-in hooks @@ -117,7 +117,9 @@ export default class Constants { */ private static readonly _defaultPinIcons = [ "pin", + "bug", "square", + "square_rounded", "circle", "checkmark", "clock", diff --git a/src/UI/Image/NearbyImages.svelte b/src/UI/Image/NearbyImages.svelte index 375b1b449..11e5c4d0c 100644 --- a/src/UI/Image/NearbyImages.svelte +++ b/src/UI/Image/NearbyImages.svelte @@ -55,7 +55,7 @@
{#each $images as image (image.pictureUrl)} - + {/each}
diff --git a/src/UI/Map/Icon.svelte b/src/UI/Map/Icon.svelte index 3265ec7de..e1c386ddb 100644 --- a/src/UI/Map/Icon.svelte +++ b/src/UI/Map/Icon.svelte @@ -1,47 +1,49 @@ @@ -50,6 +52,11 @@ {:else if icon === "square"} + {:else if icon === "square_rounded"} + + {:else if icon === "bug"} + + {:else if icon === "circle"} {:else if icon === "checkmark"} @@ -117,7 +124,7 @@ {:else if icon === "addSmall"} {:else if icon === "link"} - + {:else} {/if} diff --git a/src/UI/MapRoulette/MaprouletteSetStatus.svelte b/src/UI/MapRoulette/MaprouletteSetStatus.svelte index 18bccf608..908ec8b93 100644 --- a/src/UI/MapRoulette/MaprouletteSetStatus.svelte +++ b/src/UI/MapRoulette/MaprouletteSetStatus.svelte @@ -54,7 +54,7 @@ {:else if $status === Maproulette.STATUS_OPEN} - diff --git a/src/UI/SpecialVisualizations.ts b/src/UI/SpecialVisualizations.ts index 5bfc47024..8f7a7e001 100644 --- a/src/UI/SpecialVisualizations.ts +++ b/src/UI/SpecialVisualizations.ts @@ -3,7 +3,11 @@ import { FixedUiElement } from "./Base/FixedUiElement" import BaseUIElement from "./BaseUIElement" import Title from "./Base/Title" import Table from "./Base/Table" -import { RenderingSpecification, SpecialVisualization, SpecialVisualizationState } from "./SpecialVisualization" +import { + RenderingSpecification, + SpecialVisualization, + SpecialVisualizationState, +} from "./SpecialVisualization" import { HistogramViz } from "./Popup/HistogramViz" import { MinimapViz } from "./Popup/MinimapViz" import { ShareLinkViz } from "./Popup/ShareLinkViz" @@ -94,6 +98,11 @@ class NearbyImageVis implements SpecialVisualization { defaultValue: "closed", doc: "Either `open` or `closed`. If `open`, then the image carousel will always be shown", }, + { + name: "readonly", + required: false, + doc: "If 'readonly', will not show the 'link'-button", + }, ] docs = "A component showing nearby images loaded from various online services such as Mapillary. In edit mode and when used on a feature, the user can select an image to add to the feature" @@ -106,9 +115,10 @@ class NearbyImageVis implements SpecialVisualization { tags: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { const isOpen = args[0] === "open" + const readonly = args[1] === "readonly" const [lon, lat] = GeoOperations.centerpointCoordinates(feature) return new SvelteUIElement(isOpen ? NearbyImages : NearbyImagesCollapsed, { tags, @@ -117,6 +127,7 @@ class NearbyImageVis implements SpecialVisualization { lat, feature, layer, + linkable: !readonly, }) } } @@ -171,7 +182,7 @@ class StealViz implements SpecialVisualization { selectedElement: otherFeature, state, layer, - }), + }) ) } if (elements.length === 1) { @@ -179,8 +190,8 @@ class StealViz implements SpecialVisualization { } return new Combine(elements).SetClass("flex flex-col") }, - [state.indexedFeatures.featuresById], - ), + [state.indexedFeatures.featuresById] + ) ) } @@ -219,7 +230,7 @@ export class QuestionViz implements SpecialVisualization { tags: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { const labels = args[0] ?.split(";") @@ -273,8 +284,10 @@ export default class SpecialVisualizations { * templ.args[0] = "{email}" */ public static constructSpecification( - template: string | { special: Record> & { type: string } }, - extraMappings: SpecialVisualization[] = [], + template: + | string + | { special: Record> & { type: string } }, + extraMappings: SpecialVisualization[] = [] ): RenderingSpecification[] { if (template === "") { return [] @@ -283,7 +296,7 @@ export default class SpecialVisualizations { if (typeof template !== "string") { console.trace( "Got a non-expanded template while constructing the specification, it still has a 'special-key':", - template, + template ) throw "Got a non-expanded template while constructing the specification" } @@ -291,20 +304,20 @@ export default class SpecialVisualizations { for (const knownSpecial of allKnownSpecials) { // Note: the '.*?' in the regex reads as 'any character, but in a non-greedy way' const matched = template.match( - new RegExp(`(.*){${knownSpecial.funcName}\\((.*?)\\)(:.*)?}(.*)`, "s"), + new RegExp(`(.*){${knownSpecial.funcName}\\((.*?)\\)(:.*)?}(.*)`, "s") ) if (matched != null) { // We found a special component that should be brought to live const partBefore = SpecialVisualizations.constructSpecification( matched[1], - extraMappings, + extraMappings ) const argument = matched[2] /* .trim() // We don't trim, as spaces might be relevant, e.g. "what is ... of {title()}"*/ const style = matched[3]?.substring(1) ?? "" const partAfter = SpecialVisualizations.constructSpecification( matched[4], - extraMappings, + extraMappings ) const args = knownSpecial.args.map((arg) => arg.defaultValue ?? "") if (argument.length > 0) { @@ -343,31 +356,31 @@ export default class SpecialVisualizations { viz.docs, viz.args.length > 0 ? new Table( - ["name", "default", "description"], - viz.args.map((arg) => { - let defaultArg = arg.defaultValue ?? "_undefined_" - if (defaultArg == "") { - defaultArg = "_empty string_" - } - return [arg.name, defaultArg, arg.doc] - }), - ) + ["name", "default", "description"], + viz.args.map((arg) => { + let defaultArg = arg.defaultValue ?? "_undefined_" + if (defaultArg == "") { + defaultArg = "_empty string_" + } + return [arg.name, defaultArg, arg.doc] + }) + ) : undefined, new Title("Example usage of " + viz.funcName, 4), new FixedUiElement( viz.example ?? - "`{" + - viz.funcName + - "(" + - viz.args.map((arg) => arg.defaultValue).join(",") + - ")}`", + "`{" + + viz.funcName + + "(" + + viz.args.map((arg) => arg.defaultValue).join(",") + + ")}`" ).SetClass("literal-code"), ]) } public static HelpMessage() { const helpTexts = SpecialVisualizations.specialVisualizations.map((viz) => - SpecialVisualizations.DocumentationFor(viz), + SpecialVisualizations.DocumentationFor(viz) ) return new Combine([ @@ -401,10 +414,10 @@ export default class SpecialVisualizations { }, }, null, - " ", - ), + " " + ) ).SetClass("code"), - "In other words: use `{ \"before\": ..., \"after\": ..., \"special\": {\"type\": ..., \"argname\": ...argvalue...}`. The args are in the `special` block; an argvalue can be a string, a translation or another value. (Refer to class `RewriteSpecial` in case of problems)", + 'In other words: use `{ "before": ..., "after": ..., "special": {"type": ..., "argname": ...argvalue...}`. The args are in the `special` block; an argvalue can be a string, a translation or another value. (Refer to class `RewriteSpecial` in case of problems)', ]).SetClass("flex flex-col"), ...helpTexts, ]).SetClass("flex flex-col") @@ -413,20 +426,20 @@ export default class SpecialVisualizations { // noinspection JSUnusedGlobalSymbols public static renderExampleOfSpecial( state: SpecialVisualizationState, - s: SpecialVisualization, + s: SpecialVisualization ): BaseUIElement { const examples = s.structuredExamples === undefined ? [] : s.structuredExamples().map((e) => { - return s.constr( - state, - new UIEventSource>(e.feature.properties), - e.args, - e.feature, - undefined, - ) - }) + return s.constr( + state, + new UIEventSource>(e.feature.properties), + e.args, + e.feature, + undefined + ) + }) return new Combine([new Title(s.funcName), s.docs, ...examples]) } @@ -466,7 +479,7 @@ export default class SpecialVisualizations { assignTo: state.userRelatedState.language, availableLanguages: state.layout.language, preferredLanguages: state.osmConnection.userDetails.map( - (ud) => ud.languages, + (ud) => ud.languages ), }) }, @@ -491,7 +504,7 @@ export default class SpecialVisualizations { constr( state: SpecialVisualizationState, - tagSource: UIEventSource>, + tagSource: UIEventSource> ): BaseUIElement { return new VariableUiElement( tagSource @@ -501,7 +514,7 @@ export default class SpecialVisualizations { return new SplitRoadWizard(id, state) } return undefined - }), + }) ) }, }, @@ -515,7 +528,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { if (feature.geometry.type !== "Point") { return undefined @@ -538,7 +551,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { if (!layer.deletion) { return undefined @@ -566,7 +579,7 @@ export default class SpecialVisualizations { state: SpecialVisualizationState, tagSource: UIEventSource>, argument: string[], - feature: Feature, + feature: Feature ): BaseUIElement { const [lon, lat] = GeoOperations.centerpointCoordinates(feature) return new SvelteUIElement(CreateNewNote, { @@ -630,7 +643,7 @@ export default class SpecialVisualizations { .map((tags) => tags[args[0]]) .map((wikidata) => { wikidata = Utils.NoEmpty( - wikidata?.split(";")?.map((wd) => wd.trim()) ?? [], + wikidata?.split(";")?.map((wd) => wd.trim()) ?? [] )[0] const entry = Wikidata.LoadWikidataEntry(wikidata) return new VariableUiElement( @@ -640,9 +653,9 @@ export default class SpecialVisualizations { } const response = e["success"] return Translation.fromMap(response.labels) - }), + }) ) - }), + }) ), }, new MapillaryLinkVis(), @@ -674,7 +687,7 @@ export default class SpecialVisualizations { AllImageProviders.LoadImagesFor(tags, imagePrefixes), tags, state, - feature, + feature ) }, }, @@ -730,7 +743,7 @@ export default class SpecialVisualizations { { nameKey: nameKey, fallbackName, - }, + } ) return new SvelteUIElement(StarsBarIcon, { score: reviews.average, @@ -763,7 +776,7 @@ export default class SpecialVisualizations { { nameKey: nameKey, fallbackName, - }, + } ) return new SvelteUIElement(ReviewForm, { reviews, state, tags, feature, layer }) }, @@ -795,7 +808,7 @@ export default class SpecialVisualizations { { nameKey: nameKey, fallbackName, - }, + } ) return new SvelteUIElement(AllReviews, { reviews, state, tags, feature, layer }) }, @@ -853,7 +866,7 @@ export default class SpecialVisualizations { tags: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): SvelteUIElement { const keyToUse = args[0] const prefix = args[1] @@ -890,10 +903,10 @@ export default class SpecialVisualizations { return undefined } const allUnits: Unit[] = [].concat( - ...(state?.layout?.layers?.map((lyr) => lyr.units) ?? []), + ...(state?.layout?.layers?.map((lyr) => lyr.units) ?? []) ) const unit = allUnits.filter((unit) => - unit.isApplicableToKey(key), + unit.isApplicableToKey(key) )[0] if (unit === undefined) { return value @@ -901,7 +914,7 @@ export default class SpecialVisualizations { const getCountry = () => tagSource.data._country const [v, denom] = unit.findDenomination(value, getCountry) return unit.asHumanLongValue(v, getCountry) - }), + }) ) }, }, @@ -918,7 +931,7 @@ export default class SpecialVisualizations { new Combine([ t.downloadFeatureAsGeojson.SetClass("font-bold text-lg"), t.downloadGeoJsonHelper.SetClass("subtle"), - ]).SetClass("flex flex-col"), + ]).SetClass("flex flex-col") ) .onClick(() => { console.log("Exporting as Geojson") @@ -931,7 +944,7 @@ export default class SpecialVisualizations { title + "_mapcomplete_export.geojson", { mimetype: "application/vnd.geo+json", - }, + } ) }) .SetClass("w-full") @@ -967,7 +980,7 @@ export default class SpecialVisualizations { constr: (state) => { return new SubtleButton( Svg.delete_icon_svg().SetStyle("height: 1.5rem"), - Translations.t.general.removeLocationHistory, + Translations.t.general.removeLocationHistory ).onClick(() => { state.historicalUserLocations.features.setData([]) state.selectedElement.setData(undefined) @@ -1005,10 +1018,10 @@ export default class SpecialVisualizations { .filter((c) => c.text !== "") .map( (c, i) => - new NoteCommentElement(c, state, i, comments.length), - ), + new NoteCommentElement(c, state, i, comments.length) + ) ).SetClass("flex flex-col") - }), + }) ), }, { @@ -1049,9 +1062,9 @@ export default class SpecialVisualizations { return undefined } return new SubstitutedTranslation(title, tagsSource, state).SetClass( - "px-1", + "px-1" ) - }), + }) ), }, { @@ -1067,8 +1080,8 @@ export default class SpecialVisualizations { let challenge = Stores.FromPromise( Utils.downloadJsonCached( `${Maproulette.defaultEndpoint}/challenge/${parentId}`, - 24 * 60 * 60 * 1000, - ), + 24 * 60 * 60 * 1000 + ) ) return new VariableUiElement( @@ -1093,7 +1106,7 @@ export default class SpecialVisualizations { } else { return [title, new List(listItems)] } - }), + }) ) }, docs: "Fetches the metadata of MapRoulette campaign that this task is part of and shows those details (namely `title`, `description` and `instruction`).\n\nThis reads the property `mr_challengeId` to detect the parent campaign.", @@ -1107,15 +1120,15 @@ export default class SpecialVisualizations { "\n" + "```json\n" + "{\n" + - " \"id\": \"mark_duplicate\",\n" + - " \"render\": {\n" + - " \"special\": {\n" + - " \"type\": \"maproulette_set_status\",\n" + - " \"message\": {\n" + - " \"en\": \"Mark as not found or false positive\"\n" + + ' "id": "mark_duplicate",\n' + + ' "render": {\n' + + ' "special": {\n' + + ' "type": "maproulette_set_status",\n' + + ' "message": {\n' + + ' "en": "Mark as not found or false positive"\n' + " },\n" + - " \"status\": \"2\",\n" + - " \"image\": \"close\"\n" + + ' "status": "2",\n' + + ' "image": "close"\n' + " }\n" + " }\n" + "}\n" + @@ -1181,8 +1194,8 @@ export default class SpecialVisualizations { const fsBboxed = new BBoxFeatureSourceForLayer(fs, bbox) return new StatisticsPanel(fsBboxed) }, - [state.mapProperties.bounds], - ), + [state.mapProperties.bounds] + ) ) }, }, @@ -1248,7 +1261,7 @@ export default class SpecialVisualizations { constr( state: SpecialVisualizationState, tagSource: UIEventSource>, - args: string[], + args: string[] ): BaseUIElement { let [text, href, classnames, download, ariaLabel] = args if (download === "") { @@ -1265,8 +1278,8 @@ export default class SpecialVisualizations { download: Utils.SubstituteKeys(download, tags), ariaLabel: Utils.SubstituteKeys(ariaLabel, tags), newTab, - }), - ), + }) + ) ) }, }, @@ -1288,7 +1301,7 @@ export default class SpecialVisualizations { }, }, null, - " ", + " " ) + "\n```", args: [ @@ -1310,26 +1323,30 @@ export default class SpecialVisualizations { featureTags.map((tags) => { try { const data = tags[key] - const properties: object[] = typeof data === "string" ? JSON.parse(tags[key]) : data + const properties: object[] = + typeof data === "string" ? JSON.parse(tags[key]) : data const elements = [] for (const property of properties) { const subsTr = new SubstitutedTranslation( translation, new UIEventSource(property), - state, + state ) elements.push(subsTr) } return new List(elements) } catch (e) { - console.log("Something went wrong while generating the elements for a multi", { - e, - tags, - key, - loaded: tags[key], - }) + console.log( + "Something went wrong while generating the elements for a multi", + { + e, + tags, + key, + loaded: tags[key], + } + ) } - }), + }) ) }, }, @@ -1349,7 +1366,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { return new VariableUiElement( tagSource.map((tags) => { @@ -1361,7 +1378,7 @@ export default class SpecialVisualizations { console.error("Cannot create a translation for", v, "due to", e) return JSON.stringify(v) } - }), + }) ) }, }, @@ -1381,7 +1398,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { const key = argument[0] const validator = new FediverseValidator() @@ -1391,14 +1408,14 @@ export default class SpecialVisualizations { .map((fediAccount) => { fediAccount = validator.reformat(fediAccount) const [_, username, host] = fediAccount.match( - FediverseValidator.usernameAtServer, + FediverseValidator.usernameAtServer ) return new SvelteUIElement(Link, { text: fediAccount, url: "https://" + host + "/@" + username, newTab: true, }) - }), + }) ) }, }, @@ -1418,7 +1435,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { return new FixedUiElement("{" + args[0] + "}") }, @@ -1439,7 +1456,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { const key = argument[0] ?? "value" return new VariableUiElement( @@ -1457,12 +1474,12 @@ export default class SpecialVisualizations { } catch (e) { return new FixedUiElement( "Could not parse this tag: " + - JSON.stringify(value) + - " due to " + - e, + JSON.stringify(value) + + " due to " + + e ).SetClass("alert") } - }), + }) ) }, }, @@ -1483,7 +1500,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { const giggityUrl = argument[0] return new SvelteUIElement(Giggity, { tags: tagSource, state, giggityUrl }) @@ -1499,12 +1516,12 @@ export default class SpecialVisualizations { _: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { const tags = (( state )).geolocation.currentUserLocation.features.map( - (features) => features[0]?.properties, + (features) => features[0]?.properties ) return new Combine([ new SvelteUIElement(OrientationDebugPanel, {}), @@ -1526,7 +1543,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { return new SvelteUIElement(MarkAsFavourite, { tags: tagSource, @@ -1546,7 +1563,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { return new SvelteUIElement(MarkAsFavouriteMini, { tags: tagSource, @@ -1566,7 +1583,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { return new SvelteUIElement(DirectionIndicator, { state, feature }) }, @@ -1581,7 +1598,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { return new VariableUiElement( tagSource @@ -1603,9 +1620,9 @@ export default class SpecialVisualizations { `${window.location.protocol}//${window.location.host}${window.location.pathname}?${layout}lat=${lat}&lon=${lon}&z=15` + `#${id}` return new Img(new Qr(url).toImageElement(75)).SetStyle( - "width: 75px", + "width: 75px" ) - }), + }) ) }, }, @@ -1625,7 +1642,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { const key = args[0] === "" ? "_direction:centerpoint" : args[0] return new VariableUiElement( @@ -1636,11 +1653,11 @@ export default class SpecialVisualizations { }) .mapD((value) => { const dir = GeoOperations.bearingToHuman( - GeoOperations.parseBearing(value), + GeoOperations.parseBearing(value) ) console.log("Human dir", dir) return Translations.t.general.visualFeedback.directionsAbsolute[dir] - }), + }) ) }, }, @@ -1664,13 +1681,19 @@ export default class SpecialVisualizations { doc: "Apply some postprocessing. Currently, only 'velopark' is allowed as value", }, { - name:"readonly", + name: "readonly", required: false, - doc: "If 'yes', will not show 'apply'-buttons" - } + doc: "If 'yes', will not show 'apply'-buttons", + }, ], docs: "Gives an interactive element which shows a tag comparison between the OSM-object and the upstream object. This allows to copy some or all tags into OSM", - constr(state: SpecialVisualizationState, tagSource: UIEventSource>, args: string[], feature: Feature, layer: LayerConfig): BaseUIElement { + constr( + state: SpecialVisualizationState, + tagSource: UIEventSource>, + args: string[], + feature: Feature, + layer: LayerConfig + ): BaseUIElement { const url = args[0] const postprocessVelopark = args[2] === "velopark" const readonly = args[3] === "yes" @@ -1681,7 +1704,7 @@ export default class SpecialVisualizations { tags: tagSource, layer, feature, - readonly + readonly, }) }, }, @@ -1696,7 +1719,7 @@ export default class SpecialVisualizations { throw ( "Invalid special visualisation found: funcName is undefined for " + invalid.map((sp) => sp.i).join(", ") + - ". Did you perhaps type \n funcName: \"funcname\" // type declaration uses COLON\ninstead of:\n funcName = \"funcName\" // value definition uses EQUAL" + '. Did you perhaps type \n funcName: "funcname" // type declaration uses COLON\ninstead of:\n funcName = "funcName" // value definition uses EQUAL' ) } diff --git a/src/assets/svg/Square_rounded.svelte b/src/assets/svg/Square_rounded.svelte new file mode 100644 index 000000000..135e304ed --- /dev/null +++ b/src/assets/svg/Square_rounded.svelte @@ -0,0 +1,4 @@ + + \ No newline at end of file From c83d57eb8ddc75136c294a5c69a1102adadb76ca Mon Sep 17 00:00:00 2001 From: kjon Date: Sun, 14 Jan 2024 17:27:20 +0000 Subject: [PATCH 043/195] Translated using Weblate (German) Currently translated at 100.0% (601 of 601 strings) Translation: MapComplete/Core Translate-URL: https://hosted.weblate.org/projects/mapcomplete/core/de/ --- langs/de.json | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/langs/de.json b/langs/de.json index aa59c887b..f340d4546 100644 --- a/langs/de.json +++ b/langs/de.json @@ -186,7 +186,8 @@ "backgroundSwitch": "Hintergrund wechseln", "cancel": "Abbrechen", "confirm": "Bestätigen", - "customThemeIntro": "

Benutzerdefinierte Themen

Dies sind zuvor angesehene nutzergenerierte Themen.", + "customThemeIntro": "Bereits angesehene nutzergenerierte Themen.", + "customThemeTitle": "Benutzerdefinierte Themen", "download": { "downloadAsPdf": "Aktuelle Karte als PDF herunterladen", "downloadAsPdfHelper": "Ideal zum Drucken der aktuellen Karte", @@ -230,6 +231,7 @@ "labels": { "background": "Hintergrund ändern", "filter": "Daten filtern", + "jumpToLocation": "Eigenen Standort anzeigen", "menu": "Menü", "zoomIn": "Hineinzoomen", "zoomOut": "Herauszoomen" @@ -279,13 +281,17 @@ "openTheMap": "Karte öffnen", "openTheMapAtGeolocation": "Zum eigenen Standort zoomen", "opening_hours": { + "all_days_from": "Geöffnet täglich {ranges}", "closed_permanently": "Geschlossen auf unbestimmte Zeit", "closed_until": "Geschlossen bis {date}", + "error": "Öffnungszeiten können nicht ausgewertet werden", "error_loading": "Fehler: Diese Öffnungszeiten können nicht angezeigt werden.", "friday": "Am Freitag {ranges}", "loadingCountry": "Land ermitteln…", "monday": "Am Montag {ranges}", "not_all_rules_parsed": "Die Öffnungszeiten sind kompliziert. Folgenden Regeln werden im Eingabefenster ignoriert:", + "on_weekdays": "Geöffnet werktags {ranges}", + "on_weekends": "Geöffnet am Wochenende {ranges}", "openTill": "bis", "open_24_7": "Durchgehend geöffnet", "open_during_ph": "An Feiertagen ist hier", @@ -344,6 +350,7 @@ "searchShort": "Suche…", "searching": "Suchen …" }, + "searchAnswer": "Option suchen…", "share": "Teilen", "sharescreen": { "copiedToClipboard": "Verknüpfung in Zwischenablage kopiert", @@ -359,7 +366,7 @@ "testing": "Testen - Änderungen werden nicht gespeichert", "uploadError": "Fehler beim Hochladen von Änderungen: {error}", "uploadGpx": { - "choosePermission": "Wählen Sie unten, ob Ihre Strecke geteilt werden soll:", + "choosePermission": "Wählen Sie unten, wie Ihre Strecke geteilt werden soll:", "confirm": "Hochladen bestätigen", "gpxServiceOffline": "Der GPX-Dienst ist derzeit offline - ein Hochladen ist derzeit nicht möglich. Versuchen Sie es später noch einmal.", "intro0": "Wenn Sie Ihre Strecke hochladen, behält OpenStreetMap.org eine vollständige Kopie der Strecke.", @@ -391,6 +398,7 @@ "useSearch": "Verwenden Sie die Suche oben, um Voreinstellungen anzuzeigen", "useSearchForMore": "Verwenden Sie die Suchfunktion, um innerhalb von {total} weitere Werte zu suchen…", "visualFeedback": { + "closestFeaturesAre": "{n} Objekte im Anzeigebereich.", "directionsAbsolute": { "E": "Ost", "N": "Nord", @@ -402,6 +410,7 @@ "W": "West" }, "directionsRelative": { + "behind": "in deinem Rücken", "left": "links", "right": "rechts", "sharp_left": "scharf links", @@ -409,7 +418,23 @@ "slight_left": "leicht links", "slight_right": "leicht rechts", "straight": "geradeaus" - } + }, + "east": "Nach Osten bewegen", + "fromGps": "{distance} {direction} von deinem Standort", + "fromMapCenter": "{distance} {direction} von der Kartenmitte", + "in": "Hineinzoomen auf Stufe {z}", + "islocked": "Die Ansicht ist an Ihren GPS-Standort gebunden, Bewegen ist deaktiviert. Drücken Sie zum Entsperren die Geolocation-Taste.", + "locked": "Die Ansicht ist jetzt an Ihren GPS-Standort gebunden, Bewegen ist deaktiviert.", + "navigation": "Verwenden Sie die Pfeiltasten, um die Karte zu bewegen, drücken Sie die Leertaste, um das nächstgelegene Objekt auszuwählen. Drücken Sie eine Zahl, um weiter entfernte Objekte auszuwählen.", + "noCloseFeatures": "Keine Objekte im Anzeigebereich.", + "north": "Nach Norden bewegen", + "oneFeatureInView": "Ein Objekt im Anzeigebereich.", + "out": "Herauszoomen auf Stufe {z}", + "south": "Nach Süden bewegen", + "unlocked": "Bewegen aktiviert.", + "viewportCenterCloseToGps": "Die Karte wird um Ihren Standort zentriert.", + "viewportCenterDetails": "Die Kartenmitte ist {distance} und {bearing} von Ihrem Standort entfernt.", + "west": "Nach Westen bewegen" }, "waitingForGeopermission": "Warten auf Ihre Erlaubnis, Standortdaten zu verwenden…", "waitingForLocation": "Ihr Standort wird gesucht…", @@ -461,7 +486,9 @@ "geolocate": "Karte auf den aktuellen Standort verschieben oder zoomen. Erfordert Standortberechtigung", "intro": "MapComplete unterstützt folgende Tastaturbefehle:", "key": "Tastenkombination", + "openFilterPanel": "Öffnet das Panel für POI-Ebenen und Filter", "openLayersPanel": "Auswahl für Hintergrundebenen öffnen", + "queryCurrentLocation": "Adresse anzeigen, die der Kartenmitte am nächsten liegt", "selectAerial": "Hintergrund als Luftbild oder Satellitenbild einstellen. Wechselt zwischen den zwei besten verfügbaren Ebenen", "selectFavourites": "Favoriten anzeigen", "selectItem": "Objekt auswählen, das dem Kartenmittelpunkt (Fadenkreuz) am nächsten liegt. Nur wenn die Tastaturnavigation verwendet wird", @@ -472,6 +499,7 @@ "selectMapnik": "OpenStreetMap-carto als Hintergrundebene wählen", "selectOsmbasedmap": "OpenStreetMap-basierte Karte als Hintergrund auswählen (oder Hintergrundebene deaktivieren)", "selectSearch": "Suchleiste auswählen, um nach Orten zu suchen", + "shakePhone": "Telefon schütteln", "title": "Tastaturbefehle" }, "image": { @@ -481,7 +509,9 @@ "dontDelete": "Abbrechen", "isDeleted": "Gelöscht", "nearby": { + "close": "Übersicht mit nahegelegenen Bildern ausklappen", "link": "Dieses Bild zeigt das Objekt", + "noNearbyImages": "Keine nahegelegenen Bilder gefunden", "seeNearby": "Bilder in der Nähe durchsuchen und verlinken", "title": "Straßenbilder in der Nähe" }, @@ -628,15 +658,19 @@ "reviews": { "affiliated_reviewer_warning": "(Partner-Rezension)", "attribution": "Rezensionen von Mangrove Reviews sind unter CC-BY 4.0 verfügbar.", + "averageRating": "Mittlere Bewertung von {n} Sternen", "i_am_affiliated": "Ich bin mit diesem Objekt vertraut", "i_am_affiliated_explanation": "Prüfung, ob Sie der Eigentümer, Ersteller, Angestellter, … sind", "name_required": "Der Name des Objekts ist erforderlich, um Bewertungen zu erstellen und anzuzeigen", "no_reviews_yet": "Es gibt noch keine Bewertungen. Hilf mit der ersten Bewertung dem Geschäft und der Open Data Bewegung!", "question": "Wie bewerten Sie {title()}?", "question_opinion": "Wie war Ihre Erfahrung?", + "rate": "Mit {n} Sternen bewerten", + "rated": "Mit {n} Sternen bewertet", + "reviewPlaceholder": "Beschreibe deine Erfahrung…", "reviewing_as": "Als {nickname} bewerten", "reviewing_as_anonymous": "Anonym bewerten", - "save": "Speichern", + "save": "Bewertung speichern", "saved": "Bewertung gespeichert. Danke fürs Teilen!", "saving_review": "Speichern…", "title": "{count} Rezensionen", @@ -723,6 +757,10 @@ "description": "eine positive, ganze Zahl", "noZero": "Null ist nicht erlaubt" }, + "slope": { + "inputExplanation": "Legen Sie Ihr Telefon so auf den Boden, dass die Oberseite des Telefons zum oberen Ende des Hangs zeigt.", + "inputIncorrect": "Für korrekte Messungen achten Sie darauf, dass sich der Pfeil in der grünen Fläche befindet." + }, "string": { "description": "ein Stück Text" }, From 93bf93449e86842a02f4f9442edaf40f4b02c716 Mon Sep 17 00:00:00 2001 From: Lasse Liehu Date: Tue, 16 Jan 2024 18:35:32 +0000 Subject: [PATCH 044/195] Translated using Weblate (Finnish) Currently translated at 40.0% (241 of 601 strings) Translation: MapComplete/Core Translate-URL: https://hosted.weblate.org/projects/mapcomplete/core/fi/ --- langs/fi.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/langs/fi.json b/langs/fi.json index 5ae0f8515..6c059697a 100644 --- a/langs/fi.json +++ b/langs/fi.json @@ -157,6 +157,7 @@ "attributionBackgroundLayerWithCopyright": "Nykyinen taustataso on {name}: {copyright}", "attributionContent": "

Kaiken datan tarjoaa OpenStreetMap, vapaasti uudelleenkäytettävissä Open Database Licensen mukaisesti.

", "attributionTitle": "Kiitokset", + "codeContributionsBy": "MapCompleten on tehneet {contributors} ja {hiddenCount} muuta", "donate": "Tue MapCompletea rahallisesti", "editId": "Avaa OpenStreetMap-verkkoeditori tänne", "editJosm": "Muokkaa täällä JOSM:illa", @@ -164,9 +165,17 @@ "iconAttribution": { "title": "Käytetyt kuvakkeet" }, + "josmNotOpened": "JOSM:iin ei saatu yhteyttä. Tarkista, että se on auki ja etähallinta on käytössä", "josmOpened": "JOSM on avattu", + "mapContributionsBy": "Tällä hetkellä näkyvää tietoa on muokannut {contributors}", + "mapContributionsByAndHidden": "Tällä hetkellä näkyvää tietoa on muokannut {contributors} ja {hiddenCount} muuta", + "mapDataByOsm": "Karttatiedot: OpenStreetMap", + "mapillaryHelp": "Mapillary on verkkopalvelu, joka kerää katutason kuvia ja tarjoaa niitä vapaan lisenssi mukaisesti. Näitä kuvia saa käyttää parantamaan OpenStreetMapiä", "openIssueTracker": "Ilmoita ohjelmavirheestä", "openMapillary": "Avaa Mapillary tänne", + "openOsmcha": "Näytä viimeisimmät muokkaukset, jotka on tehty teemalla {theme}", + "seeOnMapillary": "Näytä tämä kuva Mapillaryssä", + "themeBy": "Teemaa ylläpitää {author}", "title": "Tekijänoikeudet ja alkuperä" }, "backToIndex": "Palaa kaikkien teemakarttojen yleiskuvaan", From 215b566955dd9754eac676bd61f154c1e625d7e5 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 18 Jan 2024 14:45:07 +0100 Subject: [PATCH 045/195] Add introductory text --- assets/themes/velopark/velopark.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/assets/themes/velopark/velopark.json b/assets/themes/velopark/velopark.json index be17bb702..a76c4a932 100644 --- a/assets/themes/velopark/velopark.json +++ b/assets/themes/velopark/velopark.json @@ -3,7 +3,10 @@ "title": { "nl": "Velopark naar OpenStreetMap sync tool" }, - "description": "A commisioned theme", + "description": { + "en": "

Velopark.be is a website collecting data about bicycle parkings in a semi-crowdsourced way. However, only 'authorized' instances are allowed to make changes there, in practice the operator of the bicycle parking such as SNCB, de Lijn or the municipality. They have now decided to synchronize their dataset with OpenStreetMap, and this MapComplete-instance is set up to help link and import their data into OpenStreetMap.

How to use:
  • A velopark-icon on the map (yellow with bicycle silhouette) represents a bicycle known by Velopark but not yet known by OpenStreetMap
  • Blue pins are bicycle parkings known by OpenStreetMap
  • Light blue pins are bicycle parkings known by OpenStreetMap with a reference to Velopark.be (ref-velopark=*)
  • Click a velopark item, you can either link it with a nearby OSM-bicycle parking or create a new bicycle parking. Note that the geometry of Velopark is often incorrect and can be a few up till 100 meters away from the actual bicycle parking. Use aerial imagery, linked images and streetview to determine the correct location
  • Once linked, you can compare the Velopark- and OSM-attributes and apply correct attributes
  • If Velopark has an image, you can also link the image
That's it! Thanks for helping to import this!", + "nl": "

Velopark.be is een website die data verzamelt over fietsenstallingen in een semi-crowdsource manier. Hierbij kunnen enkel geautorizeerde gebruikers data bijdragen, in de praktijk de uitbaters van de fietsenstallingen zoals de bevoegde gemeentebesturen, de NMBS of de Lijn. Velopark.be heeft nu beslist om hun data met OpenStreetMap te synchronizeren. Deze website is de tool om van Velopark.be naar OpenStreetMap te gaan en hun data te importeren.

Hoe te gebruiken?
  • Een velopark-logo op de kaart (geel met een fietssilhouette) duidt een fietsenstalling aan die gekend is in Velopark maar nog niet gekend (of gelinkt) is aan een fietsenstalling in OpenStreetMap
  • Een blauwe pin duidt een fietsenstalling aan die gekend is in OpenStreetMap
  • Een licht-blauwe pin duidt een fietsenstalling aan uit OpenStreetMap die een link heeft naar Velopark.be (ref-velopark=*)
  • Als je op een velopark-item klikt op, kan je deze linken met een fietsenstalling in de buurt (<25m) of een nieuwe fietstalling aan OpenStreetMap toevoegen. Let op: de geometrie van Velopark is zelden correct en wijkt makkelijk 10 meter of meer af van de echte locatie - in uitzonderlijke gevallen zelfs tot meer dan 100 meter. Gebruik de meest recente luchtfoto's, de gelinkte foto's en mapillary om de correcte locatie te bepalen
  • Eens gelinkt, kan je de Velopark- en OSM-attributen vergelijken en de correcte attributen toepassen in OpenStreetMap
  • Indien velopark een foto heeft, kan je die ook nog linken
Dat is het! Bedankt om mee te helpen!" + }, "hideFromOverview": true, "icon": "./assets/themes/velopark/velopark.svg", "mustHaveLanguage": [ From 8f5634ea8679d50c84d1b093b61f5db5b408692d Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Fri, 19 Jan 2024 00:02:23 +0100 Subject: [PATCH 046/195] Tooling: stop build if something goes wrong --- scripts/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build.sh b/scripts/build.sh index 766dd5a80..7851dd513 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -18,7 +18,7 @@ fi export NODE_OPTIONS=--max-old-space-size=16000 which vite vite --version -vite build --sourcemap +vite build --sourcemap || { echo 'Vite build failed' ; exit 1; } # Copy the layer files, as these might contain assets (e.g. svgs) cp -r assets/layers/ dist/assets/layers/ cp -r assets/themes/ dist/assets/themes/ From 50e3c5483461cd085d3207fbd96f5b7fdfaebe57 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Fri, 19 Jan 2024 00:03:50 +0100 Subject: [PATCH 047/195] Themes: add short description to velopark --- assets/themes/velopark/velopark.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/assets/themes/velopark/velopark.json b/assets/themes/velopark/velopark.json index a76c4a932..b970620b3 100644 --- a/assets/themes/velopark/velopark.json +++ b/assets/themes/velopark/velopark.json @@ -3,6 +3,10 @@ "title": { "nl": "Velopark naar OpenStreetMap sync tool" }, + "shortDescription": { + "en": "A tool to import data from velopark.be into OpenStreetMap", + "nl": "Een hulpmiddel om data van velopark.be in OpenStreetMap in te laden" + }, "description": { "en": "

Velopark.be is a website collecting data about bicycle parkings in a semi-crowdsourced way. However, only 'authorized' instances are allowed to make changes there, in practice the operator of the bicycle parking such as SNCB, de Lijn or the municipality. They have now decided to synchronize their dataset with OpenStreetMap, and this MapComplete-instance is set up to help link and import their data into OpenStreetMap.

How to use:
  • A velopark-icon on the map (yellow with bicycle silhouette) represents a bicycle known by Velopark but not yet known by OpenStreetMap
  • Blue pins are bicycle parkings known by OpenStreetMap
  • Light blue pins are bicycle parkings known by OpenStreetMap with a reference to Velopark.be (ref-velopark=*)
  • Click a velopark item, you can either link it with a nearby OSM-bicycle parking or create a new bicycle parking. Note that the geometry of Velopark is often incorrect and can be a few up till 100 meters away from the actual bicycle parking. Use aerial imagery, linked images and streetview to determine the correct location
  • Once linked, you can compare the Velopark- and OSM-attributes and apply correct attributes
  • If Velopark has an image, you can also link the image
That's it! Thanks for helping to import this!", "nl": "

Velopark.be is een website die data verzamelt over fietsenstallingen in een semi-crowdsource manier. Hierbij kunnen enkel geautorizeerde gebruikers data bijdragen, in de praktijk de uitbaters van de fietsenstallingen zoals de bevoegde gemeentebesturen, de NMBS of de Lijn. Velopark.be heeft nu beslist om hun data met OpenStreetMap te synchronizeren. Deze website is de tool om van Velopark.be naar OpenStreetMap te gaan en hun data te importeren.

Hoe te gebruiken?
  • Een velopark-logo op de kaart (geel met een fietssilhouette) duidt een fietsenstalling aan die gekend is in Velopark maar nog niet gekend (of gelinkt) is aan een fietsenstalling in OpenStreetMap
  • Een blauwe pin duidt een fietsenstalling aan die gekend is in OpenStreetMap
  • Een licht-blauwe pin duidt een fietsenstalling aan uit OpenStreetMap die een link heeft naar Velopark.be (ref-velopark=*)
  • Als je op een velopark-item klikt op, kan je deze linken met een fietsenstalling in de buurt (<25m) of een nieuwe fietstalling aan OpenStreetMap toevoegen. Let op: de geometrie van Velopark is zelden correct en wijkt makkelijk 10 meter of meer af van de echte locatie - in uitzonderlijke gevallen zelfs tot meer dan 100 meter. Gebruik de meest recente luchtfoto's, de gelinkte foto's en mapillary om de correcte locatie te bepalen
  • Eens gelinkt, kan je de Velopark- en OSM-attributen vergelijken en de correcte attributen toepassen in OpenStreetMap
  • Indien velopark een foto heeft, kan je die ook nog linken
Dat is het! Bedankt om mee te helpen!" From 6085a58302b43ca79d166ba27d61e5504532214e Mon Sep 17 00:00:00 2001 From: kjon Date: Tue, 16 Jan 2024 20:42:06 +0000 Subject: [PATCH 048/195] Translated using Weblate (German) Currently translated at 100.0% (3182 of 3182 strings) Translation: MapComplete/Layer translations Translate-URL: https://hosted.weblate.org/projects/mapcomplete/layers/de/ --- langs/layers/de.json | 171 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 166 insertions(+), 5 deletions(-) diff --git a/langs/layers/de.json b/langs/layers/de.json index 3df954e04..5b9aebd7b 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -3797,6 +3797,20 @@ } }, "question": "Ist die Trinkwasserstelle ganzjährig in Betrieb?" + }, + "type": { + "mappings": { + "0": { + "then": "Dies ist ein Sprudelbrunnen. Ein Wasserstrahl zum Trinken wird nach oben gerichtet und in der Regel durch einen Druckknopf gesteuert." + }, + "1": { + "then": "Dies ist eine Flaschenauffüllstation, an der das Wasser nach unten geleitet wird, in der Regel durch einen Druckknopf oder einen Bewegungssensor gesteuert. Direkt aus dem Wasserstrahl zu trinken, kann sehr schwierig oder unmöglich sein." + }, + "2": { + "then": "Dies ist ein Wasserhahn. Das Wasser fließt nach unten und der Wasserstrahl wird durch ein Ventil oder einen Druckknopf gesteuert." + } + }, + "question": "Um welche Art von Trinkwasserentnahmestelle handelt es sich?" } }, "title": { @@ -4719,6 +4733,9 @@ }, "3": { "then": "Hier werden ausschließlich vegane Gerichte angeboten" + }, + "4": { + "then": "Einige Gerichte können auf Nachfrage in eine vegane Version umgewandelt werden" } }, "question": "Werden hier vegane Gerichte angeboten?" @@ -4736,6 +4753,9 @@ }, "3": { "then": "Hier werden ausschließlich vegetarische Gerichte angeboten" + }, + "4": { + "then": "Einige Gerichte können auf Nachfrage in eine vegetarische Version umgewandelt werden" } }, "question": "Werden hier vegetarische Gerichte angeboten?" @@ -5023,9 +5043,29 @@ "1": "eine CNC-Fräse", "2": "CNC-Fräse" }, + "3": { + "1": "ein Multimediastudio", + "2": "Multimediastudio" + }, "4": { "1": "eine Nähmaschine", "2": "Nähmaschine" + }, + "5": { + "1": "eine Holzwerkstatt", + "2": "Holzwerkstatt" + }, + "6": { + "1": "eine Keramikwerkstatt", + "2": "Keramikwerkstatt" + }, + "7": { + "1": "eine Metallwerkstatt", + "2": "Metallwerkstatt" + }, + "8": { + "1": "eine Fahrradwerkstatt", + "2": "Fahrradwerkstatt" } } } @@ -5254,7 +5294,41 @@ } }, "icons": { - "description": "Eine Ebene, die als Bibliothek für Symbol-Tag-Renderings dient, insbesondere um als Abzeichen neben einem POI angezeigt zu werden" + "description": "Eine Ebene, die als Bibliothek für Symbol-Tag-Renderings dient, insbesondere um als Abzeichen neben einem POI angezeigt zu werden", + "tagRenderings": { + "osmlink": { + "mappings": { + "1": { + "then": { + "special": { + "arialabel": "Auf openstreetmap.org öffnen" + } + } + } + }, + "render": { + "special": { + "arialabel": "Auf openstreetmap.org öffnen" + } + } + }, + "phonelink": { + "mappings": { + "0": { + "then": { + "special": { + "arialabel": "Telefon" + } + } + } + }, + "render": { + "special": { + "arialabel": "Telefon" + } + } + } + } }, "indoors": { "description": "Grundlegende Innenraumkartierung: zeigt Umrisse von Räumen", @@ -6065,6 +6139,11 @@ } }, "render": "Notiz" + }, + "titleIcons": { + "0": { + "ariaLabel": "Auf OpenStreetMap.org ansehen" + } } }, "observation_tower": { @@ -6666,6 +6745,12 @@ }, "7": { "then": "Die Oberfläche ist befestigt" + }, + "8": { + "then": "Der Belag ist aus Tartan - ein synthetischer, federnder Belag, der typischerweise auf Sportbahnen zu finden ist" + }, + "9": { + "then": "Die Oberfläche besteht aus Gummi, z. B. aus Gummifliesen, Gummimulch oder einer großen Gummifläche" } }, "question": "Welchen Bodenbelag hat dieser Spielplatz?", @@ -7098,6 +7183,7 @@ "question": "Sind Hunde hier erlaubt?" }, "email": { + "editButtonAriaLabel": "E-Mail Adresse bearbeiten", "question": "Wie lautet die Mail-Adresse von {title()}?" }, "gluten_free": { @@ -7296,8 +7382,14 @@ } }, "phone": { + "editButtonAriaLabel": "Telefonnummer bearbeiten", "question": "Wie lautet die Telefonnummer von {title()}?" }, + "qr_code": { + "render": { + "after": "Scannen Sie den Code, um diesen Ort auf einem anderen Gerät zu öffnen" + } + }, "repeated": { "render": "Mehrere identische Objekte können in Geschossen {repeat_on} gefunden werden." }, @@ -7318,6 +7410,13 @@ }, "question": "Gibt es hier Steckdosen, an denen Kunden ihre Geräte laden können?" }, + "share": { + "render": { + "special": { + "text": "Standort teilen" + } + } + }, "single_level": { "mappings": { "0": { @@ -7398,6 +7497,7 @@ "question": "Bietet dieser Ort eine vegane Option an?" }, "website": { + "editButtonAriaLabel": "Webseite bearbeiten", "question": "Wie lautet die Webseite von {title()}?" }, "wheelchair-access": { @@ -7967,7 +8067,7 @@ "name": "Geschäfte", "presets": { "0": { - "description": "Ein neues Geschäft hinzufügen", + "description": "Sie können später angeben, was das Geschäft verkauft.", "title": "ein Geschäft" } }, @@ -8414,6 +8514,9 @@ }, "5": { "then": "Die Oberfläche ist feiner Kies" + }, + "6": { + "then": "Der Belag dieser Laufbahn ist Tartan, ein synthetischer, leicht federnder, poröser Belag" } }, "question": "Welchen Belag hat der Sportplatz?", @@ -8464,6 +8567,18 @@ }, "question": "Hat die Treppe einen Handlauf?" }, + "incline": { + "mappings": { + "0": { + "then": "Die Aufwärtsrichtung ist {direction_absolute()}" + }, + "1": { + "then": "Die Abwärtsrichtung ist {direction_absolute()}" + } + }, + "question": "Welche Steigung hat die Treppe?", + "render": "Die Treppe hat eine Steigung von {incline}" + }, "multilevels": { "override": { "question": "Zwischen welchen Stockwerken befindet sich diese Treppe?", @@ -9559,6 +9674,7 @@ } }, "unit": { + "description": "Bibliotheksebene mit allen gängigen Einrichtungen. Einrichtungen können _nur_ aus dieser Datei importiert werden.", "units": { "0": { "applicableUnits": { @@ -9576,6 +9692,20 @@ } } }, + "1": { + "applicableUnits": { + "0": { + "human": "{quantity} Volt" + } + } + }, + "2": { + "applicableUnits": { + "0": { + "human": "{quantity} A" + } + } + }, "3": { "applicableUnits": { "0": { @@ -9583,7 +9713,8 @@ "humanSingular": "ein Meter" }, "1": { - "human": "{quantity} Zentimeter" + "human": "{quantity} Zentimeter", + "humanSingular": "ein Zentimeter" }, "2": { "human": "{quantity} Millimeter", @@ -9605,12 +9736,42 @@ "humanShort": "{quantity} mph" } } + }, + "5": { + "applicableUnits": { + "0": { + "human": "{quantity} Minuten", + "humanSingular": "eine Minute" + }, + "1": { + "human": "{quantity} Stunden", + "humanSingular": "eine Stunde" + }, + "2": { + "human": "{quantity} Tage", + "humanSingular": "ein Tag" + } + } } } }, "usersettings": { "description": "Eine spezielle Ebene, die nicht für die Darstellung auf einer Karte gedacht ist, sondern für die Festlegung von Benutzereinstellungen verwendet wird", "tagRenderings": { + "a11y-features": { + "mappings": { + "0": { + "then": "Barrierefrei-Modus aktivieren, wenn Pfeiltasten zum Navigieren in der Karte verwendet werden" + }, + "1": { + "then": "Barrierefrei-Modus immer aktivieren" + }, + "2": { + "then": "Barrierefrei-Modus niemals aktivieren" + } + }, + "question": "Welche Barrierefrei-Funktionen sollen angewendet werden?" + }, "all-questions-at-once": { "mappings": { "0": { @@ -9685,7 +9846,7 @@ "1": { "then": { "special": { - "text": "Du hast {_unreadMessages}
Öffne Deinen Posteingang" + "text": "Sie haben {_unreadMessages} Nachrichten
Posteingang öffnen" } } } @@ -9694,7 +9855,7 @@ "language_picker": { "mappings": { "0": { - "then": "Die Sprache wurde über einen URL-Parameter gesetzt und kann nicht vom Benutzer eingestellt werden.²" + "then": "Die Sprache wurde über einen URL-Parameter gesetzt und kann nicht vom Benutzer eingestellt werden." } } }, From f1e4d36e6f516fd22e62a0422b6aeef6b16d7216 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 21:14:00 +0000 Subject: [PATCH 049/195] Translated using Weblate (English) Currently translated at 100.0% (3182 of 3182 strings) Translation: MapComplete/Layer translations Translate-URL: https://hosted.weblate.org/projects/mapcomplete/layers/en/ --- langs/layers/en.json | 754 +++++++++++++++++++++---------------------- 1 file changed, 377 insertions(+), 377 deletions(-) diff --git a/langs/layers/en.json b/langs/layers/en.json index 688ca1679..96aaac477 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -35,6 +35,16 @@ "1": { "title": "a freestanding poster box" }, + "10": { + "description": "Used for advertising signs, neon signs, logos & institutional entrance signs", + "title": "a sign" + }, + "11": { + "title": "a sculpture" + }, + "12": { + "title": "a wall painting" + }, "2": { "title": "a poster box mounted on a wall" }, @@ -61,16 +71,6 @@ }, "9": { "title": "a totem" - }, - "10": { - "description": "Used for advertising signs, neon signs, logos & institutional entrance signs", - "title": "a sign" - }, - "11": { - "title": "a sculpture" - }, - "12": { - "title": "a wall painting" } }, "tagRenderings": { @@ -165,6 +165,9 @@ "1": { "then": "This is a board" }, + "10": { + "then": "This is a wall painting" + }, "2": { "then": "This is a column" }, @@ -188,9 +191,6 @@ }, "9": { "then": "This is a totem" - }, - "10": { - "then": "This is a wall painting" } }, "question": "Which type of advertising feature is this?", @@ -205,6 +205,9 @@ "1": { "then": "Board" }, + "10": { + "then": "Wall painting" + }, "2": { "then": "Poster Box" }, @@ -228,9 +231,6 @@ }, "9": { "then": "Totem" - }, - "10": { - "then": "Wall painting" } } } @@ -353,6 +353,15 @@ "1": { "then": "Mural" }, + "10": { + "then": "Azulejo (Spanish decorative tilework)" + }, + "11": { + "then": "Tilework" + }, + "12": { + "then": "Woodcarving" + }, "2": { "then": "Painting" }, @@ -376,15 +385,6 @@ }, "9": { "then": "Relief" - }, - "10": { - "then": "Azulejo (Spanish decorative tilework)" - }, - "11": { - "then": "Tilework" - }, - "12": { - "then": "Woodcarving" } }, "question": "What is the type of this artwork?", @@ -1942,6 +1942,27 @@ "1": { "question": "Has a
Schuko wall plug without ground pin (CEE7/4 type F)
connector" }, + "10": { + "question": "Has a
Type 2 with cable (mennekes)
connector" + }, + "11": { + "question": "Has a
Tesla Supercharger CCS (a branded type2_css)
connector" + }, + "12": { + "question": "Has a
Tesla Supercharger (destination)
connector" + }, + "13": { + "question": "Has a
Tesla supercharger (destination) (A Type 2 with cable branded as tesla)
connector" + }, + "14": { + "question": "Has a
USB to charge phones and small electronics
connector" + }, + "15": { + "question": "Has a
Bosch Active Connect with 3 pins and cable
connector" + }, + "16": { + "question": "Has a
Bosch Active Connect with 5 pins and cable
connector" + }, "2": { "question": "Has a
European wall plug with ground pin (CEE7/4 type E)
connector" }, @@ -1965,27 +1986,6 @@ }, "9": { "question": "Has a
Type 2 CCS (mennekes)
connector" - }, - "10": { - "question": "Has a
Type 2 with cable (mennekes)
connector" - }, - "11": { - "question": "Has a
Tesla Supercharger CCS (a branded type2_css)
connector" - }, - "12": { - "question": "Has a
Tesla Supercharger (destination)
connector" - }, - "13": { - "question": "Has a
Tesla supercharger (destination) (A Type 2 with cable branded as tesla)
connector" - }, - "14": { - "question": "Has a
USB to charge phones and small electronics
connector" - }, - "15": { - "question": "Has a
Bosch Active Connect with 3 pins and cable
connector" - }, - "16": { - "question": "Has a
Bosch Active Connect with 5 pins and cable
connector" } } } @@ -2041,30 +2041,6 @@ "1": { "then": "Schuko wall plug without ground pin (CEE7/4 type F)" }, - "2": { - "then": "European wall plug with ground pin (CEE7/4 type E)" - }, - "3": { - "then": "European wall plug with ground pin (CEE7/4 type E)" - }, - "4": { - "then": "Chademo" - }, - "5": { - "then": "Chademo" - }, - "6": { - "then": "Type 1 with cable (J1772)" - }, - "7": { - "then": "Type 1 with cable (J1772)" - }, - "8": { - "then": "Type 1 without cable (J1772)" - }, - "9": { - "then": "Type 1 without cable (J1772)" - }, "10": { "then": "Type 1 CCS (aka Type 1 Combo)" }, @@ -2095,6 +2071,9 @@ "19": { "then": "Type 2 with cable (mennekes)" }, + "2": { + "then": "European wall plug with ground pin (CEE7/4 type E)" + }, "20": { "then": "Tesla Supercharger CCS (a branded type2_css)" }, @@ -2125,11 +2104,32 @@ "29": { "then": "Bosch Active Connect with 3 pins and cable" }, + "3": { + "then": "European wall plug with ground pin (CEE7/4 type E)" + }, "30": { "then": "Bosch Active Connect with 5 pins and cable" }, "31": { "then": "Bosch Active Connect with 5 pins and cable" + }, + "4": { + "then": "Chademo" + }, + "5": { + "then": "Chademo" + }, + "6": { + "then": "Type 1 with cable (J1772)" + }, + "7": { + "then": "Type 1 with cable (J1772)" + }, + "8": { + "then": "Type 1 without cable (J1772)" + }, + "9": { + "then": "Type 1 without cable (J1772)" } }, "question": "Which charging connections are available here?" @@ -2323,6 +2323,24 @@ "1": { "2": "European wall plug with ground pin (CEE7/4 type E)" }, + "10": { + "2": "Tesla Supercharger CCS (a branded type2_css)" + }, + "11": { + "2": "Tesla Supercharger (destination)" + }, + "12": { + "2": "Tesla supercharger (destination) (A Type 2 with cable branded as tesla)" + }, + "13": { + "2": "USB to charge phones and small electronics" + }, + "14": { + "2": "Bosch Active Connect with 3 pins and cable" + }, + "15": { + "2": "Bosch Active Connect with 5 pins and cable" + }, "2": { "2": "Chademo" }, @@ -2346,24 +2364,6 @@ }, "9": { "2": "Type 2 with cable (mennekes)" - }, - "10": { - "2": "Tesla Supercharger CCS (a branded type2_css)" - }, - "11": { - "2": "Tesla Supercharger (destination)" - }, - "12": { - "2": "Tesla supercharger (destination) (A Type 2 with cable branded as tesla)" - }, - "13": { - "2": "USB to charge phones and small electronics" - }, - "14": { - "2": "Bosch Active Connect with 3 pins and cable" - }, - "15": { - "2": "Bosch Active Connect with 5 pins and cable" } } } @@ -3141,6 +3141,15 @@ "1": { "then": "This cycleway is paved" }, + "10": { + "then": "This cycleway is made of fine gravel" + }, + "11": { + "then": "This cycleway is made of pebblestone" + }, + "12": { + "then": "This cycleway is made from raw ground" + }, "2": { "then": "This cycleway is made of asphalt" }, @@ -3164,15 +3173,6 @@ }, "9": { "then": "This cycleway is made of gravel" - }, - "10": { - "then": "This cycleway is made of fine gravel" - }, - "11": { - "then": "This cycleway is made of pebblestone" - }, - "12": { - "then": "This cycleway is made from raw ground" } }, "question": "What is the surface of the cycleway made from?", @@ -3221,6 +3221,15 @@ "1": { "then": "This cycleway is paved" }, + "10": { + "then": "This cycleway is made of fine gravel" + }, + "11": { + "then": "This cycleway is made of pebblestone" + }, + "12": { + "then": "This cycleway is made from raw ground" + }, "2": { "then": "This cycleway is made of asphalt" }, @@ -3244,15 +3253,6 @@ }, "9": { "then": "This cycleway is made of gravel" - }, - "10": { - "then": "This cycleway is made of fine gravel" - }, - "11": { - "then": "This cycleway is made of pebblestone" - }, - "12": { - "then": "This cycleway is made from raw ground" } }, "question": "What is the surface of the street made from?", @@ -4207,6 +4207,54 @@ } } }, + "10": { + "options": { + "0": { + "question": "No preference towards dogs" + }, + "1": { + "question": "Dogs allowed" + }, + "2": { + "question": "No dogs allowed" + } + } + }, + "11": { + "options": { + "0": { + "question": "Offers internet" + } + } + }, + "12": { + "options": { + "0": { + "question": "Offers electricity" + } + } + }, + "13": { + "options": { + "0": { + "question": "Has a sugar-free offering" + } + } + }, + "14": { + "options": { + "0": { + "question": "Has a gluten free offering" + } + } + }, + "15": { + "options": { + "0": { + "question": "Has a lactose free offering" + } + } + }, "2": { "options": { "0": { @@ -4277,54 +4325,6 @@ "question": "Free to use" } } - }, - "10": { - "options": { - "0": { - "question": "No preference towards dogs" - }, - "1": { - "question": "Dogs allowed" - }, - "2": { - "question": "No dogs allowed" - } - } - }, - "11": { - "options": { - "0": { - "question": "Offers internet" - } - } - }, - "12": { - "options": { - "0": { - "question": "Offers electricity" - } - } - }, - "13": { - "options": { - "0": { - "question": "Has a sugar-free offering" - } - } - }, - "14": { - "options": { - "0": { - "question": "Has a gluten free offering" - } - } - }, - "15": { - "options": { - "0": { - "question": "Has a lactose free offering" - } - } } } }, @@ -4444,30 +4444,6 @@ "1": { "then": "This fitness station has a sign with instructions for a specific exercise." }, - "2": { - "then": "This fitness station has a facility for sit-ups." - }, - "3": { - "then": "This fitness station has a facility for push-ups. Usually consists of one or more low horizontal bars." - }, - "4": { - "then": "This fitness station has bars for stretching." - }, - "5": { - "then": "This fitness station has a station for making hyperextensions." - }, - "6": { - "then": "This fitness station has rings for gymnastic exercises." - }, - "7": { - "then": "This fitness station has a horizontal ladder, also known as monkey bars." - }, - "8": { - "then": "This fitness station has wall bars to climb on." - }, - "9": { - "then": "This fitness station has posts for performing slalom exercises." - }, "10": { "then": "This fitness station has stepping stones." }, @@ -4498,6 +4474,9 @@ "19": { "then": "This fitness station has battling ropes." }, + "2": { + "then": "This fitness station has a facility for sit-ups." + }, "20": { "then": "This fitness station has a stationary bicycle." }, @@ -4512,6 +4491,27 @@ }, "24": { "then": "This fitness station has a slackline." + }, + "3": { + "then": "This fitness station has a facility for push-ups. Usually consists of one or more low horizontal bars." + }, + "4": { + "then": "This fitness station has bars for stretching." + }, + "5": { + "then": "This fitness station has a station for making hyperextensions." + }, + "6": { + "then": "This fitness station has rings for gymnastic exercises." + }, + "7": { + "then": "This fitness station has a horizontal ladder, also known as monkey bars." + }, + "8": { + "then": "This fitness station has wall bars to climb on." + }, + "9": { + "then": "This fitness station has posts for performing slalom exercises." } }, "question": "What kind of equipment does this fitness station have?" @@ -4631,6 +4631,21 @@ "1": { "then": "This is a friture" }, + "10": { + "then": "Chinese dishes are served here" + }, + "11": { + "then": "Greek dishes are served here" + }, + "12": { + "then": "Indian dishes are served here" + }, + "13": { + "then": "Turkish dishes are served here" + }, + "14": { + "then": "Thai dishes are served here" + }, "2": { "then": "Mainly serves pasta" }, @@ -4654,21 +4669,6 @@ }, "9": { "then": "French dishes are served here" - }, - "10": { - "then": "Chinese dishes are served here" - }, - "11": { - "then": "Greek dishes are served here" - }, - "12": { - "then": "Indian dishes are served here" - }, - "13": { - "then": "Turkish dishes are served here" - }, - "14": { - "then": "Thai dishes are served here" } }, "question": "What kind of food is served here?", @@ -5360,30 +5360,6 @@ "1": { "then": "This is a auditorium" }, - "2": { - "then": "This is a bedroom" - }, - "3": { - "then": "This is a chapel" - }, - "4": { - "then": "This is a classroom" - }, - "5": { - "then": "This is a classroom" - }, - "6": { - "then": "This is a computer room" - }, - "7": { - "then": "This is a conference room" - }, - "8": { - "then": "This is a crypt" - }, - "9": { - "then": "This is a kitchen" - }, "10": { "then": "This is a laboratory" }, @@ -5414,6 +5390,9 @@ "19": { "then": "This is a storage room" }, + "2": { + "then": "This is a bedroom" + }, "20": { "then": "This is a technical room" }, @@ -5422,6 +5401,27 @@ }, "22": { "then": "This is a waiting room" + }, + "3": { + "then": "This is a chapel" + }, + "4": { + "then": "This is a classroom" + }, + "5": { + "then": "This is a classroom" + }, + "6": { + "then": "This is a computer room" + }, + "7": { + "then": "This is a conference room" + }, + "8": { + "then": "This is a crypt" + }, + "9": { + "then": "This is a kitchen" } }, "question": "What type of room is this?" @@ -6048,6 +6048,19 @@ } } }, + "10": { + "options": { + "0": { + "question": "All Notes" + }, + "1": { + "question": "Hide import notes" + }, + "2": { + "question": "Show only import Notes" + } + } + }, "2": { "options": { "0": { @@ -6103,19 +6116,6 @@ "question": "Only show open notes" } } - }, - "10": { - "options": { - "0": { - "question": "All Notes" - }, - "1": { - "question": "Hide import notes" - }, - "2": { - "question": "Show only import Notes" - } - } } }, "name": "OpenStreetMap notes", @@ -6440,6 +6440,21 @@ "1": { "then": "This is a normal parking space." }, + "10": { + "then": "This is a parking space reserved for parents with children." + }, + "11": { + "then": "This is a parking space reserved for staff." + }, + "12": { + "then": "This is a parking space reserved for taxis." + }, + "13": { + "then": "This is a parking space reserved for vehicles towing a trailer." + }, + "14": { + "then": "This is a parking space reserved for car sharing." + }, "2": { "then": "This is a disabled parking space." }, @@ -6463,21 +6478,6 @@ }, "9": { "then": "This is parking space reserved for motorcycles." - }, - "10": { - "then": "This is a parking space reserved for parents with children." - }, - "11": { - "then": "This is a parking space reserved for staff." - }, - "12": { - "then": "This is a parking space reserved for taxis." - }, - "13": { - "then": "This is a parking space reserved for vehicles towing a trailer." - }, - "14": { - "then": "This is a parking space reserved for car sharing." } }, "question": "What kind of parking space is this?" @@ -7075,6 +7075,21 @@ "1": { "then": "2 cent coins are accepted" }, + "10": { + "then": "20 centimes coins are accepted" + }, + "11": { + "then": "½ franc coins are accepted" + }, + "12": { + "then": "1 franc coins are accepted" + }, + "13": { + "then": "2 francs coins are accepted" + }, + "14": { + "then": "5 francs coins are accepted" + }, "2": { "then": "5 cent coins are accepted" }, @@ -7098,21 +7113,6 @@ }, "9": { "then": "10 centimes coins are accepted" - }, - "10": { - "then": "20 centimes coins are accepted" - }, - "11": { - "then": "½ franc coins are accepted" - }, - "12": { - "then": "1 franc coins are accepted" - }, - "13": { - "then": "2 francs coins are accepted" - }, - "14": { - "then": "5 francs coins are accepted" } }, "question": "What coins can you use to pay here?" @@ -7125,6 +7125,15 @@ "1": { "then": "10 euro notes are accepted" }, + "10": { + "then": "100 francs notes are accepted" + }, + "11": { + "then": "200 francs notes are accepted" + }, + "12": { + "then": "1000 francs notes are accepted" + }, "2": { "then": "20 euro notes are accepted" }, @@ -7148,15 +7157,6 @@ }, "9": { "then": "50 francs notes are accepted" - }, - "10": { - "then": "100 francs notes are accepted" - }, - "11": { - "then": "200 francs notes are accepted" - }, - "12": { - "then": "1000 francs notes are accepted" } }, "question": "what notes can you use to pay here?" @@ -7610,30 +7610,6 @@ "1": { "question": "Recycling of batteries" }, - "2": { - "question": "Recycling of beverage cartons" - }, - "3": { - "question": "Recycling of cans" - }, - "4": { - "question": "Recycling of clothes" - }, - "5": { - "question": "Recycling of cooking oil" - }, - "6": { - "question": "Recycling of engine oil" - }, - "7": { - "question": "Recycling of fluorescent tubes" - }, - "8": { - "question": "Recycling of green waste" - }, - "9": { - "question": "Recycling of glass bottles" - }, "10": { "question": "Recycling of glass" }, @@ -7664,11 +7640,35 @@ "19": { "question": "Recycling of residual waste" }, + "2": { + "question": "Recycling of beverage cartons" + }, "20": { "question": "Recycling of printer cartridges" }, "21": { "question": "Recycling of bicycles" + }, + "3": { + "question": "Recycling of cans" + }, + "4": { + "question": "Recycling of clothes" + }, + "5": { + "question": "Recycling of cooking oil" + }, + "6": { + "question": "Recycling of engine oil" + }, + "7": { + "question": "Recycling of fluorescent tubes" + }, + "8": { + "question": "Recycling of green waste" + }, + "9": { + "question": "Recycling of glass bottles" } } }, @@ -7736,30 +7736,6 @@ "1": { "then": "Beverage cartons can be recycled here" }, - "2": { - "then": "Cans can be recycled here" - }, - "3": { - "then": "Clothes can be recycled here" - }, - "4": { - "then": "Cooking oil can be recycled here" - }, - "5": { - "then": "Engine oil can be recycled here" - }, - "6": { - "then": "Fluorescent tubes can be recycled here" - }, - "7": { - "then": "Green waste can be recycled here" - }, - "8": { - "then": "Organic waste can be recycled here" - }, - "9": { - "then": "Glass bottles can be recycled here" - }, "10": { "then": "Glass can be recycled here" }, @@ -7790,6 +7766,9 @@ "19": { "then": "Shoes can be recycled here" }, + "2": { + "then": "Cans can be recycled here" + }, "20": { "then": "Small electrical appliances can be recycled here" }, @@ -7804,6 +7783,27 @@ }, "24": { "then": "Bicycles can be recycled here" + }, + "3": { + "then": "Clothes can be recycled here" + }, + "4": { + "then": "Cooking oil can be recycled here" + }, + "5": { + "then": "Engine oil can be recycled here" + }, + "6": { + "then": "Fluorescent tubes can be recycled here" + }, + "7": { + "then": "Green waste can be recycled here" + }, + "8": { + "then": "Organic waste can be recycled here" + }, + "9": { + "then": "Glass bottles can be recycled here" } }, "question": "What can be recycled here?" @@ -8711,6 +8711,12 @@ "1": { "then": "This lamp uses LEDs" }, + "10": { + "then": "This lamp uses high pressure sodium lamps (orange with white)" + }, + "11": { + "then": "This lamp is lit using gas" + }, "2": { "then": "This lamp uses incandescent lighting" }, @@ -8734,12 +8740,6 @@ }, "9": { "then": "This lamp uses low pressure sodium lamps (monochrome orange)" - }, - "10": { - "then": "This lamp uses high pressure sodium lamps (orange with white)" - }, - "11": { - "then": "This lamp is lit using gas" } }, "question": "What kind of lighting does this lamp use?" @@ -9105,20 +9105,20 @@ "toilet-changing_table:location": { "mappings": { "0": { - "then": "The changing table is in the toilet for women. " + "then": "A changing table is in the toilet for women" }, "1": { - "then": "The changing table is in the toilet for men. " + "then": "A changing table is in the toilet for men" }, "2": { - "then": "The changing table is in the toilet for wheelchair users. " + "then": "A changing table is in the toilet for wheelchair users" }, "3": { - "then": "The changing table is in a dedicated room. " + "then": "A changing table is in a dedicated room" } }, "question": "Where is the changing table located?", - "render": "The changing table is located at {changing_table:location}" + "render": "A changing table is located at {changing_table:location}" }, "toilet-charge": { "freeform": { @@ -9989,30 +9989,6 @@ "1": { "question": "Sale of drinks" }, - "2": { - "question": "Sale of sweets" - }, - "3": { - "question": "Sale of food" - }, - "4": { - "question": "Sale of cigarettes" - }, - "5": { - "question": "Sale of condoms" - }, - "6": { - "question": "Sale of coffee" - }, - "7": { - "question": "Sale of water" - }, - "8": { - "question": "Sale of newspapers" - }, - "9": { - "question": "Sale of bicycle inner tubes" - }, "10": { "question": "Sale of milk" }, @@ -10043,6 +10019,9 @@ "19": { "question": "Sale of flowers" }, + "2": { + "question": "Sale of sweets" + }, "20": { "question": "Sale of parking tickets" }, @@ -10066,6 +10045,27 @@ }, "27": { "question": "Sale of bicycle locks" + }, + "3": { + "question": "Sale of food" + }, + "4": { + "question": "Sale of cigarettes" + }, + "5": { + "question": "Sale of condoms" + }, + "6": { + "question": "Sale of coffee" + }, + "7": { + "question": "Sale of water" + }, + "8": { + "question": "Sale of newspapers" + }, + "9": { + "question": "Sale of bicycle inner tubes" } } } @@ -10112,30 +10112,6 @@ "1": { "then": "Sweets are sold" }, - "2": { - "then": "Food is sold" - }, - "3": { - "then": "Cigarettes are sold" - }, - "4": { - "then": "Condoms are sold" - }, - "5": { - "then": "Coffee is sold" - }, - "6": { - "then": "Drinking water is sold" - }, - "7": { - "then": "Newspapers are sold" - }, - "8": { - "then": "Bicycle inner tubes are sold" - }, - "9": { - "then": "Milk is sold" - }, "10": { "then": "Bread is sold" }, @@ -10166,6 +10142,9 @@ "19": { "then": "Parking tickets are sold" }, + "2": { + "then": "Food is sold" + }, "20": { "then": "Pressed pennies are sold" }, @@ -10186,6 +10165,27 @@ }, "26": { "then": "Bicycle locks are sold" + }, + "3": { + "then": "Cigarettes are sold" + }, + "4": { + "then": "Condoms are sold" + }, + "5": { + "then": "Coffee is sold" + }, + "6": { + "then": "Drinking water is sold" + }, + "7": { + "then": "Newspapers are sold" + }, + "8": { + "then": "Bicycle inner tubes are sold" + }, + "9": { + "then": "Milk is sold" } }, "question": "What does this vending machine sell?", @@ -10477,4 +10477,4 @@ "render": "wind turbine" } } -} \ No newline at end of file +} From a702fbe293a893e6508877050529d77dfb3ae099 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 16 Jan 2024 21:14:06 +0000 Subject: [PATCH 050/195] Translated using Weblate (Dutch) Currently translated at 86.8% (2764 of 3182 strings) Translation: MapComplete/Layer translations Translate-URL: https://hosted.weblate.org/projects/mapcomplete/layers/nl/ --- langs/layers/nl.json | 542 +++++++++++++++++++++---------------------- 1 file changed, 271 insertions(+), 271 deletions(-) diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 3d513ebcc..79f1d1a82 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -27,6 +27,9 @@ "advertising": { "name": "Reclame", "presets": { + "12": { + "title": "een muurschildering" + }, "3": { "description": "Een klein uithangbord voor buurtadvertenties, meestal gericht op voetgangers", "title": "een uithangbord" @@ -47,9 +50,6 @@ "8": { "description": "Een stuk groot, weerbestendig textiel met opgedrukte reclameboodschap die permanent aan de muur hangt", "title": "een spandoek" - }, - "12": { - "title": "een muurschildering" } }, "tagRenderings": { @@ -107,6 +107,9 @@ }, "title": { "mappings": { + "10": { + "then": "Muurschildering" + }, "3": { "then": "Aanplakzuil" }, @@ -124,9 +127,6 @@ }, "9": { "then": "Aanplakzuil" - }, - "10": { - "then": "Muurschildering" } } } @@ -208,6 +208,15 @@ "1": { "then": "Muurschildering" }, + "10": { + "then": "Azulejo (Spaanse siertegels)" + }, + "11": { + "then": "Tegelwerk" + }, + "12": { + "then": "Houtsculptuur" + }, "2": { "then": "Schilderij" }, @@ -231,15 +240,6 @@ }, "9": { "then": "Reliëf" - }, - "10": { - "then": "Azulejo (Spaanse siertegels)" - }, - "11": { - "then": "Tegelwerk" - }, - "12": { - "then": "Houtsculptuur" } }, "question": "Wat voor soort kunstwerk is dit?", @@ -1791,6 +1791,27 @@ "1": { "question": "Heeft een
Schuko stekker zonder aardingspin (CEE7/4 type F)
" }, + "10": { + "question": "Heeft een
Type 2 met kabel (J1772)
" + }, + "11": { + "question": "Heeft een
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "12": { + "question": "Heeft een
Tesla Supercharger (destination)
" + }, + "13": { + "question": "Heeft een
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "14": { + "question": "Heeft een
USB om GSMs en kleine electronica op te laden
" + }, + "15": { + "question": "Heeft een
Bosch Active Connect met 3 pinnen aan een kabel
" + }, + "16": { + "question": "Heeft een
Bosch Active Connect met 5 pinnen aan een kabel
" + }, "2": { "question": "Heeft een
Europese stekker met aardingspin (CEE7/4 type E)
" }, @@ -1814,27 +1835,6 @@ }, "9": { "question": "Heeft een
Type 2 CCS (mennekes)
" - }, - "10": { - "question": "Heeft een
Type 2 met kabel (J1772)
" - }, - "11": { - "question": "Heeft een
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "12": { - "question": "Heeft een
Tesla Supercharger (destination)
" - }, - "13": { - "question": "Heeft een
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "14": { - "question": "Heeft een
USB om GSMs en kleine electronica op te laden
" - }, - "15": { - "question": "Heeft een
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "16": { - "question": "Heeft een
Bosch Active Connect met 5 pinnen aan een kabel
" } } } @@ -1890,30 +1890,6 @@ "1": { "then": "Schuko stekker zonder aardingspin (CEE7/4 type F)" }, - "2": { - "then": "Europese stekker met aardingspin (CEE7/4 type E)" - }, - "3": { - "then": "Europese stekker met aardingspin (CEE7/4 type E)" - }, - "4": { - "then": "Chademo" - }, - "5": { - "then": "Chademo" - }, - "6": { - "then": "Type 1 met kabel (J1772)" - }, - "7": { - "then": "Type 1 met kabel (J1772)" - }, - "8": { - "then": "Type 1 zonder kabel (J1772)" - }, - "9": { - "then": "Type 1 zonder kabel (J1772)" - }, "10": { "then": "Type 1 CCS (ook gekend als Type 1 Combo)" }, @@ -1944,6 +1920,9 @@ "19": { "then": "Type 2 met kabel (J1772)" }, + "2": { + "then": "Europese stekker met aardingspin (CEE7/4 type E)" + }, "20": { "then": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" }, @@ -1974,11 +1953,32 @@ "29": { "then": "Bosch Active Connect met 3 pinnen aan een kabel" }, + "3": { + "then": "Europese stekker met aardingspin (CEE7/4 type E)" + }, "30": { "then": "Bosch Active Connect met 5 pinnen aan een kabel" }, "31": { "then": "Bosch Active Connect met 5 pinnen aan een kabel" + }, + "4": { + "then": "Chademo" + }, + "5": { + "then": "Chademo" + }, + "6": { + "then": "Type 1 met kabel (J1772)" + }, + "7": { + "then": "Type 1 met kabel (J1772)" + }, + "8": { + "then": "Type 1 zonder kabel (J1772)" + }, + "9": { + "then": "Type 1 zonder kabel (J1772)" } }, "question": "Welke aansluitingen zijn hier beschikbaar?" @@ -2172,6 +2172,24 @@ "1": { "2": "Europese stekker met aardingspin (CEE7/4 type E)" }, + "10": { + "2": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" + }, + "11": { + "2": "Tesla Supercharger (destination)" + }, + "12": { + "2": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)" + }, + "13": { + "2": "USB om GSMs en kleine electronica op te laden" + }, + "14": { + "2": "Bosch Active Connect met 3 pinnen aan een kabel" + }, + "15": { + "2": "Bosch Active Connect met 5 pinnen aan een kabel" + }, "2": { "2": "Chademo" }, @@ -2195,24 +2213,6 @@ }, "9": { "2": "Type 2 met kabel (J1772)" - }, - "10": { - "2": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" - }, - "11": { - "2": "Tesla Supercharger (destination)" - }, - "12": { - "2": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)" - }, - "13": { - "2": "USB om GSMs en kleine electronica op te laden" - }, - "14": { - "2": "Bosch Active Connect met 3 pinnen aan een kabel" - }, - "15": { - "2": "Bosch Active Connect met 5 pinnen aan een kabel" } } } @@ -2978,6 +2978,15 @@ "1": { "then": "Dit fietspad is geplaveid" }, + "10": { + "then": "Dit fietspad is gemaakt van fijn grind" + }, + "11": { + "then": "Dit fietspad is gemaakt van kiezelsteentjes" + }, + "12": { + "then": "Dit fietspad is gemaakt van aarde" + }, "2": { "then": "Dit fietspad is gemaakt van asfalt" }, @@ -3001,15 +3010,6 @@ }, "9": { "then": "Dit fietspad is gemaakt van grind" - }, - "10": { - "then": "Dit fietspad is gemaakt van fijn grind" - }, - "11": { - "then": "Dit fietspad is gemaakt van kiezelsteentjes" - }, - "12": { - "then": "Dit fietspad is gemaakt van aarde" } }, "question": "Waaruit is het oppervlak van het fietspad van gemaakt?", @@ -3058,6 +3058,15 @@ "1": { "then": "Dit fietspad is geplaveid" }, + "10": { + "then": "Dit fietspad is gemaakt van fijn grind" + }, + "11": { + "then": "Dit fietspad is gemaakt van kiezelsteentjes" + }, + "12": { + "then": "Dit fietspad is gemaakt van aarde" + }, "2": { "then": "Dit fietspad is gemaakt van asfalt" }, @@ -3081,15 +3090,6 @@ }, "9": { "then": "Dit fietspad is gemaakt van grind" - }, - "10": { - "then": "Dit fietspad is gemaakt van fijn grind" - }, - "11": { - "then": "Dit fietspad is gemaakt van kiezelsteentjes" - }, - "12": { - "then": "Dit fietspad is gemaakt van aarde" } }, "question": "Waaruit is het oppervlak van de straat gemaakt?", @@ -4138,6 +4138,21 @@ "1": { "then": "Dit is een frituur" }, + "10": { + "then": "Dit is een Chinees restaurant" + }, + "11": { + "then": "Dit is een Grieks restaurant" + }, + "12": { + "then": "Dit is een Indisch restaurant" + }, + "13": { + "then": "Dit is een Turks restaurant (dat meer dan enkel kebab verkoopt)" + }, + "14": { + "then": "Dit is een Thaïs restaurant" + }, "2": { "then": "Dit is een pastazaak" }, @@ -4161,21 +4176,6 @@ }, "9": { "then": "Dit is een Frans restaurant" - }, - "10": { - "then": "Dit is een Chinees restaurant" - }, - "11": { - "then": "Dit is een Grieks restaurant" - }, - "12": { - "then": "Dit is een Indisch restaurant" - }, - "13": { - "then": "Dit is een Turks restaurant (dat meer dan enkel kebab verkoopt)" - }, - "14": { - "then": "Dit is een Thaïs restaurant" } }, "question": "Welk soort gerechten worden hier geserveerd?", @@ -5346,6 +5346,19 @@ } } }, + "10": { + "options": { + "0": { + "question": "Alle Notes" + }, + "1": { + "question": "Verberg import Notes" + }, + "2": { + "question": "Toon enkel import Notes" + } + } + }, "2": { "options": { "0": { @@ -5401,19 +5414,6 @@ "question": "Toon enkel open Notes" } } - }, - "10": { - "options": { - "0": { - "question": "Alle Notes" - }, - "1": { - "question": "Verberg import Notes" - }, - "2": { - "question": "Toon enkel import Notes" - } - } } }, "name": "OpenStreetMap Notes", @@ -5705,6 +5705,21 @@ "1": { "then": "Dit is een normale parkeerplek." }, + "10": { + "then": "Deze parkeerplek is gereserveerd voor ouders met kinderen." + }, + "11": { + "then": "Deze parkeerplek is gereserveerd voor personeel." + }, + "12": { + "then": "Deze parkeerplek is gereserveerd voor taxis." + }, + "13": { + "then": "Deze parkeerplek is gereserveerd voor voertuigen met een aanhanger." + }, + "14": { + "then": "Deze parkeerplek is gereserveerd voor autodelen." + }, "2": { "then": "Dit is een gehandicaptenparkeerplaats." }, @@ -5728,21 +5743,6 @@ }, "9": { "then": "Deze parkeerplek is gereserveerd voor motoren." - }, - "10": { - "then": "Deze parkeerplek is gereserveerd voor ouders met kinderen." - }, - "11": { - "then": "Deze parkeerplek is gereserveerd voor personeel." - }, - "12": { - "then": "Deze parkeerplek is gereserveerd voor taxis." - }, - "13": { - "then": "Deze parkeerplek is gereserveerd voor voertuigen met een aanhanger." - }, - "14": { - "then": "Deze parkeerplek is gereserveerd voor autodelen." } }, "question": "Wat voor parkeerplek is dit?" @@ -6309,6 +6309,21 @@ "1": { "then": "Munten van 2 cent worden geaccepteerd" }, + "10": { + "then": "Munten van 20 rappen worden geaccepteerd" + }, + "11": { + "then": "Munten van ½ frank worden geaccepteerd" + }, + "12": { + "then": "Munten van 1 frank worden geaccepteerd" + }, + "13": { + "then": "Munten van 2 frank worden geaccepteerd" + }, + "14": { + "then": "Munten van 5 frank worden geaccepteerd" + }, "2": { "then": "Munten van 5 cent worden geaccepteerd" }, @@ -6332,21 +6347,6 @@ }, "9": { "then": "Munten van 10 rappen worden geaccepteerd" - }, - "10": { - "then": "Munten van 20 rappen worden geaccepteerd" - }, - "11": { - "then": "Munten van ½ frank worden geaccepteerd" - }, - "12": { - "then": "Munten van 1 frank worden geaccepteerd" - }, - "13": { - "then": "Munten van 2 frank worden geaccepteerd" - }, - "14": { - "then": "Munten van 5 frank worden geaccepteerd" } }, "question": "Met welke munten kan je hier betalen?" @@ -6359,6 +6359,15 @@ "1": { "then": "Biljetten van 10 euro worden geaccepteerd" }, + "10": { + "then": "Biljetten van 100 frank worden geaccepteerd" + }, + "11": { + "then": "Biljetten van 200 frank worden geaccepteerd" + }, + "12": { + "then": "Biljetten van 1000 frank worden geaccepteerd" + }, "2": { "then": "Biljetten van 20 euro worden geaccepteerd" }, @@ -6382,15 +6391,6 @@ }, "9": { "then": "Biljetten van 50 frank worden geaccepteerd" - }, - "10": { - "then": "Biljetten van 100 frank worden geaccepteerd" - }, - "11": { - "then": "Biljetten van 200 frank worden geaccepteerd" - }, - "12": { - "then": "Biljetten van 1000 frank worden geaccepteerd" } }, "question": "Met welke bankbiljetten kan je hier betalen?" @@ -6709,30 +6709,6 @@ "1": { "question": "Recycling van batterijen" }, - "2": { - "question": "Recycling van drankpakken" - }, - "3": { - "question": "Recycling van blikken" - }, - "4": { - "question": "Recycling van kleding" - }, - "5": { - "question": "Recycling van frituurvet" - }, - "6": { - "question": "Recycling van motorolie" - }, - "7": { - "question": "Recycling van tl-buizen" - }, - "8": { - "question": "Recycling van groen afval" - }, - "9": { - "question": "Recycling van glazen flessen" - }, "10": { "question": "Recycling van glas" }, @@ -6763,11 +6739,35 @@ "19": { "question": "Recycling van restafval" }, + "2": { + "question": "Recycling van drankpakken" + }, "20": { "question": "Recycling van inktpatronen" }, "21": { "question": "Recycling van fietsen" + }, + "3": { + "question": "Recycling van blikken" + }, + "4": { + "question": "Recycling van kleding" + }, + "5": { + "question": "Recycling van frituurvet" + }, + "6": { + "question": "Recycling van motorolie" + }, + "7": { + "question": "Recycling van tl-buizen" + }, + "8": { + "question": "Recycling van groen afval" + }, + "9": { + "question": "Recycling van glazen flessen" } } }, @@ -6835,30 +6835,6 @@ "1": { "then": "Drankpakken kunnen hier gerecycled worden" }, - "2": { - "then": "Blikken kunnen hier gerecycled worden" - }, - "3": { - "then": "Kleren kunnen hier gerecycled worden" - }, - "4": { - "then": "Frituurvet kan hier gerecycled worden" - }, - "5": { - "then": "Motorolie kan hier gerecycled worden" - }, - "6": { - "then": "TL-buizen kunnen hier gerecycled worden" - }, - "7": { - "then": "Groen afval kan hier gerecycled worden" - }, - "8": { - "then": "Organisch afval kan hier gerecycled worden" - }, - "9": { - "then": "Glazen flessen kunnen hier gerecycled worden" - }, "10": { "then": "Glas kan hier gerecycled worden" }, @@ -6889,6 +6865,9 @@ "19": { "then": "Schoenen kunnen hier gerecycled worden" }, + "2": { + "then": "Blikken kunnen hier gerecycled worden" + }, "20": { "then": "Kleine elektrische apparaten kunnen hier gerecycled worden" }, @@ -6903,6 +6882,27 @@ }, "24": { "then": "Fietsen (en fietswrakken) kunnen hier gerecycled worden" + }, + "3": { + "then": "Kleren kunnen hier gerecycled worden" + }, + "4": { + "then": "Frituurvet kan hier gerecycled worden" + }, + "5": { + "then": "Motorolie kan hier gerecycled worden" + }, + "6": { + "then": "TL-buizen kunnen hier gerecycled worden" + }, + "7": { + "then": "Groen afval kan hier gerecycled worden" + }, + "8": { + "then": "Organisch afval kan hier gerecycled worden" + }, + "9": { + "then": "Glazen flessen kunnen hier gerecycled worden" } }, "question": "Wat kan hier gerecycled worden?" @@ -7624,6 +7624,12 @@ "1": { "then": "Deze lantaarn gebruikt LEDs" }, + "10": { + "then": "Deze lantaarn gebruikt hogedruknatriumlampen (oranje met wit)" + }, + "11": { + "then": "Deze lantaarn wordt verlicht met gas" + }, "2": { "then": "Deze lantaarn gebruikt gloeilampen" }, @@ -7647,12 +7653,6 @@ }, "9": { "then": "Deze lantaarn gebruikt lagedruknatriumlampen (monochroom oranje)" - }, - "10": { - "then": "Deze lantaarn gebruikt hogedruknatriumlampen (oranje met wit)" - }, - "11": { - "then": "Deze lantaarn wordt verlicht met gas" } }, "question": "Wat voor verlichting gebruikt deze lantaarn?" @@ -7965,20 +7965,20 @@ "toilet-changing_table:location": { "mappings": { "0": { - "then": "De luiertafel bevindt zich in de vrouwentoiletten " + "then": "Er bevindt zich een luiertafel in de vrouwentoiletten " }, "1": { - "then": "De luiertafel bevindt zich in de herentoiletten " + "then": "Er bevindt zich een luiertafel in de herentoiletten " }, "2": { - "then": "De luiertafel bevindt zich in de rolstoeltoegankelijke toilet " + "then": "Er bevindt zich een luiertafel in de rolstoeltoegankelijke toilet " }, "3": { - "then": "De luiertafel bevindt zich in een daartoe voorziene kamer " + "then": "Er bevindt zich een luiertafel in een daartoe voorziene kamer " } }, "question": "Waar bevindt de luiertafel zich?", - "render": "De luiertafel bevindt zich in {changing_table:location}" + "render": "Er bevindt zich een luiertafel in {changing_table:location}" }, "toilet-charge": { "freeform": { @@ -8796,30 +8796,6 @@ "1": { "question": "Verkoop van dranken" }, - "2": { - "question": "Verkoop van snoep" - }, - "3": { - "question": "Verkoop van eten" - }, - "4": { - "question": "Verkoop van sigaretten" - }, - "5": { - "question": "Verkoop van condooms" - }, - "6": { - "question": "Verkoop van koffie" - }, - "7": { - "question": "Verkoop van water" - }, - "8": { - "question": "Verkoop van kranten" - }, - "9": { - "question": "Verkoop van fietsbinnenbanden" - }, "10": { "question": "Verkoop van melk" }, @@ -8850,6 +8826,9 @@ "19": { "question": "Verkoop van bloemen" }, + "2": { + "question": "Verkoop van snoep" + }, "23": { "question": "Verkoop van fietslampjes" }, @@ -8864,6 +8843,27 @@ }, "27": { "question": "Verkoop van fietssloten" + }, + "3": { + "question": "Verkoop van eten" + }, + "4": { + "question": "Verkoop van sigaretten" + }, + "5": { + "question": "Verkoop van condooms" + }, + "6": { + "question": "Verkoop van koffie" + }, + "7": { + "question": "Verkoop van water" + }, + "8": { + "question": "Verkoop van kranten" + }, + "9": { + "question": "Verkoop van fietsbinnenbanden" } } } @@ -8904,30 +8904,6 @@ "1": { "then": "Snoep wordt verkocht" }, - "2": { - "then": "Eten wordt verkocht" - }, - "3": { - "then": "Sigaretten worden verkocht" - }, - "4": { - "then": "Condooms worden verkocht" - }, - "5": { - "then": "Koffie wordt verkocht" - }, - "6": { - "then": "Drinkwater wordt verkocht" - }, - "7": { - "then": "Kranten worden verkocht" - }, - "8": { - "then": "Binnenbanden voor fietsen worden verkocht" - }, - "9": { - "then": "Melk wordt verkocht" - }, "10": { "then": "Brood wordt verkocht" }, @@ -8958,6 +8934,9 @@ "19": { "then": "Parkeerkaarten worden verkocht" }, + "2": { + "then": "Eten wordt verkocht" + }, "21": { "then": "Openbaar vervoerkaartjes worden verkocht" }, @@ -8975,6 +8954,27 @@ }, "26": { "then": "Fietssloten worden verkocht" + }, + "3": { + "then": "Sigaretten worden verkocht" + }, + "4": { + "then": "Condooms worden verkocht" + }, + "5": { + "then": "Koffie wordt verkocht" + }, + "6": { + "then": "Drinkwater wordt verkocht" + }, + "7": { + "then": "Kranten worden verkocht" + }, + "8": { + "then": "Binnenbanden voor fietsen worden verkocht" + }, + "9": { + "then": "Melk wordt verkocht" } }, "question": "Wat verkoopt deze verkoopautomaat?", @@ -9267,4 +9267,4 @@ "render": "windturbine" } } -} \ No newline at end of file +} From acbf07ae325fb00db053ef8c992735aa580bc49b Mon Sep 17 00:00:00 2001 From: kjon Date: Thu, 18 Jan 2024 19:04:15 +0000 Subject: [PATCH 051/195] Translated using Weblate (German) Currently translated at 100.0% (3182 of 3182 strings) Translation: MapComplete/Layer translations Translate-URL: https://hosted.weblate.org/projects/mapcomplete/layers/de/ --- langs/layers/de.json | 754 +++++++++++++++++++++---------------------- 1 file changed, 377 insertions(+), 377 deletions(-) diff --git a/langs/layers/de.json b/langs/layers/de.json index 5b9aebd7b..56f18983c 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -35,6 +35,16 @@ "1": { "title": "eine freistehende Posterbox" }, + "10": { + "description": "Verwendet für Werbeschilder, Leuchtreklamen, Logos und institutionelle Eingangsschilder", + "title": "ein Schild" + }, + "11": { + "title": "eine Skulptur" + }, + "12": { + "title": "eine Wandmalerei" + }, "2": { "title": "eine wandmontierte Posterbox" }, @@ -61,16 +71,6 @@ }, "9": { "title": "ein Totem" - }, - "10": { - "description": "Verwendet für Werbeschilder, Leuchtreklamen, Logos und institutionelle Eingangsschilder", - "title": "ein Schild" - }, - "11": { - "title": "eine Skulptur" - }, - "12": { - "title": "eine Wandmalerei" } }, "tagRenderings": { @@ -165,6 +165,9 @@ "1": { "then": "Dies ist ein Brett" }, + "10": { + "then": "Dies ist eine Wandmalerei" + }, "2": { "then": "Dies ist eine Litfaßsäule" }, @@ -188,9 +191,6 @@ }, "9": { "then": "Dies ist ein Totem" - }, - "10": { - "then": "Dies ist eine Wandmalerei" } }, "question": "Welche Art von Werbung ist das?", @@ -205,6 +205,9 @@ "1": { "then": "Brett" }, + "10": { + "then": "Wandmalerei" + }, "2": { "then": "Posterbox" }, @@ -228,9 +231,6 @@ }, "9": { "then": "Totem" - }, - "10": { - "then": "Wandmalerei" } } } @@ -353,6 +353,15 @@ "1": { "then": "Wandbild" }, + "10": { + "then": "Azulejo (spanische dekorative Fliesenarbeit)" + }, + "11": { + "then": "Fliesenarbeit" + }, + "12": { + "then": "Holzschnitzerei" + }, "2": { "then": "Malerei" }, @@ -376,15 +385,6 @@ }, "9": { "then": "Relief" - }, - "10": { - "then": "Azulejo (spanische dekorative Fliesenarbeit)" - }, - "11": { - "then": "Fliesenarbeit" - }, - "12": { - "then": "Holzschnitzerei" } }, "question": "Um welche Art Kunstwerk handelt es sich?", @@ -1942,6 +1942,27 @@ "1": { "question": "Verfügt über einen
Schuko-Stecker ohne Erdungsstift (CEE7/4 Typ F)
" }, + "10": { + "question": "Hat einen
Typ 2 (Mennekes)
Anschluss mit Kabel" + }, + "11": { + "question": "Hat einen
Tesla Supercharger CCS (Typ 2 CSS vonTesla)
Anschluss" + }, + "12": { + "question": "Hat einen
Tesla Supercharger (Destination)
Anschluss" + }, + "13": { + "question": "Hat einen
Tesla Supercharger (Destination) (Typ 2 von Tesla)
Anschluss mit Kabel" + }, + "14": { + "question": "Hat einen
USB-Anschluss zum Aufladen von Telefonen und kleinen Elektrogeräten
" + }, + "15": { + "question": "Hat einen
Bosch Active Connect Anschluss mit 3 Pins
und Kabel" + }, + "16": { + "question": "Hat einen
Bosch Active Connect Anschluss mit 5 Pins
und Kabel" + }, "2": { "question": "Verfügt über einen
europäischen Netzstecker mit Erdungsstift (CEE7/4 Typ E)
Anschluss" }, @@ -1965,27 +1986,6 @@ }, "9": { "question": "Hat einen
Typ 2 CCS (Mennekes)
Anschluss" - }, - "10": { - "question": "Hat einen
Typ 2 (Mennekes)
Anschluss mit Kabel" - }, - "11": { - "question": "Hat einen
Tesla Supercharger CCS (Typ 2 CSS vonTesla)
Anschluss" - }, - "12": { - "question": "Hat einen
Tesla Supercharger (Destination)
Anschluss" - }, - "13": { - "question": "Hat einen
Tesla Supercharger (Destination) (Typ 2 von Tesla)
Anschluss mit Kabel" - }, - "14": { - "question": "Hat einen
USB-Anschluss zum Aufladen von Telefonen und kleinen Elektrogeräten
" - }, - "15": { - "question": "Hat einen
Bosch Active Connect Anschluss mit 3 Pins
und Kabel" - }, - "16": { - "question": "Hat einen
Bosch Active Connect Anschluss mit 5 Pins
und Kabel" } } } @@ -2041,30 +2041,6 @@ "1": { "then": "Schuko-Stecker ohne Erdungsstift (CEE7/4 Typ F)" }, - "2": { - "then": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" - }, - "3": { - "then": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" - }, - "4": { - "then": "Chademo-Anschluss" - }, - "5": { - "then": "Chademo-Anschluss" - }, - "6": { - "then": "Typ 1 mit Kabel (J1772)" - }, - "7": { - "then": "Typ 1 mit Kabel (J1772)" - }, - "8": { - "then": "Typ 1 ohne Kabel (J1772)" - }, - "9": { - "then": " Typ 1 ohne Kabel (J1772)" - }, "10": { "then": "Typ 1 CCS (Typ 1 Combo)" }, @@ -2095,6 +2071,9 @@ "19": { "then": "Typ 2 mit Kabel (mennekes)" }, + "2": { + "then": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" + }, "20": { "then": "Tesla Supercharger CCS (Typ 2 CSS von Tesla)" }, @@ -2125,11 +2104,32 @@ "29": { "then": " Bosch Active Connect mit 3 Pins und Kabel" }, + "3": { + "then": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" + }, "30": { "then": "Bosch Active Connect mit 5 Pins und Kabel" }, "31": { "then": " Bosch Active Connect mit 5 Pins und Kabel" + }, + "4": { + "then": "Chademo-Anschluss" + }, + "5": { + "then": "Chademo-Anschluss" + }, + "6": { + "then": "Typ 1 mit Kabel (J1772)" + }, + "7": { + "then": "Typ 1 mit Kabel (J1772)" + }, + "8": { + "then": "Typ 1 ohne Kabel (J1772)" + }, + "9": { + "then": " Typ 1 ohne Kabel (J1772)" } }, "question": "Welche Ladeanschlüsse gibt es hier?" @@ -2323,6 +2323,24 @@ "1": { "2": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" }, + "10": { + "2": "Tesla Supercharger CCS (Typ 2 CSS von Tesla)" + }, + "11": { + "2": "Tesla Supercharger (Destination)" + }, + "12": { + "2": "Tesla Supercharger (Destination) (Typ 2 mit Kabel von Tesla)" + }, + "13": { + "2": "USB zum Aufladen von Handys und kleinen Elektrogeräten" + }, + "14": { + "2": " Bosch Active Connect mit 3 Pins und Kabel" + }, + "15": { + "2": " Bosch Active Connect mit 5 Pins und Kabel" + }, "2": { "2": "Chademo-Stecker" }, @@ -2346,24 +2364,6 @@ }, "9": { "2": "Typ 2 mit Kabel (Mennekes)" - }, - "10": { - "2": "Tesla Supercharger CCS (Typ 2 CSS von Tesla)" - }, - "11": { - "2": "Tesla Supercharger (Destination)" - }, - "12": { - "2": "Tesla Supercharger (Destination) (Typ 2 mit Kabel von Tesla)" - }, - "13": { - "2": "USB zum Aufladen von Handys und kleinen Elektrogeräten" - }, - "14": { - "2": " Bosch Active Connect mit 3 Pins und Kabel" - }, - "15": { - "2": " Bosch Active Connect mit 5 Pins und Kabel" } } } @@ -3141,6 +3141,15 @@ "1": { "then": "Dieser Radweg hat einen festen Belag" }, + "10": { + "then": "Dieser Radweg besteht aus feinem Schotter" + }, + "11": { + "then": "Der Radweg ist aus Kies" + }, + "12": { + "then": "Dieser Radweg besteht aus Rohboden" + }, "2": { "then": "Der Radweg ist aus Asphalt" }, @@ -3164,15 +3173,6 @@ }, "9": { "then": "Der Radweg ist aus Schotter" - }, - "10": { - "then": "Dieser Radweg besteht aus feinem Schotter" - }, - "11": { - "then": "Der Radweg ist aus Kies" - }, - "12": { - "then": "Dieser Radweg besteht aus Rohboden" } }, "question": "Was ist der Belag dieses Radwegs?", @@ -3221,6 +3221,15 @@ "1": { "then": "Dieser Radweg hat einen festen Belag" }, + "10": { + "then": "Dieser Radweg besteht aus feinem Schotter" + }, + "11": { + "then": "Der Radweg ist aus Kies" + }, + "12": { + "then": "Dieser Radweg besteht aus Rohboden" + }, "2": { "then": "Der Radweg ist aus Asphalt" }, @@ -3244,15 +3253,6 @@ }, "9": { "then": "Der Radweg ist aus Schotter" - }, - "10": { - "then": "Dieser Radweg besteht aus feinem Schotter" - }, - "11": { - "then": "Der Radweg ist aus Kies" - }, - "12": { - "then": "Dieser Radweg besteht aus Rohboden" } }, "question": "Was ist der Belag dieser Straße?", @@ -4207,6 +4207,54 @@ } } }, + "10": { + "options": { + "0": { + "question": "Keine Bevorzugung von Hunden" + }, + "1": { + "question": "Hunde erlaubt" + }, + "2": { + "question": "Keine Hunde erlaubt" + } + } + }, + "11": { + "options": { + "0": { + "question": "Internetzugang vorhanden" + } + } + }, + "12": { + "options": { + "0": { + "question": "Stromanschluss vorhanden" + } + } + }, + "13": { + "options": { + "0": { + "question": "Hat zuckerfreie Angebote" + } + } + }, + "14": { + "options": { + "0": { + "question": "Hat glutenfreie Angebote" + } + } + }, + "15": { + "options": { + "0": { + "question": "Hat laktosefreie Angebote" + } + } + }, "2": { "options": { "0": { @@ -4277,54 +4325,6 @@ "question": "Nutzung kostenlos" } } - }, - "10": { - "options": { - "0": { - "question": "Keine Bevorzugung von Hunden" - }, - "1": { - "question": "Hunde erlaubt" - }, - "2": { - "question": "Keine Hunde erlaubt" - } - } - }, - "11": { - "options": { - "0": { - "question": "Internetzugang vorhanden" - } - } - }, - "12": { - "options": { - "0": { - "question": "Stromanschluss vorhanden" - } - } - }, - "13": { - "options": { - "0": { - "question": "Hat zuckerfreie Angebote" - } - } - }, - "14": { - "options": { - "0": { - "question": "Hat glutenfreie Angebote" - } - } - }, - "15": { - "options": { - "0": { - "question": "Hat laktosefreie Angebote" - } - } } } }, @@ -4444,30 +4444,6 @@ "1": { "then": "Die Fitness-Station hat ein Schild mit Anweisungen für eine bestimmte Übung." }, - "2": { - "then": "Die Fitness-Station hat eine Einrichtung für Sit-ups." - }, - "3": { - "then": "Die Fitness-Station hat eine Vorrichtung für Liegestütze. In der Regel eine oder mehrere niedrige Reckstangen." - }, - "4": { - "then": "Die Fitness-Station hat Stangen zum Dehnen." - }, - "5": { - "then": "Die Fitness-Station hat eine Vorrichtung für Rückenstrecker (Hyperextensions)." - }, - "6": { - "then": "Die Fitness-Station hat Ringe für Gymnastikübungen." - }, - "7": { - "then": "Die Fitness-Station hat eine horizontale Leiter (Monkey Bars)." - }, - "8": { - "then": "Die Fitness-Station hat eine Sprossenwand zum Klettern." - }, - "9": { - "then": "Die Fitness-Station hat Pfosten für Slalomübungen." - }, "10": { "then": "Die Fitness-Station hat Trittsteine." }, @@ -4498,6 +4474,9 @@ "19": { "then": "Die Fitness-Station hat Kampfseile (battle ropes)." }, + "2": { + "then": "Die Fitness-Station hat eine Einrichtung für Sit-ups." + }, "20": { "then": "Die Fitness-Station hat ein Fahrradergometer." }, @@ -4512,6 +4491,27 @@ }, "24": { "then": "Die Fitness-Station hat eine Slackline." + }, + "3": { + "then": "Die Fitness-Station hat eine Vorrichtung für Liegestütze. In der Regel eine oder mehrere niedrige Reckstangen." + }, + "4": { + "then": "Die Fitness-Station hat Stangen zum Dehnen." + }, + "5": { + "then": "Die Fitness-Station hat eine Vorrichtung für Rückenstrecker (Hyperextensions)." + }, + "6": { + "then": "Die Fitness-Station hat Ringe für Gymnastikübungen." + }, + "7": { + "then": "Die Fitness-Station hat eine horizontale Leiter (Monkey Bars)." + }, + "8": { + "then": "Die Fitness-Station hat eine Sprossenwand zum Klettern." + }, + "9": { + "then": "Die Fitness-Station hat Pfosten für Slalomübungen." } }, "question": "Welche Übungsgeräte gibt es an dieser Fitness-Station?" @@ -4631,6 +4631,21 @@ "1": { "then": "Dies ist eine Pommesbude" }, + "10": { + "then": "Hier werden chinesische Gerichte serviert" + }, + "11": { + "then": "Hier werden griechische Gerichte serviert" + }, + "12": { + "then": "Hier werden indische Gerichte serviert" + }, + "13": { + "then": "Hier werden türkische Gerichte serviert" + }, + "14": { + "then": "Hier werden thailändische Gerichte serviert" + }, "2": { "then": "Bietet vorwiegend Pastagerichte an" }, @@ -4654,21 +4669,6 @@ }, "9": { "then": "Hier werden französische Gerichte serviert" - }, - "10": { - "then": "Hier werden chinesische Gerichte serviert" - }, - "11": { - "then": "Hier werden griechische Gerichte serviert" - }, - "12": { - "then": "Hier werden indische Gerichte serviert" - }, - "13": { - "then": "Hier werden türkische Gerichte serviert" - }, - "14": { - "then": "Hier werden thailändische Gerichte serviert" } }, "question": "Was für Essen gibt es hier?", @@ -5360,30 +5360,6 @@ "1": { "then": "Dies ist ein Auditorium" }, - "2": { - "then": "Dies ist ein Schlafzimmer" - }, - "3": { - "then": "Dies ist eine Kapelle" - }, - "4": { - "then": "Dies ist ein Klassenzimmer" - }, - "5": { - "then": "Dies ist ein Klassenzimmer" - }, - "6": { - "then": "Dies ist ein Computerraum" - }, - "7": { - "then": "Dies ist ein Konferenzraum" - }, - "8": { - "then": "Dies ist eine Krypta" - }, - "9": { - "then": "Dies ist eine Küche" - }, "10": { "then": "Dies ist ein Labor" }, @@ -5414,6 +5390,9 @@ "19": { "then": "Dies ist ein Lagerraum" }, + "2": { + "then": "Dies ist ein Schlafzimmer" + }, "20": { "then": "Dies ist ein Technikraum" }, @@ -5422,6 +5401,27 @@ }, "22": { "then": "Dies ist ein Wartezimmer" + }, + "3": { + "then": "Dies ist eine Kapelle" + }, + "4": { + "then": "Dies ist ein Klassenzimmer" + }, + "5": { + "then": "Dies ist ein Klassenzimmer" + }, + "6": { + "then": "Dies ist ein Computerraum" + }, + "7": { + "then": "Dies ist ein Konferenzraum" + }, + "8": { + "then": "Dies ist eine Krypta" + }, + "9": { + "then": "Dies ist eine Küche" } }, "question": "Wie wird dieser Raum genutzt?" @@ -6048,6 +6048,19 @@ } } }, + "10": { + "options": { + "0": { + "question": "Alle Notizen" + }, + "1": { + "question": "Importnotizen ausblenden" + }, + "2": { + "question": "Nur Importnotizen anzeigen" + } + } + }, "2": { "options": { "0": { @@ -6103,19 +6116,6 @@ "question": "Nur offene Notizen anzeigen" } } - }, - "10": { - "options": { - "0": { - "question": "Alle Notizen" - }, - "1": { - "question": "Importnotizen ausblenden" - }, - "2": { - "question": "Nur Importnotizen anzeigen" - } - } } }, "name": "OpenStreetMap-Hinweise", @@ -6440,6 +6440,21 @@ "1": { "then": "Dies ist ein normaler Stellplatz." }, + "10": { + "then": "Dies ist ein Stellplatz, der für Eltern mit Kindern reserviert ist." + }, + "11": { + "then": "Dies ist ein Stellplatz, der für das Personal reserviert ist." + }, + "12": { + "then": "Dies ist ein Stellplatz, der für Taxis reserviert ist." + }, + "13": { + "then": "Dies ist ein Stellplatz, der für Fahrzeuge mit Anhänger reserviert ist." + }, + "14": { + "then": "Dies ist ein Stellplatz, der für Carsharing reserviert ist." + }, "2": { "then": "Dies ist ein Behindertenstellplatz." }, @@ -6463,21 +6478,6 @@ }, "9": { "then": "Dies ist ein Stellplatz, der für Motorräder reserviert ist." - }, - "10": { - "then": "Dies ist ein Stellplatz, der für Eltern mit Kindern reserviert ist." - }, - "11": { - "then": "Dies ist ein Stellplatz, der für das Personal reserviert ist." - }, - "12": { - "then": "Dies ist ein Stellplatz, der für Taxis reserviert ist." - }, - "13": { - "then": "Dies ist ein Stellplatz, der für Fahrzeuge mit Anhänger reserviert ist." - }, - "14": { - "then": "Dies ist ein Stellplatz, der für Carsharing reserviert ist." } }, "question": "Welche Art von Stellplatz ist dies?" @@ -7075,6 +7075,21 @@ "1": { "then": "2-Cent-Münzen werden akzeptiert" }, + "10": { + "then": "20-Centime-Münzen werden akzeptiert" + }, + "11": { + "then": "½-Schweizer Franken-Münzen werden akzeptiert" + }, + "12": { + "then": "1-Schweizer Franken-Münzen werden akzeptiert" + }, + "13": { + "then": "2-Schweizer Franken-Münzen werden akzeptiert" + }, + "14": { + "then": "5-Schweizer Franken-Münzen werden akzeptiert" + }, "2": { "then": "5-Cent-Münzen werden akzeptiert" }, @@ -7098,21 +7113,6 @@ }, "9": { "then": "10-Centime-Münzen werden akzeptiert" - }, - "10": { - "then": "20-Centime-Münzen werden akzeptiert" - }, - "11": { - "then": "½-Schweizer Franken-Münzen werden akzeptiert" - }, - "12": { - "then": "1-Schweizer Franken-Münzen werden akzeptiert" - }, - "13": { - "then": "2-Schweizer Franken-Münzen werden akzeptiert" - }, - "14": { - "then": "5-Schweizer Franken-Münzen werden akzeptiert" } }, "question": "Mit welchen Münzen kann man hier bezahlen?" @@ -7125,6 +7125,15 @@ "1": { "then": "10-Euro-Scheine werden angenommen" }, + "10": { + "then": "100-Schweizer Franken-Scheine werden akzeptiert" + }, + "11": { + "then": "200-Schweizer Franken-Scheine werden akzeptiert" + }, + "12": { + "then": "1000-Schweizer Franken-Scheine werden akzeptiert" + }, "2": { "then": "20-Euro-Scheine werden angenommen" }, @@ -7148,15 +7157,6 @@ }, "9": { "then": "50-Schweizer Franken-Scheine werden akzeptiert" - }, - "10": { - "then": "100-Schweizer Franken-Scheine werden akzeptiert" - }, - "11": { - "then": "200-Schweizer Franken-Scheine werden akzeptiert" - }, - "12": { - "then": "1000-Schweizer Franken-Scheine werden akzeptiert" } }, "question": "Mit welchen Banknoten kann man hier bezahlen?" @@ -7610,30 +7610,6 @@ "1": { "question": "Recycling von Batterien" }, - "2": { - "question": "Recycling von Getränkekartons" - }, - "3": { - "question": "Recycling von Dosen" - }, - "4": { - "question": "Recycling von Kleidung" - }, - "5": { - "question": "Recycling von Speiseöl" - }, - "6": { - "question": "Recycling von Motoröl" - }, - "7": { - "question": "Recycling von Leuchtstoffröhren" - }, - "8": { - "question": "Recycling von Grünabfällen" - }, - "9": { - "question": "Recycling von Glasflaschen" - }, "10": { "question": "Recycling von Glas" }, @@ -7664,11 +7640,35 @@ "19": { "question": "Recycling von Restabfällen" }, + "2": { + "question": "Recycling von Getränkekartons" + }, "20": { "question": "Recycling von Druckerpatronen" }, "21": { "question": "Recycling von Fahrrädern" + }, + "3": { + "question": "Recycling von Dosen" + }, + "4": { + "question": "Recycling von Kleidung" + }, + "5": { + "question": "Recycling von Speiseöl" + }, + "6": { + "question": "Recycling von Motoröl" + }, + "7": { + "question": "Recycling von Leuchtstoffröhren" + }, + "8": { + "question": "Recycling von Grünabfällen" + }, + "9": { + "question": "Recycling von Glasflaschen" } } }, @@ -7736,30 +7736,6 @@ "1": { "then": "Getränkekartons können hier recycelt werden" }, - "2": { - "then": "Dosen können hier recycelt werden" - }, - "3": { - "then": "Kleidung kann hier recycelt werden" - }, - "4": { - "then": "Speiseöl kann hier recycelt werden" - }, - "5": { - "then": "Motoröl kann hier recycelt werden" - }, - "6": { - "then": "Hier können Leuchtstoffröhren recycelt werden" - }, - "7": { - "then": "Grünabfälle können hier recycelt werden" - }, - "8": { - "then": "Bio-Abfall kann hier recycelt werden" - }, - "9": { - "then": "Glasflaschen können hier recycelt werden" - }, "10": { "then": "Glas kann hier recycelt werden" }, @@ -7790,6 +7766,9 @@ "19": { "then": "Schuhe können hier recycelt werden" }, + "2": { + "then": "Dosen können hier recycelt werden" + }, "20": { "then": "Elektrokleingeräte können hier recycelt werden" }, @@ -7804,6 +7783,27 @@ }, "24": { "then": "Fahrräder können hier recycelt werden" + }, + "3": { + "then": "Kleidung kann hier recycelt werden" + }, + "4": { + "then": "Speiseöl kann hier recycelt werden" + }, + "5": { + "then": "Motoröl kann hier recycelt werden" + }, + "6": { + "then": "Hier können Leuchtstoffröhren recycelt werden" + }, + "7": { + "then": "Grünabfälle können hier recycelt werden" + }, + "8": { + "then": "Bio-Abfall kann hier recycelt werden" + }, + "9": { + "then": "Glasflaschen können hier recycelt werden" } }, "question": "Was kann hier recycelt werden?" @@ -8711,6 +8711,12 @@ "1": { "then": "Diese Straßenlaterne verwendet LEDs" }, + "10": { + "then": "Diese Straßenlaterne verwendet Hochdruck-Natriumdampflampen (orange mit weiß)" + }, + "11": { + "then": "Diese Straßenlaterne wird mit Gas beleuchtet" + }, "2": { "then": "Diese Straßenlaterne verwendet Glühlampenlicht" }, @@ -8734,12 +8740,6 @@ }, "9": { "then": "Diese Straßenlaterne verwendet Niederdruck-Natriumdampflampen (einfarbig orange)" - }, - "10": { - "then": "Diese Straßenlaterne verwendet Hochdruck-Natriumdampflampen (orange mit weiß)" - }, - "11": { - "then": "Diese Straßenlaterne wird mit Gas beleuchtet" } }, "question": "Mit welcher Art von Beleuchtung arbeitet diese Straßenlaterne?" @@ -9105,20 +9105,20 @@ "toilet-changing_table:location": { "mappings": { "0": { - "then": "Der Wickeltisch befindet sich in der Damentoilette. " + "then": "Ein Wickeltisch ist in der Damentoilette vorhanden" }, "1": { - "then": "Der Wickeltisch befindet sich in der Herrentoilette. " + "then": "Ein Wickeltisch ist in der Herrentoilette vorhanden" }, "2": { - "then": "Der Wickeltisch befindet sich in der Toilette für Rollstuhlfahrer. " + "then": "Ein Wickeltisch ist in der barrierefreien Toilette vorhanden" }, "3": { - "then": "Der Wickeltisch befindet sich in einem eigenen Raum. " + "then": "Ein Wickeltisch befindet sich in einem eigenen Raum" } }, "question": "Wo befindet sich der Wickeltisch?", - "render": "Die Wickeltabelle befindet sich in {changing_table:location}" + "render": "Ein Wickeltisch befindet sich in {changing_table:location}" }, "toilet-charge": { "freeform": { @@ -9989,30 +9989,6 @@ "1": { "question": "Verkauf von Getränken" }, - "2": { - "question": "Verkauf von Süßigkeiten" - }, - "3": { - "question": "Verkauf von Lebensmitteln" - }, - "4": { - "question": "Verkauf von Zigaretten" - }, - "5": { - "question": "Verkauf von Kondomen" - }, - "6": { - "question": "Verkauf von Kaffee" - }, - "7": { - "question": "Verkauf von Trinkwasser" - }, - "8": { - "question": "Verkauf von Zeitungen" - }, - "9": { - "question": "Verkauf von Fahrradschläuchen" - }, "10": { "question": "Verkauf von Milch" }, @@ -10043,6 +10019,9 @@ "19": { "question": "Verkauf von Blumen" }, + "2": { + "question": "Verkauf von Süßigkeiten" + }, "20": { "question": "Verkauf von Parkscheinen" }, @@ -10066,6 +10045,27 @@ }, "27": { "question": "Verkauf von Fahrradschlössern" + }, + "3": { + "question": "Verkauf von Lebensmitteln" + }, + "4": { + "question": "Verkauf von Zigaretten" + }, + "5": { + "question": "Verkauf von Kondomen" + }, + "6": { + "question": "Verkauf von Kaffee" + }, + "7": { + "question": "Verkauf von Trinkwasser" + }, + "8": { + "question": "Verkauf von Zeitungen" + }, + "9": { + "question": "Verkauf von Fahrradschläuchen" } } } @@ -10112,30 +10112,6 @@ "1": { "then": "Süßigkeiten werden verkauft" }, - "2": { - "then": "Lebensmittel werden verkauft" - }, - "3": { - "then": "Zigaretten werden verkauft" - }, - "4": { - "then": "Kondome werden verkauft" - }, - "5": { - "then": "Kaffee wird verkauft" - }, - "6": { - "then": "Trinkwasser wird verkauft" - }, - "7": { - "then": "Zeitungen werden verkauft" - }, - "8": { - "then": "Fahrradschläuche werden verkauft" - }, - "9": { - "then": "Milch wird verkauft" - }, "10": { "then": "Brot wird verkauft" }, @@ -10166,6 +10142,9 @@ "19": { "then": "Parkscheine werden verkauft" }, + "2": { + "then": "Lebensmittel werden verkauft" + }, "20": { "then": "Souvenirmünzen werden verkauft" }, @@ -10186,6 +10165,27 @@ }, "26": { "then": "Fahrradschlösser werden verkauft" + }, + "3": { + "then": "Zigaretten werden verkauft" + }, + "4": { + "then": "Kondome werden verkauft" + }, + "5": { + "then": "Kaffee wird verkauft" + }, + "6": { + "then": "Trinkwasser wird verkauft" + }, + "7": { + "then": "Zeitungen werden verkauft" + }, + "8": { + "then": "Fahrradschläuche werden verkauft" + }, + "9": { + "then": "Milch wird verkauft" } }, "question": "Was wird in diesem Automaten verkauft?", @@ -10477,4 +10477,4 @@ "render": "Windrad" } } -} \ No newline at end of file +} From e220c4103c587751a852d67ea0cc9214ee853b51 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Fri, 19 Jan 2024 01:21:18 +0100 Subject: [PATCH 052/195] Chore: housekeeping --- .../charging_station/charging_station.json | 272 +++---- .../layers/drinking_water/drinking_water.json | 12 +- assets/layers/food/food.json | 6 +- assets/layers/hackerspace/hackerspace.json | 30 +- assets/layers/icons/icons.json | 12 +- assets/layers/note/note.json | 3 +- assets/layers/playground/playground.json | 6 +- assets/layers/questions/questions.json | 15 +- assets/layers/shops/shops.json | 2 +- assets/layers/sport_pitch/sport_pitch.json | 3 +- assets/layers/stairs/stairs.json | 12 +- assets/layers/toilet/toilet.json | 30 +- assets/layers/unit/unit.json | 30 +- assets/layers/usersettings/usersettings.json | 16 +- .../mapcomplete-changes.json | 115 +-- langs/layers/de.json | 744 +++++++++--------- langs/layers/en.json | 744 +++++++++--------- langs/layers/nl.json | 532 ++++++------- package-lock.json | 12 +- 19 files changed, 1293 insertions(+), 1303 deletions(-) diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index 60193ab23..d0d6c66f0 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -79,8 +79,8 @@ "question": { "en": "Which vehicles are allowed to charge here?", "nl": "Welke voertuigen kunnen hier opgeladen worden?", - "de": "Welche Fahrzeuge können hier laden?", - "ca": "Quins vehicles tenen permesa la càrrega aquí?" + "ca": "Quins vehicles tenen permesa la càrrega aquí?", + "de": "Welche Fahrzeuge können hier laden?" }, "multiAnswer": true, "mappings": [ @@ -90,8 +90,8 @@ "then": { "en": "Bicycles can be charged here", "nl": "Elektrische fietsen kunnen hier opgeladen worden", - "de": "Hier können Fahrräder laden", - "ca": "Aquí es poden carregar bicicletes" + "ca": "Aquí es poden carregar bicicletes", + "de": "Hier können Fahrräder laden" } }, { @@ -100,8 +100,8 @@ "then": { "en": "Cars can be charged here", "nl": "Elektrische auto's kunnen hier opgeladen worden", - "de": "Hier können Autos laden", - "ca": "Aquí es poden carregar cotxes" + "ca": "Aquí es poden carregar cotxes", + "de": "Hier können Autos laden" } }, { @@ -110,8 +110,8 @@ "then": { "en": "Scooters can be charged here", "nl": "Elektrische scooters (snorfiets of bromfiets) kunnen hier opgeladen worden", - "de": "Hier können Roller laden", - "ca": "Aquí es poden carregar Scooters" + "ca": "Aquí es poden carregar Scooters", + "de": "Hier können Roller laden" } }, { @@ -120,8 +120,8 @@ "then": { "en": "Heavy good vehicles (such as trucks) can be charged here", "nl": "Vrachtwagens kunnen hier opgeladen worden", - "de": "Hier können LKW laden", - "ca": "Aquí es poden carregar camions o trailers" + "ca": "Aquí es poden carregar camions o trailers", + "de": "Hier können LKW laden" } }, { @@ -130,8 +130,8 @@ "then": { "en": "Buses can be charged here", "nl": "Bussen kunnen hier opgeladen worden", - "de": "Hier können Busse laden", - "ca": "Aquí es poden carregar autobusos" + "ca": "Aquí es poden carregar autobusos", + "de": "Hier können Busse laden" } } ] @@ -141,14 +141,14 @@ "question": { "en": "Who is allowed to use this charging station?", "nl": "Wie mag er dit oplaadpunt gebruiken?", - "de": "Wer darf diese Ladestation benutzen?", - "ca": "Qui pot utilitzar aquesta estació de càrrega?" + "ca": "Qui pot utilitzar aquesta estació de càrrega?", + "de": "Wer darf diese Ladestation benutzen?" }, "render": { "en": "Access is {access}", "nl": "Toegang voor {access}", - "de": "Zugang ist {access}", - "ca": "L'accés està {access}" + "ca": "L'accés està {access}", + "de": "Zugang ist {access}" }, "freeform": { "key": "access", @@ -162,8 +162,8 @@ "then": { "en": "Anyone can use this charging station (payment might be needed)", "nl": "Toegankelijk voor iedereen (mogelijks met aanmelden en/of te betalen)", - "de": "Jeder kann die Station nutzen (eventuell gegen Bezahlung)", - "ca": "Qualsevol persona pot utilitzar aquesta estació de recàrrega (pot ser calgui un pagament)" + "ca": "Qualsevol persona pot utilitzar aquesta estació de recàrrega (pot ser calgui un pagament)", + "de": "Jeder kann die Station nutzen (eventuell gegen Bezahlung)" } }, { @@ -171,8 +171,8 @@ "then": { "en": "Anyone can use this charging station (payment might be needed)", "nl": "Toegankelijk voor iedereen (mogelijks met aanmelden en/of te betalen)", - "de": "Jeder kann diese Ladestation nutzen (eventuell gegen Bezahlung)", - "ca": "Qualsevol persona pot utilitzar aquesta estació de recàrrega (pot ser calgui un pagament)" + "ca": "Qualsevol persona pot utilitzar aquesta estació de recàrrega (pot ser calgui un pagament)", + "de": "Jeder kann diese Ladestation nutzen (eventuell gegen Bezahlung)" }, "hideInAnswer": true }, @@ -181,8 +181,8 @@ "then": { "en": "Only customers of the place this station belongs to can use this charging station
E.g. a charging station operated by hotel which is only usable by their guests", "nl": "Enkel klanten van de bijhorende plaats mogen dit oplaadpunt gebruiken
Bv. op de parking van een hotel en enkel toegankelijk voor klanten van dit hotel", - "de": "Nur Kunden des Ortes, zu dem diese Station gehört, können diese Ladestation nutzen
Z.B. eine von einem Hotel betriebene Ladestation, die nur von dessen Gästen genutzt werden kann", - "ca": "Sols clientes del lloc al que pertany aquest punt de càrrega poden utilitzar-lo
p.e. un punt de càrrega d'un hotel que sols poden utilizar-los els hostes" + "ca": "Sols clientes del lloc al que pertany aquest punt de càrrega poden utilitzar-lo
p.e. un punt de càrrega d'un hotel que sols poden utilizar-los els hostes", + "de": "Nur Kunden des Ortes, zu dem diese Station gehört, können diese Ladestation nutzen
Z.B. eine von einem Hotel betriebene Ladestation, die nur von dessen Gästen genutzt werden kann" } }, { @@ -190,8 +190,8 @@ "then": { "en": "A key must be requested to access this charging station
E.g. a charging station operated by hotel which is only usable by their guests, which receive a key from the reception to unlock the charging station", "nl": "Een sleutel is nodig om dit oplaadpunt te gebruiken
Bv. voor klanten van een hotel of een bar, die de sleutel aan de receptie kunnen krijgen", - "de": "Für den Zugang zur Station muss ein Schlüssel angefordert werden
z.B. eine von einem Hotel betriebene Ladestation, die nur von dessen Gästen genutzt werden kann, die an der Rezeption einen Schlüssel erhalten, um die Ladestation aufzuschließen", - "ca": "S'ha de sol·licitar una clau per a utilitzar aquest punt de càrrega
p.e un punt de càrrega operat per un hotel nomes utilitzable pel seus hostes, els quals reben una clau des de recepció per a desbloquejar el punt de càrrega" + "ca": "S'ha de sol·licitar una clau per a utilitzar aquest punt de càrrega
p.e un punt de càrrega operat per un hotel nomes utilitzable pel seus hostes, els quals reben una clau des de recepció per a desbloquejar el punt de càrrega", + "de": "Für den Zugang zur Station muss ein Schlüssel angefordert werden
z.B. eine von einem Hotel betriebene Ladestation, die nur von dessen Gästen genutzt werden kann, die an der Rezeption einen Schlüssel erhalten, um die Ladestation aufzuschließen" } }, { @@ -199,8 +199,8 @@ "then": { "en": "Not accessible to the general public (e.g. only accessible to the owners, employees, ...)", "nl": "Niet toegankelijk voor het publiek
Bv. enkel toegankelijk voor de eigenaar, medewerkers ,... ", - "de": "Die Station ist nicht für die Allgemeinheit zugänglich (z. B. nur für die Eigentümer, Mitarbeiter, ...)", - "ca": "No accessible per al públic general (p.e. només accessible pels propietaris, empleats, ...)" + "ca": "No accessible per al públic general (p.e. només accessible pels propietaris, empleats, ...)", + "de": "Die Station ist nicht für die Allgemeinheit zugänglich (z. B. nur für die Eigentümer, Mitarbeiter, ...)" } }, { @@ -208,8 +208,8 @@ "then": { "en": "This charging station is accessible to the public during certain hours or conditions. Restrictions might apply, but general use is allowed.", "nl": "Dit oplaadstation is publiek toegankelijk onder voorwaarden (bv. enkel tijdens bepaalde uren). ", - "de": "Diese Ladestation ist zu gewissen Öffnungszeiten oder Bedingungen öffentlich zugänglich. Einschränkungen sind möglich, aber generelle Nutzung ist erlaubt.", - "ca": "Aquesta estació de càrrega és accessible al públic durant certes hores o condicions. Es poden aplicar restriccions, però es permet l'ús general." + "ca": "Aquesta estació de càrrega és accessible al públic durant certes hores o condicions. Es poden aplicar restriccions, però es permet l'ús general.", + "de": "Diese Ladestation ist zu gewissen Öffnungszeiten oder Bedingungen öffentlich zugänglich. Einschränkungen sind möglich, aber generelle Nutzung ist erlaubt." } } ] @@ -219,14 +219,14 @@ "render": { "en": "{capacity} vehicles can be charged here at the same time", "nl": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden", - "de": "Hier können {capacity} Fahrzeuge gleichzeitig laden", - "ca": "Aquí poden carregar {capacity} vehicles a l'hora" + "ca": "Aquí poden carregar {capacity} vehicles a l'hora", + "de": "Hier können {capacity} Fahrzeuge gleichzeitig laden" }, "question": { "en": "How much vehicles can be charged here at the same time?", "nl": "Hoeveel voertuigen kunnen hier opgeladen worden?", - "de": "Wie viele Fahrzeuge können hier gleichzeitig laden?", - "ca": "Quants vehicles poden carregar a la vegada?" + "ca": "Quants vehicles poden carregar a la vegada?", + "de": "Wie viele Fahrzeuge können hier gleichzeitig laden?" }, "freeform": { "key": "capacity", @@ -248,8 +248,8 @@ "then": { "en": "Schuko wall plug without ground pin (CEE7/4 type F)", "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F)", - "de": "Schuko-Stecker ohne Erdungsstift (CEE7/4 Typ F)", - "ca": "Endoll de paret Schuko sense pin a terra (CEE7/4 tipus F)" + "ca": "Endoll de paret Schuko sense pin a terra (CEE7/4 tipus F)", + "de": "Schuko-Stecker ohne Erdungsstift (CEE7/4 Typ F)" }, "icon": { "path": "./assets/layers/charging_station/CEE7_4F.svg", @@ -343,8 +343,8 @@ "then": { "en": "Schuko wall plug without ground pin (CEE7/4 type F)", "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F)", - "de": "Schuko-Stecker ohne Erdungsstift (CEE7/4 Typ F)", - "ca": "Endoll de paret Schuko sense pin a terra (CEE7/4 tipus F)" + "ca": "Endoll de paret Schuko sense pin a terra (CEE7/4 tipus F)", + "de": "Schuko-Stecker ohne Erdungsstift (CEE7/4 Typ F)" }, "hideInAnswer": true, "icon": { @@ -358,8 +358,8 @@ "then": { "en": "European wall plug with ground pin (CEE7/4 type E)", "nl": "Europese stekker met aardingspin (CEE7/4 type E)", - "de": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)", - "ca": "Endoll de paret Europeu amb pin a terra (CEE7/4 tipus E)" + "ca": "Endoll de paret Europeu amb pin a terra (CEE7/4 tipus E)", + "de": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" }, "icon": { "path": "./assets/layers/charging_station/TypeE.svg", @@ -422,8 +422,8 @@ "then": { "en": "European wall plug with ground pin (CEE7/4 type E)", "nl": "Europese stekker met aardingspin (CEE7/4 type E)", - "de": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)", - "ca": "Endoll de paret Europeu amb pin a terra (CEE7/4 tipus E)" + "ca": "Endoll de paret Europeu amb pin a terra (CEE7/4 tipus E)", + "de": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" }, "hideInAnswer": true, "icon": { @@ -437,8 +437,8 @@ "then": { "en": "Chademo", "nl": "Chademo", - "de": "Chademo-Anschluss", - "ca": "CHAdeMo" + "ca": "CHAdeMo", + "de": "Chademo-Anschluss" }, "icon": { "path": "./assets/layers/charging_station/Chademo_type4.svg", @@ -481,8 +481,8 @@ "then": { "en": "Chademo", "nl": "Chademo", - "de": "Chademo-Anschluss", - "ca": "CHAdeMo" + "ca": "CHAdeMo", + "de": "Chademo-Anschluss" }, "hideInAnswer": true, "icon": { @@ -496,8 +496,8 @@ "then": { "en": "Type 1 with cable (J1772)", "nl": "Type 1 met kabel (J1772)", - "de": "Typ 1 mit Kabel (J1772)", - "ca": "Tipus 1 amb cable" + "ca": "Tipus 1 amb cable", + "de": "Typ 1 mit Kabel (J1772)" }, "icon": { "path": "./assets/layers/charging_station/Type1_J1772.svg", @@ -540,8 +540,8 @@ "then": { "en": "Type 1 with cable (J1772)", "nl": "Type 1 met kabel (J1772)", - "de": "Typ 1 mit Kabel (J1772)", - "ca": "Tipus 1 amb cable" + "ca": "Tipus 1 amb cable", + "de": "Typ 1 mit Kabel (J1772)" }, "hideInAnswer": true, "icon": { @@ -555,8 +555,8 @@ "then": { "en": "Type 1 without cable (J1772)", "nl": "Type 1 zonder kabel (J1772)", - "de": "Typ 1 ohne Kabel (J1772)", - "ca": "Tipus 1 sense cable" + "ca": "Tipus 1 sense cable", + "de": "Typ 1 ohne Kabel (J1772)" }, "icon": { "path": "./assets/layers/charging_station/Type1_J1772.svg", @@ -599,8 +599,8 @@ "then": { "en": "Type 1 without cable (J1772)", "nl": "Type 1 zonder kabel (J1772)", - "de": " Typ 1 ohne Kabel (J1772)", - "ca": "Tipus 1 sense cable(J1772)" + "ca": "Tipus 1 sense cable(J1772)", + "de": " Typ 1 ohne Kabel (J1772)" }, "hideInAnswer": true, "icon": { @@ -614,8 +614,8 @@ "then": { "en": "Type 1 CCS (aka Type 1 Combo)", "nl": "Type 1 CCS (ook gekend als Type 1 Combo)", - "de": "Typ 1 CCS (Typ 1 Combo)", - "ca": "CSS Tipus 1 (també conegut com a Tipus 1 Combo)" + "ca": "CSS Tipus 1 (també conegut com a Tipus 1 Combo)", + "de": "Typ 1 CCS (Typ 1 Combo)" }, "icon": { "path": "./assets/layers/charging_station/Type1-ccs.svg", @@ -658,8 +658,8 @@ "then": { "en": "Type 1 CCS (aka Type 1 Combo)", "nl": "Type 1 CCS (ook gekend als Type 1 Combo)", - "de": " Typ 1 CCS (auch bekannt als Typ 1 Combo)", - "ca": "CSS Tipus 1 (també conegut com a Tipus 1 Combo)" + "ca": "CSS Tipus 1 (també conegut com a Tipus 1 Combo)", + "de": " Typ 1 CCS (auch bekannt als Typ 1 Combo)" }, "hideInAnswer": true, "icon": { @@ -673,8 +673,8 @@ "then": { "en": "Tesla Supercharger", "nl": "Tesla Supercharger", - "de": "Tesla Supercharger", - "ca": "Supercarregador de Tesla" + "ca": "Supercarregador de Tesla", + "de": "Tesla Supercharger" }, "icon": { "path": "./assets/layers/charging_station/Tesla-hpwc-model-s.svg", @@ -717,8 +717,8 @@ "then": { "en": "Tesla Supercharger", "nl": "Tesla Supercharger", - "de": "Tesla Supercharger", - "ca": "Supercarregador de Tesla" + "ca": "Supercarregador de Tesla", + "de": "Tesla Supercharger" }, "hideInAnswer": true, "icon": { @@ -732,8 +732,8 @@ "then": { "en": "Type 2 (mennekes)", "nl": "Type 2 (mennekes)", - "de": "Typ 2 (Mennekes)", - "ca": "Tipus 2 (mennekes)" + "ca": "Tipus 2 (mennekes)", + "de": "Typ 2 (Mennekes)" }, "icon": { "path": "./assets/layers/charging_station/Type2_socket.svg", @@ -776,8 +776,8 @@ "then": { "en": "Type 2 (mennekes)", "nl": "Type 2 (mennekes)", - "de": "Typ 2 (Mennekes)", - "ca": "Tipus 2 (mennekes)" + "ca": "Tipus 2 (mennekes)", + "de": "Typ 2 (Mennekes)" }, "hideInAnswer": true, "icon": { @@ -791,8 +791,8 @@ "then": { "en": "Type 2 CCS (mennekes)", "nl": "Type 2 CCS (mennekes)", - "de": "Typ 2 CCS (Mennekes)", - "ca": "CSS Tipus 2 (mennekes)" + "ca": "CSS Tipus 2 (mennekes)", + "de": "Typ 2 CCS (Mennekes)" }, "icon": { "path": "./assets/layers/charging_station/Type2_CCS.svg", @@ -835,8 +835,8 @@ "then": { "en": "Type 2 CCS (mennekes)", "nl": "Type 2 CCS (mennekes)", - "de": "Typ 2 CCS (mennekes)", - "ca": "CSS Tipus 2 (mennekes)" + "ca": "CSS Tipus 2 (mennekes)", + "de": "Typ 2 CCS (mennekes)" }, "hideInAnswer": true, "icon": { @@ -1710,8 +1710,8 @@ "question": { "en": "When is this charging station opened?", "nl": "Wanneer is dit oplaadpunt beschikbaar??", - "de": "Wann ist die Ladestation geöffnet?", - "ca": "Quan està oberta aquesta estació de càrrega?" + "ca": "Quan està oberta aquesta estació de càrrega?", + "de": "Wann ist die Ladestation geöffnet?" } }, "id": "OH" @@ -1721,8 +1721,8 @@ "question": { "en": "Does one have to pay to use this charging station?", "nl": "Moet men betalen om dit oplaadpunt te gebruiken?", - "de": "Muss man für die Nutzung dieser Ladestation bezahlen?", - "ca": "Hi ha que pagar per utilitzar aquest punt de càrrega?" + "ca": "Hi ha que pagar per utilitzar aquest punt de càrrega?", + "de": "Muss man für die Nutzung dieser Ladestation bezahlen?" }, "mappings": [ { @@ -1737,8 +1737,8 @@ "then": { "nl": "Gratis te gebruiken (zonder aan te melden)", "en": "Free to use (without authenticating)", - "de": "Die Nutzung ist kostenlos, keine Authentifizierung erforderlich", - "ca": "Ús gratuït (sense autentificació)" + "ca": "Ús gratuït (sense autentificació)", + "de": "Die Nutzung ist kostenlos, keine Authentifizierung erforderlich" } }, { @@ -1753,8 +1753,8 @@ "then": { "nl": "Gratis te gebruiken, maar aanmelden met een applicatie is verplicht", "en": "Free to use, but one has to authenticate", - "de": "Die Nutzung ist kostenlos, Authentifizierung erforderlich", - "ca": "Ús gratuït, però un s'ha d'autentificar" + "ca": "Ús gratuït, però un s'ha d'autentificar", + "de": "Die Nutzung ist kostenlos, Authentifizierung erforderlich" } }, { @@ -1766,8 +1766,8 @@ "then": { "nl": "Gratis te gebruiken", "en": "Free to use", - "de": "Kostenlose Nutzung", - "ca": "Ús gratuït" + "ca": "Ús gratuït", + "de": "Kostenlose Nutzung" }, "hideInAnswer": true }, @@ -1781,8 +1781,8 @@ "then": { "nl": "Betalend te gebruiken, maar gratis voor klanten van het bijhorende hotel/café/ziekenhuis/...", "en": "Paid use, but free for customers of the hotel/pub/hospital/... who operates the charging station", - "de": "Die Nutzung ist kostenpflichtig, aber für Kunden des Betreibers der Einrichtung, wie Hotel, Krankenhaus, ... kostenlos", - "ca": "De pagament, però gratuït per als clients de l'hotel/bar/hospital/... que gestiona l'estació de càrrega" + "ca": "De pagament, però gratuït per als clients de l'hotel/bar/hospital/... que gestiona l'estació de càrrega", + "de": "Die Nutzung ist kostenpflichtig, aber für Kunden des Betreibers der Einrichtung, wie Hotel, Krankenhaus, ... kostenlos" } }, { @@ -1795,8 +1795,8 @@ "then": { "nl": "Betalend", "en": "Paid use", - "de": "Die Nutzung ist kostenpflichtig", - "ca": "Ús de pagament" + "ca": "Ús de pagament", + "de": "Die Nutzung ist kostenpflichtig" } } ] @@ -1806,14 +1806,14 @@ "question": { "en": "How much does one have to pay to use this charging station?", "nl": "Hoeveel moet men betalen om dit oplaadpunt te gebruiken?", - "de": "Wie viel muss man für die Nutzung dieser Ladestation bezahlen?", - "ca": "Quant cal pagar per utilitzar aquesta estació de càrrega?" + "ca": "Quant cal pagar per utilitzar aquesta estació de càrrega?", + "de": "Wie viel muss man für die Nutzung dieser Ladestation bezahlen?" }, "render": { "en": "Using this charging station costs {charge}", "nl": "Dit oplaadpunt gebruiken kost {charge}", - "de": "Die Nutzung dieser Ladestation kostet {charge}", - "ca": "Utilitzar aquesta estació de càrrega costa {charge}" + "ca": "Utilitzar aquesta estació de càrrega costa {charge}", + "de": "Die Nutzung dieser Ladestation kostet {charge}" }, "freeform": { "key": "charge" @@ -1838,8 +1838,8 @@ "question": { "en": "What kind of authentication is available at the charging station?", "nl": "Hoe kan men zich aanmelden aan dit oplaadstation?", - "de": "Welche Art der Authentifizierung ist an der Ladestation möglich?", - "ca": "Quin tipus d'autenticació hi ha disponible a l'estació de càrrega?" + "ca": "Quin tipus d'autenticació hi ha disponible a l'estació de càrrega?", + "de": "Welche Art der Authentifizierung ist an der Ladestation möglich?" }, "multiAnswer": true, "mappings": [ @@ -1885,8 +1885,8 @@ "then": { "en": "Authentication via NFC is available", "nl": "Aanmelden via NFC is mogelijk", - "de": "Authentifizierung per NFC ist möglich", - "ca": "L'autenticació mitjançant NFC està disponible" + "ca": "L'autenticació mitjançant NFC està disponible", + "de": "Authentifizierung per NFC ist möglich" } }, { @@ -1895,8 +1895,8 @@ "then": { "en": "Authentication via Money Card is available", "nl": "Aanmelden met Money Card is mogelijk", - "de": "Authentifizierung per Geldkarte ist möglich", - "ca": "L'autenticació mitjançant targeta de pagament està disponible" + "ca": "L'autenticació mitjançant targeta de pagament està disponible", + "de": "Authentifizierung per Geldkarte ist möglich" } }, { @@ -1905,8 +1905,8 @@ "then": { "en": "Authentication via debit card is available", "nl": "Aanmelden met een betaalkaart is mogelijk", - "de": "Authentifizierung per Kreditkarte ist möglich", - "ca": "L'autenticació mitjançant targeta de debit està disponible" + "ca": "L'autenticació mitjançant targeta de debit està disponible", + "de": "Authentifizierung per Kreditkarte ist möglich" } }, { @@ -1915,8 +1915,8 @@ "then": { "en": "Charging here is (also) possible without authentication", "nl": "Hier opladen is (ook) mogelijk zonder aan te melden", - "de": "Das Laden ist hier (auch) ohne Authentifizierung möglich", - "ca": "Carregar aquí (també) és possible sense autenticació" + "ca": "Carregar aquí (també) és possible sense autenticació", + "de": "Das Laden ist hier (auch) ohne Authentifizierung möglich" } } ], @@ -1955,8 +1955,8 @@ "question": { "en": "What is the maximum amount of time one is allowed to stay here?", "nl": "Hoelang mag een voertuig hier blijven staan?", - "de": "Wie lange darf man hier maximal parken?", - "ca": "Quina és la quantitat màxima de temps que es permet permaneixer aquí?" + "ca": "Quina és la quantitat màxima de temps que es permet permaneixer aquí?", + "de": "Wie lange darf man hier maximal parken?" }, "freeform": { "key": "maxstay" @@ -1964,8 +1964,8 @@ "render": { "en": "One can stay at most {canonical(maxstay)}", "nl": "De maximale parkeertijd hier is {canonical(maxstay)}", - "de": "Die maximale Parkdauer beträgt {canonical(maxstay)}", - "ca": "Un pot quedar-se com a màxim {canonical(maxstay)}" + "ca": "Un pot quedar-se com a màxim {canonical(maxstay)}", + "de": "Die maximale Parkdauer beträgt {canonical(maxstay)}" }, "mappings": [ { @@ -1973,8 +1973,8 @@ "then": { "en": "No timelimit on leaving your vehicle here", "nl": "Geen maximum parkeertijd", - "de": "Keine Höchstparkdauer", - "ca": "No hi ha límit de temps per a deixar el teu vehicle aquí" + "ca": "No hi ha límit de temps per a deixar el teu vehicle aquí", + "de": "Keine Höchstparkdauer" } } ], @@ -1992,14 +1992,14 @@ "render": { "en": "Part of the network {network}", "nl": "Maakt deel uit van het {network}-netwerk", - "de": "Teil des Netzwerks {network}", - "ca": "Part de la xarxa {network}" + "ca": "Part de la xarxa {network}", + "de": "Teil des Netzwerks {network}" }, "question": { "en": "Is this charging station part of a network?", "nl": "Is dit oplaadpunt deel van een groter netwerk?", - "de": "Ist diese Ladestation Teil eines Netzwerks?", - "ca": "Aquesta estació de càrrega forma part d'una xarxa?" + "ca": "Aquesta estació de càrrega forma part d'una xarxa?", + "de": "Ist diese Ladestation Teil eines Netzwerks?" }, "freeform": { "key": "network" @@ -2065,14 +2065,14 @@ "question": { "en": "Who is the operator of this charging station?", "nl": "Wie beheert dit oplaadpunt?", - "de": "Wer ist der Betreiber dieser Ladestation?", - "ca": "Qui és l'operadora d'aquesta estació de càrrega?" + "ca": "Qui és l'operadora d'aquesta estació de càrrega?", + "de": "Wer ist der Betreiber dieser Ladestation?" }, "render": { "en": "This charging station is operated by {operator}", "nl": "Wordt beheerd door {operator}", - "de": "Die Station wird betrieben von {operator}", - "ca": "Aquesta estació de càrrega l'opera {operator}" + "ca": "Aquesta estació de càrrega l'opera {operator}", + "de": "Die Station wird betrieben von {operator}" }, "freeform": { "key": "operator" @@ -2087,8 +2087,8 @@ "then": { "en": "Actually, {operator} is the network", "nl": "Eigenlijk is {operator} het netwerk waarvan het deel uitmaakt", - "de": "Eigentlich ist {operator} das Netzwerk", - "ca": "De fet, {operator} és la xarxa" + "ca": "De fet, {operator} és la xarxa", + "de": "Eigentlich ist {operator} das Netzwerk" }, "addExtraTags": [ "operator=" @@ -2103,14 +2103,14 @@ "question": { "en": "What number can one call if there is a problem with this charging station?", "nl": "Wat is het telefoonnummer van de beheerder van dit oplaadpunt?", - "de": "Welche Nummer kann man anrufen, wenn es ein Problem mit dieser Ladestation gibt?", - "ca": "A quin número es pot cridar si hi ha algun problema amb aquest punt de càrrega?" + "ca": "A quin número es pot cridar si hi ha algun problema amb aquest punt de càrrega?", + "de": "Welche Nummer kann man anrufen, wenn es ein Problem mit dieser Ladestation gibt?" }, "render": { "en": "In case of problems, call {phone}", "nl": "Bij problemen, bel naar {phone}", - "de": "Bei Problemen, anrufen unter {phone}", - "ca": "En cas de problemes, truqueu a {phone}" + "ca": "En cas de problemes, truqueu a {phone}", + "de": "Bei Problemen, anrufen unter {phone}" }, "freeform": { "key": "phone", @@ -2122,14 +2122,14 @@ "question": { "en": "What is the email address of the operator?", "nl": "Wat is het email-adres van de operator?", - "de": "Wie lautet die E-Mail-Adresse des Betreibers?", - "ca": "Quin és el correu electrònic de l'operadora?" + "ca": "Quin és el correu electrònic de l'operadora?", + "de": "Wie lautet die E-Mail-Adresse des Betreibers?" }, "render": { "en": "In case of problems, send an email to {email}", "nl": "Bij problemen, email naar {email}", - "de": "Bei Problemen senden Sie bitte eine E-Mail an {email}", - "ca": "En cas de problemes, envia un email a {email}" + "ca": "En cas de problemes, envia un email a {email}", + "de": "Bei Problemen senden Sie bitte eine E-Mail an {email}" }, "freeform": { "key": "email", @@ -2177,8 +2177,8 @@ "question": { "en": "Is this charging point in use?", "nl": "Is dit oplaadpunt operationeel?", - "de": "Ist die Station in Betrieb?", - "ca": "Està en ús aquest punt de càrrega?" + "ca": "Està en ús aquest punt de càrrega?", + "de": "Ist die Station in Betrieb?" }, "mappings": [ { @@ -2194,8 +2194,8 @@ "then": { "en": "This charging station works", "nl": "Dit oplaadpunt werkt", - "de": "Die Station ist in Betrieb", - "ca": "Aquesta estació de càrrega funciona" + "ca": "Aquesta estació de càrrega funciona", + "de": "Die Station ist in Betrieb" } }, { @@ -2211,8 +2211,8 @@ "then": { "en": "This charging station is broken", "nl": "Dit oplaadpunt is kapot", - "de": "Die Station ist defekt", - "ca": "Aquesta estació de carrega està trencada" + "ca": "Aquesta estació de carrega està trencada", + "de": "Die Station ist defekt" } }, { @@ -2228,8 +2228,8 @@ "then": { "en": "A charging station is planned here", "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden", - "de": "Die Station ist erst in Planung", - "ca": "Aquí està prevista una estació de recàrrega" + "ca": "Aquí està prevista una estació de recàrrega", + "de": "Die Station ist erst in Planung" } }, { @@ -2261,8 +2261,8 @@ "then": { "en": "This charging station has beed permanently disabled and is not in use anymore but is still visible", "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig", - "de": "Die Station ist dauerhaft geschlossen und nicht mehr in Nutzung, aber noch sichtbar", - "ca": "Aquesta estació de recàrrega s'ha desactivat permanentment i ja no s'utilitza, però encara és visible" + "ca": "Aquesta estació de recàrrega s'ha desactivat permanentment i ja no s'utilitza, però encara és visible", + "de": "Die Station ist dauerhaft geschlossen und nicht mehr in Nutzung, aber noch sichtbar" } } ] @@ -2272,8 +2272,8 @@ "question": { "en": "Does one have to pay a parking fee while charging?", "nl": "Moet men parkeergeld betalen tijdens het opladen?", - "de": "Muss man während des Ladens eine Parkgebühr bezahlen?", - "ca": "Cal pagar una taxa d'aparcament mentre es carrega?" + "ca": "Cal pagar una taxa d'aparcament mentre es carrega?", + "de": "Muss man während des Ladens eine Parkgebühr bezahlen?" }, "mappings": [ { @@ -2281,8 +2281,8 @@ "then": { "en": "No additional parking cost while charging", "nl": "Geen extra parkeerkost tijdens het opladen", - "de": "Keine zusätzlichen Parkkosten während des Ladens", - "ca": "No cal pagar una taxa addicional mentres carrega" + "ca": "No cal pagar una taxa addicional mentres carrega", + "de": "Keine zusätzlichen Parkkosten während des Ladens" } }, { @@ -2290,8 +2290,8 @@ "then": { "en": "An additional parking fee should be paid while charging", "nl": "Tijdens het opladen moet er parkeergeld betaald worden", - "de": "Während des Ladens ist eine zusätzliche Parkgebühr zu entrichten", - "ca": "Cal pagar una taxa addicional d'aparcament mentres carrega" + "ca": "Cal pagar una taxa addicional d'aparcament mentres carrega", + "de": "Während des Ladens ist eine zusätzliche Parkgebühr zu entrichten" } } ], diff --git a/assets/layers/drinking_water/drinking_water.json b/assets/layers/drinking_water/drinking_water.json index 61b309cbd..0330eb2b0 100644 --- a/assets/layers/drinking_water/drinking_water.json +++ b/assets/layers/drinking_water/drinking_water.json @@ -211,7 +211,8 @@ "id": "type", "question": { "en": "What type of drinking water point is this?", - "nl": "Wat voor soort drinkwaterpunt is dit?" + "nl": "Wat voor soort drinkwaterpunt is dit?", + "de": "Um welche Art von Trinkwasserentnahmestelle handelt es sich?" }, "mappings": [ { @@ -219,7 +220,8 @@ "icon": "./assets/layers/drinking_water/bubbler.svg", "then": { "en": "This is a bubbler fountain. A water jet to drink from is sent upwards, typically controlled by a push button.", - "nl": "Dit is een waterhappertje - een drinkwaterfonteintje waarbij een waterstraaltje omhoog spuit. Dit kan permanent werken of door op een drukknop te duwen." + "nl": "Dit is een waterhappertje - een drinkwaterfonteintje waarbij een waterstraaltje omhoog spuit. Dit kan permanent werken of door op een drukknop te duwen.", + "de": "Dies ist ein Sprudelbrunnen. Ein Wasserstrahl zum Trinken wird nach oben gerichtet und in der Regel durch einen Druckknopf gesteuert." }, "addExtraTags": [ "man_made=" @@ -230,7 +232,8 @@ "icon": "./assets/layers/drinking_water/bottle.svg", "then": { "en": "This is a bottle refill point where the water is sent downwards, typically controlled by a push button or a motion sensor. Drinking directly from the stream might be very hard or impossible.", - "nl": "Dit is een hervulpunt voor drinkwaterflessen. De waterstraal wordt omlaag gestuurd wanneer op een drukknop geduwd wordt of wanneer er beweging gedetecteerd wordt. Rechtstreeks van de waterstraal drinking kan moeilijk of zelfs onmogelijk zijn." + "nl": "Dit is een hervulpunt voor drinkwaterflessen. De waterstraal wordt omlaag gestuurd wanneer op een drukknop geduwd wordt of wanneer er beweging gedetecteerd wordt. Rechtstreeks van de waterstraal drinking kan moeilijk of zelfs onmogelijk zijn.", + "de": "Dies ist eine Flaschenauffüllstation, an der das Wasser nach unten geleitet wird, in der Regel durch einen Druckknopf oder einen Bewegungssensor gesteuert. Direkt aus dem Wasserstrahl zu trinken, kann sehr schwierig oder unmöglich sein." }, "addExtraTags": [ "man_made=", @@ -242,7 +245,8 @@ "icon": "./assets/layers/drinking_water/tap.svg", "then": { "en": "This is a water tap. The water flows downward and the stream is controlled by a valve or push-button.", - "nl": "Dit is een waterkraan. Het water strooomt naar beneden en het volume wordt door een knop of draaimechanisme geregeld." + "nl": "Dit is een waterkraan. Het water strooomt naar beneden en het volume wordt door een knop of draaimechanisme geregeld.", + "de": "Dies ist ein Wasserhahn. Das Wasser fließt nach unten und der Wasserstrahl wird durch ein Ventil oder einen Druckknopf gesteuert." }, "addExtraTags": [ "fountain=" diff --git a/assets/layers/food/food.json b/assets/layers/food/food.json index 943acab38..9de5d9a81 100644 --- a/assets/layers/food/food.json +++ b/assets/layers/food/food.json @@ -737,7 +737,8 @@ "if": "diet:vegetarian=on_demand", "then": { "en": "Some dishes might be adapted to a vegetarian version, but this should be demanded", - "nl": "Sommige gerechten kunnen op vraag vegetarisch gemaakt worden" + "nl": "Sommige gerechten kunnen op vraag vegetarisch gemaakt worden", + "de": "Einige Gerichte können auf Nachfrage in eine vegetarische Version umgewandelt werden" } } ], @@ -811,7 +812,8 @@ "if": "diet:vegan=on_demand", "then": { "en": "Some dishes might be adapted to a vegan version if asked for", - "nl": "Op vraag kan een veganistische variant van een gerecht gemaakt worden" + "nl": "Op vraag kan een veganistische variant van een gerecht gemaakt worden", + "de": "Einige Gerichte können auf Nachfrage in eine vegane Version umgewandelt werden" } } ], diff --git a/assets/layers/hackerspace/hackerspace.json b/assets/layers/hackerspace/hackerspace.json index fe2d17279..95ba491f9 100644 --- a/assets/layers/hackerspace/hackerspace.json +++ b/assets/layers/hackerspace/hackerspace.json @@ -305,11 +305,13 @@ "media_studio", { "en": "a multimedia studio", - "nl": "een multimedia-studio" + "nl": "een multimedia-studio", + "de": "ein Multimediastudio" }, { "en": "multimedia studio", - "nl": "multimedia-studio" + "nl": "multimedia-studio", + "de": "Multimediastudio" }, "./assets/layers/hackerspace/media_studio.svg" ], @@ -331,11 +333,13 @@ "workshop:wood", { "en": "a woodworking workshop", - "nl": "een houtbewerkingsatelier" + "nl": "een houtbewerkingsatelier", + "de": "eine Holzwerkstatt" }, { "en": "woodworking workshop", - "nl": "houtbewerkingsatelier" + "nl": "houtbewerkingsatelier", + "de": "Holzwerkstatt" }, "./assets/layers/hackerspace/woodworking.svg" ], @@ -343,11 +347,13 @@ "workshop:ceramics", { "en": "a ceramics workshop", - "nl": "een keramiekatelier" + "nl": "een keramiekatelier", + "de": "eine Keramikwerkstatt" }, { "en": "ceramics workshop", - "nl": "keramiekatelier" + "nl": "keramiekatelier", + "de": "Keramikwerkstatt" }, "./assets/layers/hackerspace/ceramics.svg" ], @@ -355,11 +361,13 @@ "workshop:metal", { "en": "a metal workshop", - "nl": "een metaalatelier" + "nl": "een metaalatelier", + "de": "eine Metallwerkstatt" }, { "en": "metal workshop", - "nl": "metaalatelier" + "nl": "metaalatelier", + "de": "Metallwerkstatt" }, "./assets/layers/hackerspace/metal.svg" ], @@ -367,11 +375,13 @@ "bicycle:diy", { "en": "a bicycle repair workshop", - "nl": "een fietsherstelplaats" + "nl": "een fietsherstelplaats", + "de": "eine Fahrradwerkstatt" }, { "en": "bicycle repair workshop", - "nl": "fietsherstelplaats" + "nl": "fietsherstelplaats", + "de": "Fahrradwerkstatt" }, "./assets/layers/hackerspace/bicycle.svg" ] diff --git a/assets/layers/icons/icons.json b/assets/layers/icons/icons.json index 3acfb3cac..29688d36e 100644 --- a/assets/layers/icons/icons.json +++ b/assets/layers/icons/icons.json @@ -126,7 +126,8 @@ "text": "phone", "arialabel": { "en": "phone", - "nl": "Telefoneer" + "nl": "Telefoneer", + "de": "Telefon" } } }, @@ -141,7 +142,8 @@ "text": "phone", "arialabel": { "en": "phone", - "nl": "Telefoneer" + "nl": "Telefoneer", + "de": "Telefon" } } } @@ -233,7 +235,8 @@ "href": "https://openstreetmap.org/{id}", "arialabel": { "en": "Open on openstreetmap.org", - "nl": "Bekijk op openstreetmap.org" + "nl": "Bekijk op openstreetmap.org", + "de": "Auf openstreetmap.org öffnen" } } }, @@ -252,7 +255,8 @@ "href": "{_backend}/{id}", "arialabel": { "en": "Open on openstreetmap.org", - "nl": "Bekijk op openstreetmap.org" + "nl": "Bekijk op openstreetmap.org", + "de": "Auf openstreetmap.org öffnen" } } } diff --git a/assets/layers/note/note.json b/assets/layers/note/note.json index b8f69c142..a39f53107 100644 --- a/assets/layers/note/note.json +++ b/assets/layers/note/note.json @@ -56,7 +56,8 @@ { "ariaLabel": { "en": "See on OpenStreetMap.org", - "nl": "Bekijk op OpenStreetMap.org" + "nl": "Bekijk op OpenStreetMap.org", + "de": "Auf OpenStreetMap.org ansehen" }, "render": "" } diff --git a/assets/layers/playground/playground.json b/assets/layers/playground/playground.json index 4324a58df..52447d8f0 100644 --- a/assets/layers/playground/playground.json +++ b/assets/layers/playground/playground.json @@ -273,14 +273,16 @@ "if": "surface=tartan", "then": { "en": "The surface is tartan - a synthetic, springy surface typically seen on athletic pistes", - "nl": "De ondergrond bestaat uit Tartan - een synthetisch, elastisch en poreus materiaal dat je ook vindt op atletiekpistes" + "nl": "De ondergrond bestaat uit Tartan - een synthetisch, elastisch en poreus materiaal dat je ook vindt op atletiekpistes", + "de": "Der Belag ist aus Tartan - ein synthetischer, federnder Belag, der typischerweise auf Sportbahnen zu finden ist" } }, { "if": "surface=rubber", "then": { "en": "The surface is made from rubber, such as rubber tiles, rubber mulch or a big rubber area", - "nl": "De ondergrond bestaat uit rubber, zoals rubberen tegels, rubber snippers of een groot rubberen oppervlak" + "nl": "De ondergrond bestaat uit rubber, zoals rubberen tegels, rubber snippers of een groot rubberen oppervlak", + "de": "Die Oberfläche besteht aus Gummi, z. B. aus Gummifliesen, Gummimulch oder einer großen Gummifläche" } } ], diff --git a/assets/layers/questions/questions.json b/assets/layers/questions/questions.json index ada1d0582..e2f06be10 100644 --- a/assets/layers/questions/questions.json +++ b/assets/layers/questions/questions.json @@ -193,7 +193,8 @@ }, "editButtonAriaLabel": { "en": "Edit phone number", - "nl": "Pas telefoonnummer aan" + "nl": "Pas telefoonnummer aan", + "de": "Telefonnummer bearbeiten" } }, { @@ -276,7 +277,8 @@ }, "editButtonAriaLabel": { "en": "Edit email address", - "nl": "Pas emailadres aan" + "nl": "Pas emailadres aan", + "de": "E-Mail Adresse bearbeiten" } }, { @@ -330,7 +332,8 @@ ], "editButtonAriaLabel": { "en": "Edit website", - "nl": "Pas website aan" + "nl": "Pas website aan", + "de": "Webseite bearbeiten" } }, { @@ -2629,7 +2632,8 @@ }, "after": { "en": "Scan this code to open this location on another device", - "nl": "Scan deze code om deze locatie op een ander apparaat te zien" + "nl": "Scan deze code om deze locatie op een ander apparaat te zien", + "de": "Scannen Sie den Code, um diesen Ort auf einem anderen Gerät zu öffnen" } } }, @@ -2644,7 +2648,8 @@ "type": "share_link", "text": { "en": "Share this location", - "nl": "Deel deze locatie" + "nl": "Deel deze locatie", + "de": "Standort teilen" } } } diff --git a/assets/layers/shops/shops.json b/assets/layers/shops/shops.json index be124f8ca..727419ee1 100644 --- a/assets/layers/shops/shops.json +++ b/assets/layers/shops/shops.json @@ -206,7 +206,7 @@ "en": "You can specify later on what this shop sells.", "ca": "Podeu especificar més endavant el que ven aquesta botiga.", "cs": "Přidat nový obchod", - "de": "Ein neues Geschäft hinzufügen", + "de": "Sie können später angeben, was das Geschäft verkauft.", "eo": "Enmeti novan butikon", "es": "Añadir una nueva tienda", "fr": "Ajouter un nouveau magasin", diff --git a/assets/layers/sport_pitch/sport_pitch.json b/assets/layers/sport_pitch/sport_pitch.json index 37629af29..8081d8a7a 100644 --- a/assets/layers/sport_pitch/sport_pitch.json +++ b/assets/layers/sport_pitch/sport_pitch.json @@ -475,7 +475,8 @@ "then": { "en": "The surface of this track is Tartan, a synthetic, slightly springy, porous surface", "nl": "De ondergrond is Tartan, een synthetisch, licht verende en poreuze ondergrond", - "ca": "La superfície d'aquesta pista és Tartan, una superfície sintètica, lleugerament molla i porosa" + "ca": "La superfície d'aquesta pista és Tartan, una superfície sintètica, lleugerament molla i porosa", + "de": "Der Belag dieser Laufbahn ist Tartan, ein synthetischer, leicht federnder, poröser Belag" } } ], diff --git a/assets/layers/stairs/stairs.json b/assets/layers/stairs/stairs.json index d51da3c48..8e55fe971 100644 --- a/assets/layers/stairs/stairs.json +++ b/assets/layers/stairs/stairs.json @@ -294,7 +294,8 @@ "id": "incline", "render": { "en": "These stairs have an incline of {incline}", - "ca": "Aquestes escales tenen una inclinació de {incline}" + "ca": "Aquestes escales tenen una inclinació de {incline}", + "de": "Die Treppe hat eine Steigung von {incline}" }, "freeform": { "key": "incline", @@ -302,14 +303,16 @@ }, "question": { "en": "What is the incline of these stairs?", - "ca": "Quina és la inclinació d'aquestes escales?" + "ca": "Quina és la inclinació d'aquestes escales?", + "de": "Welche Steigung hat die Treppe?" }, "mappings": [ { "if": "incline=up", "then": { "en": "The upward direction is {direction_absolute()}", - "ca": "La direcció ascendent és {direction_absolute()}" + "ca": "La direcció ascendent és {direction_absolute()}", + "de": "Die Aufwärtsrichtung ist {direction_absolute()}" }, "hideInAnswer": true }, @@ -317,7 +320,8 @@ "if": "incline=down", "then": { "en": "The downward direction is {direction_absolute()}", - "ca": "La direcció descendent és {direction_absolute()}" + "ca": "La direcció descendent és {direction_absolute()}", + "de": "Die Abwärtsrichtung ist {direction_absolute()}" }, "hideInAnswer": true } diff --git a/assets/layers/toilet/toilet.json b/assets/layers/toilet/toilet.json index 463286896..4e528c901 100644 --- a/assets/layers/toilet/toilet.json +++ b/assets/layers/toilet/toilet.json @@ -611,10 +611,10 @@ "cs": "Kde je umístěn přebalovací pult?" }, "render": { - "en": "The changing table is located at {changing_table:location}", - "de": "Die Wickeltabelle befindet sich in {changing_table:location}", + "en": "A changing table is located at {changing_table:location}", + "de": "Ein Wickeltisch befindet sich in {changing_table:location}", "fr": "Emplacement de la table à langer : {changing_table:location}", - "nl": "De luiertafel bevindt zich in {changing_table:location}", + "nl": "Er bevindt zich een luiertafel in {changing_table:location}", "it": "Il fasciatoio si trova presso {changing_table:location}", "es": "El cambiador está en {changing_table:location}", "da": "Puslebordet er placeret på {changing_table:location}", @@ -633,10 +633,10 @@ "mappings": [ { "then": { - "en": "The changing table is in the toilet for women. ", - "de": "Der Wickeltisch befindet sich in der Damentoilette. ", + "en": "A changing table is in the toilet for women", + "de": "Ein Wickeltisch ist in der Damentoilette vorhanden", "fr": "La table à langer est dans les toilettes pour femmes. ", - "nl": "De luiertafel bevindt zich in de vrouwentoiletten ", + "nl": "Er bevindt zich een luiertafel in de vrouwentoiletten ", "it": "Il fasciatoio è nei servizi igienici femminili. ", "da": "Puslebordet er på toilettet til kvinder. ", "ca": "El canviador està al lavabo per a dones. ", @@ -646,10 +646,10 @@ }, { "then": { - "en": "The changing table is in the toilet for men. ", - "de": "Der Wickeltisch befindet sich in der Herrentoilette. ", + "en": "A changing table is in the toilet for men", + "de": "Ein Wickeltisch ist in der Herrentoilette vorhanden", "fr": "La table à langer est dans les toilettes pour hommes. ", - "nl": "De luiertafel bevindt zich in de herentoiletten ", + "nl": "Er bevindt zich een luiertafel in de herentoiletten ", "it": "Il fasciatoio è nei servizi igienici maschili. ", "ca": "El canviador està al lavabo per a homes. ", "cs": "Přebalovací pult je na pánské toaletě. " @@ -659,10 +659,10 @@ { "if": "changing_table:location=wheelchair_toilet", "then": { - "en": "The changing table is in the toilet for wheelchair users. ", - "de": "Der Wickeltisch befindet sich in der Toilette für Rollstuhlfahrer. ", + "en": "A changing table is in the toilet for wheelchair users", + "de": "Ein Wickeltisch ist in der barrierefreien Toilette vorhanden", "fr": "La table à langer est dans les toilettes pour personnes à mobilité réduite. ", - "nl": "De luiertafel bevindt zich in de rolstoeltoegankelijke toilet ", + "nl": "Er bevindt zich een luiertafel in de rolstoeltoegankelijke toilet ", "it": "Il fasciatoio è nei servizi igienici per persone in sedia a rotelle. ", "da": "Puslebordet er på toilettet for kørestolsbrugere. ", "ca": "El canviador està al lavabo per a usuaris de cadira de rodes. ", @@ -672,10 +672,10 @@ { "if": "changing_table:location=dedicated_room", "then": { - "en": "The changing table is in a dedicated room. ", - "de": "Der Wickeltisch befindet sich in einem eigenen Raum. ", + "en": "A changing table is in a dedicated room", + "de": "Ein Wickeltisch befindet sich in einem eigenen Raum", "fr": "La table à langer est dans un espace dédié. ", - "nl": "De luiertafel bevindt zich in een daartoe voorziene kamer ", + "nl": "Er bevindt zich een luiertafel in een daartoe voorziene kamer ", "it": "Il fasciatoio è in una stanza dedicata. ", "es": "El cambiador está en una habitación dedicada ", "da": "Vuggestuen står i et særligt rum. ", diff --git a/assets/layers/unit/unit.json b/assets/layers/unit/unit.json index 78e1334ee..9f26747d7 100644 --- a/assets/layers/unit/unit.json +++ b/assets/layers/unit/unit.json @@ -1,7 +1,8 @@ { "id": "unit", "description": { - "en": "Library layer with all common units. Units can _only_ be imported from this file." + "en": "Library layer with all common units. Units can _only_ be imported from this file.", + "de": "Bibliotheksebene mit allen gängigen Einrichtungen. Einrichtungen können _nur_ aus dieser Datei importiert werden." }, "source": "special:library", "units": [ @@ -113,7 +114,8 @@ ], "human": { "en": "{quantity} Volt", - "nl": "{quantity} volt" + "nl": "{quantity} volt", + "de": "{quantity} Volt" } } ], @@ -133,7 +135,8 @@ "human": { "en": "{quantity} A", "nl": "{quantity} A", - "ca": "{quantity} A" + "ca": "{quantity} A", + "de": "{quantity} A" } } ], @@ -200,7 +203,8 @@ "humanSingular": { "en": "one centimeter", "nl": "één centimeter", - "ca": "un centímetre" + "ca": "un centímetre", + "de": "ein Zentimeter" } }, { @@ -335,12 +339,14 @@ "human": { "en": "{quantity} minutes", "nl": "{quantity} minuten", - "ca": "{quantity} minuts" + "ca": "{quantity} minuts", + "de": "{quantity} Minuten" }, "humanSingular": { "en": "one minute", "nl": "één minuut", - "ca": "un minut" + "ca": "un minut", + "de": "eine Minute" } }, { @@ -358,12 +364,14 @@ "human": { "en": "{quantity} hours", "nl": "{quantity} uren", - "ca": "{quantity} hores" + "ca": "{quantity} hores", + "de": "{quantity} Stunden" }, "humanSingular": { "en": "one hour", "nl": "één uur", - "ca": "una hora" + "ca": "una hora", + "de": "eine Stunde" } }, { @@ -378,11 +386,13 @@ "human": { "en": "{quantity} days", "nl": "{quantity} day", - "ca": "{quantity} dies" + "ca": "{quantity} dies", + "de": "{quantity} Tage" }, "humanSingular": { "en": "one day", - "nl": "één dag" + "nl": "één dag", + "de": "ein Tag" } } ] diff --git a/assets/layers/usersettings/usersettings.json b/assets/layers/usersettings/usersettings.json index d68e87d5a..3e6673971 100644 --- a/assets/layers/usersettings/usersettings.json +++ b/assets/layers/usersettings/usersettings.json @@ -49,7 +49,7 @@ "icon": "./assets/layers/usersettings/translate_disabled.svg", "then": { "en": "The language was set via an URL-parameter and cannot be set by the user.", - "de": "Die Sprache wurde über einen URL-Parameter gesetzt und kann nicht vom Benutzer eingestellt werden.²", + "de": "Die Sprache wurde über einen URL-Parameter gesetzt und kann nicht vom Benutzer eingestellt werden.", "ca": "L'idioma es va establir mitjançant un paràmetre d'URL i l'usuari no pot definir-lo.", "cs": "Jazyk byl nastaven pomocí parametru URL a uživatel jej nemůže nastavit.²", "nl": "De taal werd ingesteld via een URL-parameter en kan niet manueel ingesteld worden." @@ -83,7 +83,7 @@ "type": "link", "text": { "en": "You have {_unreadMessages} messages
Open your inbox", - "de": "Du hast {_unreadMessages}
Öffne Deinen Posteingang", + "de": "Sie haben {_unreadMessages} Nachrichten
Posteingang öffnen", "ca": "Tens {_unreadMessages} missatges
Obri la safata d'entrada", "cs": "Máte {_unreadMessages}
Otevřít schránku", "nl": "Je hebt {_unreadMessages} ongelezen berichten
Ga naar je inbox" @@ -120,7 +120,8 @@ "id": "a11y-features", "question": { "en": "What accessibility features should be applied?", - "nl": "Wanneer moet de toegankelijkheidsmode ingeschakeld worden?" + "nl": "Wanneer moet de toegankelijkheidsmode ingeschakeld worden?", + "de": "Welche Barrierefrei-Funktionen sollen angewendet werden?" }, "mappings": [ { @@ -129,7 +130,8 @@ "then": { "en": "Enable accessibility features when arrow keys are used to navigate the map", "ca": "Activar les funcions d'accessibilitat quan s'utilitzen les tecles de fletxa per navegar pel mapa", - "nl": "Schakel toegankelijkheidsmode aan wanneer op de pijltjestoetsen wordt geduwd om de kaart te bewegen" + "nl": "Schakel toegankelijkheidsmode aan wanneer op de pijltjestoetsen wordt geduwd om de kaart te bewegen", + "de": "Barrierefrei-Modus aktivieren, wenn Pfeiltasten zum Navigieren in der Karte verwendet werden" } }, { @@ -137,14 +139,16 @@ "then": { "en": "Always enable accessibility features", "ca": "Sempre habilita les característiques d'accessibilitat", - "nl": "Schakel de toegankelijkheidsmode altijd aan" + "nl": "Schakel de toegankelijkheidsmode altijd aan", + "de": "Barrierefrei-Modus immer aktivieren" } }, { "if": "mapcomplete-a11y=never", "then": { "en": "Never enable accessibility features", - "nl": "Gebruik geen toegankelijkheidsmode" + "nl": "Gebruik geen toegankelijkheidsmode", + "de": "Barrierefrei-Modus niemals aktivieren" } } ] diff --git a/assets/themes/mapcomplete-changes/mapcomplete-changes.json b/assets/themes/mapcomplete-changes/mapcomplete-changes.json index 743e2ab97..6c7376596 100644 --- a/assets/themes/mapcomplete-changes/mapcomplete-changes.json +++ b/assets/themes/mapcomplete-changes/mapcomplete-changes.json @@ -1,20 +1,13 @@ { "id": "mapcomplete-changes", "title": { - "en": "Changes made with MapComplete", - "ca": "Canvis fets amb MapComplete", - "de": "Mit MapComplete vorgenommene Änderungen" + "en": "Changes made with MapComplete" }, "shortDescription": { - "en": "Shows changes made by MapComplete", - "ca": "Mostra els canvis fets amb MapComplete", - "de": "Zeigt die von MapComplete vorgenommenen Änderungen an" + "en": "Shows changes made by MapComplete" }, "description": { - "en": "This maps shows all the changes made with MapComplete", - "ca": "Aquest mapa mostra tots els canvis fets amb MapComplete", - "de": "Diese Karte zeigt alle mit MapComplete vorgenommenen Änderungen", - "es": "Este mapa muestra todos los cambios realizados con MapComplete" + "en": "This maps shows all the changes made with MapComplete" }, "icon": "./assets/svg/logo.svg", "hideFromOverview": true, @@ -27,8 +20,7 @@ { "id": "mapcomplete-changes", "name": { - "en": "Changeset centers", - "de": "Zentrum der Änderungssätze" + "en": "Changeset centers" }, "minzoom": 0, "source": { @@ -39,56 +31,41 @@ }, "title": { "render": { - "en": "Changeset for {theme}", - "ca": "Conjunt de canvis per a {theme}", - "de": "Änderungssatz für {theme}" + "en": "Changeset for {theme}" } }, "description": { - "en": "Shows all MapComplete changes", - "ca": "Mostra tots els canvis de MapComplete", - "de": "Zeigt alle MapComplete-Änderungen", - "es": "Muestra todos los cambios de MapComplete" + "en": "Shows all MapComplete changes" }, "tagRenderings": [ { "id": "show_changeset_id", "render": { - "en": "Changeset {id}", - "ca": "Conjunt de canvi {id}", - "de": "Änderungssatz {id}" + "en": "Changeset {id}" } }, { "id": "contributor", "question": { - "en": "What contributor did make this change?", - "ca": "Quin col·laborador va fer aquest canvi?", - "de": "Wer hat diese Änderung vorgenommen?" + "en": "What contributor did make this change?" }, "freeform": { "key": "user" }, "render": { - "en": "Change made by {user}", - "ca": "Canvi fet per {user}", - "de": "Änderung von {user}" + "en": "Change made by {user}" } }, { "id": "theme-id", "question": { - "en": "What theme was used to make this change?", - "ca": "Quin tema es va utilitzar per fer aquest canvi?", - "de": "Welches Theme wurde für diese Änderung verwendet?" + "en": "What theme was used to make this change?" }, "freeform": { "key": "theme" }, "render": { - "en": "Change with theme {theme}", - "ca": "Canvi amb el tema {theme}", - "de": "Geändert mit Thema {theme}" + "en": "Change with theme {theme}" } }, { @@ -97,27 +74,19 @@ "key": "locale" }, "question": { - "en": "What locale (language) was this change made in?", - "ca": "Amb quina configuració regional (idioma) s'ha fet aquest canvi?", - "de": "In welcher Benutzersprache wurde diese Änderung vorgenommen?" + "en": "What locale (language) was this change made in?" }, "render": { - "en": "User locale is {locale}", - "ca": "La configuració regional de l'usuari és {locale}", - "de": "Benutzersprache {locale}" + "en": "User locale is {locale}" } }, { "id": "host", "render": { - "en": "Change with with {host}", - "ca": "Canviat amb {host}", - "de": "Geändert über {host}" + "en": "Change with with {host}" }, "question": { - "en": "What host (website) was this change made with?", - "ca": "Amb quin amfitrió (lloc web) es va fer aquest canvi?", - "de": "Über welchen Host (Webseite) wurde diese Änderung vorgenommen?" + "en": "What host (website) was this change made with?" }, "freeform": { "key": "host" @@ -138,14 +107,10 @@ { "id": "version", "question": { - "en": "What version of MapComplete was used to make this change?", - "ca": "Quina versió de MapComplete es va utilitzar per fer aquest canvi?", - "de": "Welche Version von MapComplete wurde verwendet, um diese Änderung vorzunehmen?" + "en": "What version of MapComplete was used to make this change?" }, "render": { - "en": "Made with {editor}", - "ca": "Fet amb {editor}", - "de": "Erstellt mit {editor}" + "en": "Made with {editor}" }, "freeform": { "key": "editor" @@ -495,10 +460,7 @@ } ], "question": { - "en": "Themename contains {search}", - "ca": "El nom del tema conté {search}", - "de": "Themename enthält {search}", - "es": "El nombre del tema contiene {search}" + "en": "Themename contains {search}" } } ] @@ -514,8 +476,7 @@ } ], "question": { - "en": "Themename does not contain {search}", - "de": "Der Name enthält nicht {search}" + "en": "Themename does not contain {search}" } } ] @@ -531,9 +492,7 @@ } ], "question": { - "en": "Made by contributor {search}", - "ca": "Fet pel col·laborador {search}", - "de": "Der Name enthält nicht {search}" + "en": "Made by contributor {search}" } } ] @@ -549,9 +508,7 @@ } ], "question": { - "en": "Not made by contributor {search}", - "ca": "No fet pel col·laborador {search}", - "de": "Nicht erstellt von {search}" + "en": "Not made by contributor {search}" } } ] @@ -568,9 +525,7 @@ } ], "question": { - "en": "Made before {search}", - "ca": "Fet abans de {search}", - "de": "Erstellt vor {search}" + "en": "Made before {search}" } } ] @@ -587,9 +542,7 @@ } ], "question": { - "en": "Made after {search}", - "ca": "Fet després de {search}", - "de": "Erstellt nach {search}" + "en": "Made after {search}" } } ] @@ -605,9 +558,7 @@ } ], "question": { - "en": "User language (iso-code) {search}", - "ca": "Idioma de l'usuari (codi iso) {search}", - "de": "Benutzersprache (ISO-Code) {search}" + "en": "User language (iso-code) {search}" } } ] @@ -623,9 +574,7 @@ } ], "question": { - "en": "Made with host {search}", - "ca": "Fet amb l'amfitrió {search}", - "de": "Erstellt mit Host {search}" + "en": "Made with host {search}" } } ] @@ -636,9 +585,7 @@ { "osmTags": "add-image>0", "question": { - "en": "Changeset added at least one image", - "ca": "El conjunt de canvis ha afegit almenys una imatge", - "de": "Änderungssatz hat mindestens ein Bild hinzugefügt" + "en": "Changeset added at least one image" } } ] @@ -649,8 +596,7 @@ { "osmTags": "theme!=grb", "question": { - "en": "Exclude GRB theme", - "de": "GRB-Theme ausschließen" + "en": "Exclude GRB theme" } } ] @@ -661,8 +607,7 @@ { "osmTags": "theme!=etymology", "question": { - "en": "Exclude etymology theme", - "de": "Etymologie-Thema ausschließen" + "en": "Exclude etymology theme" } } ] @@ -677,9 +622,7 @@ { "id": "link_to_more", "render": { - "en": "More statistics can be found here", - "ca": "Es pot trobar més estadística aquí", - "de": "Mehr Statistiken gibt es hier" + "en": "More statistics can be found here" } }, { diff --git a/langs/layers/de.json b/langs/layers/de.json index 56f18983c..d5cb439c8 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -35,16 +35,6 @@ "1": { "title": "eine freistehende Posterbox" }, - "10": { - "description": "Verwendet für Werbeschilder, Leuchtreklamen, Logos und institutionelle Eingangsschilder", - "title": "ein Schild" - }, - "11": { - "title": "eine Skulptur" - }, - "12": { - "title": "eine Wandmalerei" - }, "2": { "title": "eine wandmontierte Posterbox" }, @@ -71,6 +61,16 @@ }, "9": { "title": "ein Totem" + }, + "10": { + "description": "Verwendet für Werbeschilder, Leuchtreklamen, Logos und institutionelle Eingangsschilder", + "title": "ein Schild" + }, + "11": { + "title": "eine Skulptur" + }, + "12": { + "title": "eine Wandmalerei" } }, "tagRenderings": { @@ -165,9 +165,6 @@ "1": { "then": "Dies ist ein Brett" }, - "10": { - "then": "Dies ist eine Wandmalerei" - }, "2": { "then": "Dies ist eine Litfaßsäule" }, @@ -191,6 +188,9 @@ }, "9": { "then": "Dies ist ein Totem" + }, + "10": { + "then": "Dies ist eine Wandmalerei" } }, "question": "Welche Art von Werbung ist das?", @@ -205,9 +205,6 @@ "1": { "then": "Brett" }, - "10": { - "then": "Wandmalerei" - }, "2": { "then": "Posterbox" }, @@ -231,6 +228,9 @@ }, "9": { "then": "Totem" + }, + "10": { + "then": "Wandmalerei" } } } @@ -353,15 +353,6 @@ "1": { "then": "Wandbild" }, - "10": { - "then": "Azulejo (spanische dekorative Fliesenarbeit)" - }, - "11": { - "then": "Fliesenarbeit" - }, - "12": { - "then": "Holzschnitzerei" - }, "2": { "then": "Malerei" }, @@ -385,6 +376,15 @@ }, "9": { "then": "Relief" + }, + "10": { + "then": "Azulejo (spanische dekorative Fliesenarbeit)" + }, + "11": { + "then": "Fliesenarbeit" + }, + "12": { + "then": "Holzschnitzerei" } }, "question": "Um welche Art Kunstwerk handelt es sich?", @@ -1942,27 +1942,6 @@ "1": { "question": "Verfügt über einen
Schuko-Stecker ohne Erdungsstift (CEE7/4 Typ F)
" }, - "10": { - "question": "Hat einen
Typ 2 (Mennekes)
Anschluss mit Kabel" - }, - "11": { - "question": "Hat einen
Tesla Supercharger CCS (Typ 2 CSS vonTesla)
Anschluss" - }, - "12": { - "question": "Hat einen
Tesla Supercharger (Destination)
Anschluss" - }, - "13": { - "question": "Hat einen
Tesla Supercharger (Destination) (Typ 2 von Tesla)
Anschluss mit Kabel" - }, - "14": { - "question": "Hat einen
USB-Anschluss zum Aufladen von Telefonen und kleinen Elektrogeräten
" - }, - "15": { - "question": "Hat einen
Bosch Active Connect Anschluss mit 3 Pins
und Kabel" - }, - "16": { - "question": "Hat einen
Bosch Active Connect Anschluss mit 5 Pins
und Kabel" - }, "2": { "question": "Verfügt über einen
europäischen Netzstecker mit Erdungsstift (CEE7/4 Typ E)
Anschluss" }, @@ -1986,6 +1965,27 @@ }, "9": { "question": "Hat einen
Typ 2 CCS (Mennekes)
Anschluss" + }, + "10": { + "question": "Hat einen
Typ 2 (Mennekes)
Anschluss mit Kabel" + }, + "11": { + "question": "Hat einen
Tesla Supercharger CCS (Typ 2 CSS vonTesla)
Anschluss" + }, + "12": { + "question": "Hat einen
Tesla Supercharger (Destination)
Anschluss" + }, + "13": { + "question": "Hat einen
Tesla Supercharger (Destination) (Typ 2 von Tesla)
Anschluss mit Kabel" + }, + "14": { + "question": "Hat einen
USB-Anschluss zum Aufladen von Telefonen und kleinen Elektrogeräten
" + }, + "15": { + "question": "Hat einen
Bosch Active Connect Anschluss mit 3 Pins
und Kabel" + }, + "16": { + "question": "Hat einen
Bosch Active Connect Anschluss mit 5 Pins
und Kabel" } } } @@ -2041,6 +2041,30 @@ "1": { "then": "Schuko-Stecker ohne Erdungsstift (CEE7/4 Typ F)" }, + "2": { + "then": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" + }, + "3": { + "then": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" + }, + "4": { + "then": "Chademo-Anschluss" + }, + "5": { + "then": "Chademo-Anschluss" + }, + "6": { + "then": "Typ 1 mit Kabel (J1772)" + }, + "7": { + "then": "Typ 1 mit Kabel (J1772)" + }, + "8": { + "then": "Typ 1 ohne Kabel (J1772)" + }, + "9": { + "then": " Typ 1 ohne Kabel (J1772)" + }, "10": { "then": "Typ 1 CCS (Typ 1 Combo)" }, @@ -2071,9 +2095,6 @@ "19": { "then": "Typ 2 mit Kabel (mennekes)" }, - "2": { - "then": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" - }, "20": { "then": "Tesla Supercharger CCS (Typ 2 CSS von Tesla)" }, @@ -2104,32 +2125,11 @@ "29": { "then": " Bosch Active Connect mit 3 Pins und Kabel" }, - "3": { - "then": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" - }, "30": { "then": "Bosch Active Connect mit 5 Pins und Kabel" }, "31": { "then": " Bosch Active Connect mit 5 Pins und Kabel" - }, - "4": { - "then": "Chademo-Anschluss" - }, - "5": { - "then": "Chademo-Anschluss" - }, - "6": { - "then": "Typ 1 mit Kabel (J1772)" - }, - "7": { - "then": "Typ 1 mit Kabel (J1772)" - }, - "8": { - "then": "Typ 1 ohne Kabel (J1772)" - }, - "9": { - "then": " Typ 1 ohne Kabel (J1772)" } }, "question": "Welche Ladeanschlüsse gibt es hier?" @@ -2323,24 +2323,6 @@ "1": { "2": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" }, - "10": { - "2": "Tesla Supercharger CCS (Typ 2 CSS von Tesla)" - }, - "11": { - "2": "Tesla Supercharger (Destination)" - }, - "12": { - "2": "Tesla Supercharger (Destination) (Typ 2 mit Kabel von Tesla)" - }, - "13": { - "2": "USB zum Aufladen von Handys und kleinen Elektrogeräten" - }, - "14": { - "2": " Bosch Active Connect mit 3 Pins und Kabel" - }, - "15": { - "2": " Bosch Active Connect mit 5 Pins und Kabel" - }, "2": { "2": "Chademo-Stecker" }, @@ -2364,6 +2346,24 @@ }, "9": { "2": "Typ 2 mit Kabel (Mennekes)" + }, + "10": { + "2": "Tesla Supercharger CCS (Typ 2 CSS von Tesla)" + }, + "11": { + "2": "Tesla Supercharger (Destination)" + }, + "12": { + "2": "Tesla Supercharger (Destination) (Typ 2 mit Kabel von Tesla)" + }, + "13": { + "2": "USB zum Aufladen von Handys und kleinen Elektrogeräten" + }, + "14": { + "2": " Bosch Active Connect mit 3 Pins und Kabel" + }, + "15": { + "2": " Bosch Active Connect mit 5 Pins und Kabel" } } } @@ -3141,15 +3141,6 @@ "1": { "then": "Dieser Radweg hat einen festen Belag" }, - "10": { - "then": "Dieser Radweg besteht aus feinem Schotter" - }, - "11": { - "then": "Der Radweg ist aus Kies" - }, - "12": { - "then": "Dieser Radweg besteht aus Rohboden" - }, "2": { "then": "Der Radweg ist aus Asphalt" }, @@ -3173,6 +3164,15 @@ }, "9": { "then": "Der Radweg ist aus Schotter" + }, + "10": { + "then": "Dieser Radweg besteht aus feinem Schotter" + }, + "11": { + "then": "Der Radweg ist aus Kies" + }, + "12": { + "then": "Dieser Radweg besteht aus Rohboden" } }, "question": "Was ist der Belag dieses Radwegs?", @@ -3221,15 +3221,6 @@ "1": { "then": "Dieser Radweg hat einen festen Belag" }, - "10": { - "then": "Dieser Radweg besteht aus feinem Schotter" - }, - "11": { - "then": "Der Radweg ist aus Kies" - }, - "12": { - "then": "Dieser Radweg besteht aus Rohboden" - }, "2": { "then": "Der Radweg ist aus Asphalt" }, @@ -3253,6 +3244,15 @@ }, "9": { "then": "Der Radweg ist aus Schotter" + }, + "10": { + "then": "Dieser Radweg besteht aus feinem Schotter" + }, + "11": { + "then": "Der Radweg ist aus Kies" + }, + "12": { + "then": "Dieser Radweg besteht aus Rohboden" } }, "question": "Was ist der Belag dieser Straße?", @@ -4207,54 +4207,6 @@ } } }, - "10": { - "options": { - "0": { - "question": "Keine Bevorzugung von Hunden" - }, - "1": { - "question": "Hunde erlaubt" - }, - "2": { - "question": "Keine Hunde erlaubt" - } - } - }, - "11": { - "options": { - "0": { - "question": "Internetzugang vorhanden" - } - } - }, - "12": { - "options": { - "0": { - "question": "Stromanschluss vorhanden" - } - } - }, - "13": { - "options": { - "0": { - "question": "Hat zuckerfreie Angebote" - } - } - }, - "14": { - "options": { - "0": { - "question": "Hat glutenfreie Angebote" - } - } - }, - "15": { - "options": { - "0": { - "question": "Hat laktosefreie Angebote" - } - } - }, "2": { "options": { "0": { @@ -4325,6 +4277,54 @@ "question": "Nutzung kostenlos" } } + }, + "10": { + "options": { + "0": { + "question": "Keine Bevorzugung von Hunden" + }, + "1": { + "question": "Hunde erlaubt" + }, + "2": { + "question": "Keine Hunde erlaubt" + } + } + }, + "11": { + "options": { + "0": { + "question": "Internetzugang vorhanden" + } + } + }, + "12": { + "options": { + "0": { + "question": "Stromanschluss vorhanden" + } + } + }, + "13": { + "options": { + "0": { + "question": "Hat zuckerfreie Angebote" + } + } + }, + "14": { + "options": { + "0": { + "question": "Hat glutenfreie Angebote" + } + } + }, + "15": { + "options": { + "0": { + "question": "Hat laktosefreie Angebote" + } + } } } }, @@ -4444,6 +4444,30 @@ "1": { "then": "Die Fitness-Station hat ein Schild mit Anweisungen für eine bestimmte Übung." }, + "2": { + "then": "Die Fitness-Station hat eine Einrichtung für Sit-ups." + }, + "3": { + "then": "Die Fitness-Station hat eine Vorrichtung für Liegestütze. In der Regel eine oder mehrere niedrige Reckstangen." + }, + "4": { + "then": "Die Fitness-Station hat Stangen zum Dehnen." + }, + "5": { + "then": "Die Fitness-Station hat eine Vorrichtung für Rückenstrecker (Hyperextensions)." + }, + "6": { + "then": "Die Fitness-Station hat Ringe für Gymnastikübungen." + }, + "7": { + "then": "Die Fitness-Station hat eine horizontale Leiter (Monkey Bars)." + }, + "8": { + "then": "Die Fitness-Station hat eine Sprossenwand zum Klettern." + }, + "9": { + "then": "Die Fitness-Station hat Pfosten für Slalomübungen." + }, "10": { "then": "Die Fitness-Station hat Trittsteine." }, @@ -4474,9 +4498,6 @@ "19": { "then": "Die Fitness-Station hat Kampfseile (battle ropes)." }, - "2": { - "then": "Die Fitness-Station hat eine Einrichtung für Sit-ups." - }, "20": { "then": "Die Fitness-Station hat ein Fahrradergometer." }, @@ -4491,27 +4512,6 @@ }, "24": { "then": "Die Fitness-Station hat eine Slackline." - }, - "3": { - "then": "Die Fitness-Station hat eine Vorrichtung für Liegestütze. In der Regel eine oder mehrere niedrige Reckstangen." - }, - "4": { - "then": "Die Fitness-Station hat Stangen zum Dehnen." - }, - "5": { - "then": "Die Fitness-Station hat eine Vorrichtung für Rückenstrecker (Hyperextensions)." - }, - "6": { - "then": "Die Fitness-Station hat Ringe für Gymnastikübungen." - }, - "7": { - "then": "Die Fitness-Station hat eine horizontale Leiter (Monkey Bars)." - }, - "8": { - "then": "Die Fitness-Station hat eine Sprossenwand zum Klettern." - }, - "9": { - "then": "Die Fitness-Station hat Pfosten für Slalomübungen." } }, "question": "Welche Übungsgeräte gibt es an dieser Fitness-Station?" @@ -4631,21 +4631,6 @@ "1": { "then": "Dies ist eine Pommesbude" }, - "10": { - "then": "Hier werden chinesische Gerichte serviert" - }, - "11": { - "then": "Hier werden griechische Gerichte serviert" - }, - "12": { - "then": "Hier werden indische Gerichte serviert" - }, - "13": { - "then": "Hier werden türkische Gerichte serviert" - }, - "14": { - "then": "Hier werden thailändische Gerichte serviert" - }, "2": { "then": "Bietet vorwiegend Pastagerichte an" }, @@ -4669,6 +4654,21 @@ }, "9": { "then": "Hier werden französische Gerichte serviert" + }, + "10": { + "then": "Hier werden chinesische Gerichte serviert" + }, + "11": { + "then": "Hier werden griechische Gerichte serviert" + }, + "12": { + "then": "Hier werden indische Gerichte serviert" + }, + "13": { + "then": "Hier werden türkische Gerichte serviert" + }, + "14": { + "then": "Hier werden thailändische Gerichte serviert" } }, "question": "Was für Essen gibt es hier?", @@ -5360,6 +5360,30 @@ "1": { "then": "Dies ist ein Auditorium" }, + "2": { + "then": "Dies ist ein Schlafzimmer" + }, + "3": { + "then": "Dies ist eine Kapelle" + }, + "4": { + "then": "Dies ist ein Klassenzimmer" + }, + "5": { + "then": "Dies ist ein Klassenzimmer" + }, + "6": { + "then": "Dies ist ein Computerraum" + }, + "7": { + "then": "Dies ist ein Konferenzraum" + }, + "8": { + "then": "Dies ist eine Krypta" + }, + "9": { + "then": "Dies ist eine Küche" + }, "10": { "then": "Dies ist ein Labor" }, @@ -5390,9 +5414,6 @@ "19": { "then": "Dies ist ein Lagerraum" }, - "2": { - "then": "Dies ist ein Schlafzimmer" - }, "20": { "then": "Dies ist ein Technikraum" }, @@ -5401,27 +5422,6 @@ }, "22": { "then": "Dies ist ein Wartezimmer" - }, - "3": { - "then": "Dies ist eine Kapelle" - }, - "4": { - "then": "Dies ist ein Klassenzimmer" - }, - "5": { - "then": "Dies ist ein Klassenzimmer" - }, - "6": { - "then": "Dies ist ein Computerraum" - }, - "7": { - "then": "Dies ist ein Konferenzraum" - }, - "8": { - "then": "Dies ist eine Krypta" - }, - "9": { - "then": "Dies ist eine Küche" } }, "question": "Wie wird dieser Raum genutzt?" @@ -6048,19 +6048,6 @@ } } }, - "10": { - "options": { - "0": { - "question": "Alle Notizen" - }, - "1": { - "question": "Importnotizen ausblenden" - }, - "2": { - "question": "Nur Importnotizen anzeigen" - } - } - }, "2": { "options": { "0": { @@ -6116,6 +6103,19 @@ "question": "Nur offene Notizen anzeigen" } } + }, + "10": { + "options": { + "0": { + "question": "Alle Notizen" + }, + "1": { + "question": "Importnotizen ausblenden" + }, + "2": { + "question": "Nur Importnotizen anzeigen" + } + } } }, "name": "OpenStreetMap-Hinweise", @@ -6440,21 +6440,6 @@ "1": { "then": "Dies ist ein normaler Stellplatz." }, - "10": { - "then": "Dies ist ein Stellplatz, der für Eltern mit Kindern reserviert ist." - }, - "11": { - "then": "Dies ist ein Stellplatz, der für das Personal reserviert ist." - }, - "12": { - "then": "Dies ist ein Stellplatz, der für Taxis reserviert ist." - }, - "13": { - "then": "Dies ist ein Stellplatz, der für Fahrzeuge mit Anhänger reserviert ist." - }, - "14": { - "then": "Dies ist ein Stellplatz, der für Carsharing reserviert ist." - }, "2": { "then": "Dies ist ein Behindertenstellplatz." }, @@ -6478,6 +6463,21 @@ }, "9": { "then": "Dies ist ein Stellplatz, der für Motorräder reserviert ist." + }, + "10": { + "then": "Dies ist ein Stellplatz, der für Eltern mit Kindern reserviert ist." + }, + "11": { + "then": "Dies ist ein Stellplatz, der für das Personal reserviert ist." + }, + "12": { + "then": "Dies ist ein Stellplatz, der für Taxis reserviert ist." + }, + "13": { + "then": "Dies ist ein Stellplatz, der für Fahrzeuge mit Anhänger reserviert ist." + }, + "14": { + "then": "Dies ist ein Stellplatz, der für Carsharing reserviert ist." } }, "question": "Welche Art von Stellplatz ist dies?" @@ -7075,21 +7075,6 @@ "1": { "then": "2-Cent-Münzen werden akzeptiert" }, - "10": { - "then": "20-Centime-Münzen werden akzeptiert" - }, - "11": { - "then": "½-Schweizer Franken-Münzen werden akzeptiert" - }, - "12": { - "then": "1-Schweizer Franken-Münzen werden akzeptiert" - }, - "13": { - "then": "2-Schweizer Franken-Münzen werden akzeptiert" - }, - "14": { - "then": "5-Schweizer Franken-Münzen werden akzeptiert" - }, "2": { "then": "5-Cent-Münzen werden akzeptiert" }, @@ -7113,6 +7098,21 @@ }, "9": { "then": "10-Centime-Münzen werden akzeptiert" + }, + "10": { + "then": "20-Centime-Münzen werden akzeptiert" + }, + "11": { + "then": "½-Schweizer Franken-Münzen werden akzeptiert" + }, + "12": { + "then": "1-Schweizer Franken-Münzen werden akzeptiert" + }, + "13": { + "then": "2-Schweizer Franken-Münzen werden akzeptiert" + }, + "14": { + "then": "5-Schweizer Franken-Münzen werden akzeptiert" } }, "question": "Mit welchen Münzen kann man hier bezahlen?" @@ -7125,15 +7125,6 @@ "1": { "then": "10-Euro-Scheine werden angenommen" }, - "10": { - "then": "100-Schweizer Franken-Scheine werden akzeptiert" - }, - "11": { - "then": "200-Schweizer Franken-Scheine werden akzeptiert" - }, - "12": { - "then": "1000-Schweizer Franken-Scheine werden akzeptiert" - }, "2": { "then": "20-Euro-Scheine werden angenommen" }, @@ -7157,6 +7148,15 @@ }, "9": { "then": "50-Schweizer Franken-Scheine werden akzeptiert" + }, + "10": { + "then": "100-Schweizer Franken-Scheine werden akzeptiert" + }, + "11": { + "then": "200-Schweizer Franken-Scheine werden akzeptiert" + }, + "12": { + "then": "1000-Schweizer Franken-Scheine werden akzeptiert" } }, "question": "Mit welchen Banknoten kann man hier bezahlen?" @@ -7610,6 +7610,30 @@ "1": { "question": "Recycling von Batterien" }, + "2": { + "question": "Recycling von Getränkekartons" + }, + "3": { + "question": "Recycling von Dosen" + }, + "4": { + "question": "Recycling von Kleidung" + }, + "5": { + "question": "Recycling von Speiseöl" + }, + "6": { + "question": "Recycling von Motoröl" + }, + "7": { + "question": "Recycling von Leuchtstoffröhren" + }, + "8": { + "question": "Recycling von Grünabfällen" + }, + "9": { + "question": "Recycling von Glasflaschen" + }, "10": { "question": "Recycling von Glas" }, @@ -7640,35 +7664,11 @@ "19": { "question": "Recycling von Restabfällen" }, - "2": { - "question": "Recycling von Getränkekartons" - }, "20": { "question": "Recycling von Druckerpatronen" }, "21": { "question": "Recycling von Fahrrädern" - }, - "3": { - "question": "Recycling von Dosen" - }, - "4": { - "question": "Recycling von Kleidung" - }, - "5": { - "question": "Recycling von Speiseöl" - }, - "6": { - "question": "Recycling von Motoröl" - }, - "7": { - "question": "Recycling von Leuchtstoffröhren" - }, - "8": { - "question": "Recycling von Grünabfällen" - }, - "9": { - "question": "Recycling von Glasflaschen" } } }, @@ -7736,6 +7736,30 @@ "1": { "then": "Getränkekartons können hier recycelt werden" }, + "2": { + "then": "Dosen können hier recycelt werden" + }, + "3": { + "then": "Kleidung kann hier recycelt werden" + }, + "4": { + "then": "Speiseöl kann hier recycelt werden" + }, + "5": { + "then": "Motoröl kann hier recycelt werden" + }, + "6": { + "then": "Hier können Leuchtstoffröhren recycelt werden" + }, + "7": { + "then": "Grünabfälle können hier recycelt werden" + }, + "8": { + "then": "Bio-Abfall kann hier recycelt werden" + }, + "9": { + "then": "Glasflaschen können hier recycelt werden" + }, "10": { "then": "Glas kann hier recycelt werden" }, @@ -7766,9 +7790,6 @@ "19": { "then": "Schuhe können hier recycelt werden" }, - "2": { - "then": "Dosen können hier recycelt werden" - }, "20": { "then": "Elektrokleingeräte können hier recycelt werden" }, @@ -7783,27 +7804,6 @@ }, "24": { "then": "Fahrräder können hier recycelt werden" - }, - "3": { - "then": "Kleidung kann hier recycelt werden" - }, - "4": { - "then": "Speiseöl kann hier recycelt werden" - }, - "5": { - "then": "Motoröl kann hier recycelt werden" - }, - "6": { - "then": "Hier können Leuchtstoffröhren recycelt werden" - }, - "7": { - "then": "Grünabfälle können hier recycelt werden" - }, - "8": { - "then": "Bio-Abfall kann hier recycelt werden" - }, - "9": { - "then": "Glasflaschen können hier recycelt werden" } }, "question": "Was kann hier recycelt werden?" @@ -8711,12 +8711,6 @@ "1": { "then": "Diese Straßenlaterne verwendet LEDs" }, - "10": { - "then": "Diese Straßenlaterne verwendet Hochdruck-Natriumdampflampen (orange mit weiß)" - }, - "11": { - "then": "Diese Straßenlaterne wird mit Gas beleuchtet" - }, "2": { "then": "Diese Straßenlaterne verwendet Glühlampenlicht" }, @@ -8740,6 +8734,12 @@ }, "9": { "then": "Diese Straßenlaterne verwendet Niederdruck-Natriumdampflampen (einfarbig orange)" + }, + "10": { + "then": "Diese Straßenlaterne verwendet Hochdruck-Natriumdampflampen (orange mit weiß)" + }, + "11": { + "then": "Diese Straßenlaterne wird mit Gas beleuchtet" } }, "question": "Mit welcher Art von Beleuchtung arbeitet diese Straßenlaterne?" @@ -9989,6 +9989,30 @@ "1": { "question": "Verkauf von Getränken" }, + "2": { + "question": "Verkauf von Süßigkeiten" + }, + "3": { + "question": "Verkauf von Lebensmitteln" + }, + "4": { + "question": "Verkauf von Zigaretten" + }, + "5": { + "question": "Verkauf von Kondomen" + }, + "6": { + "question": "Verkauf von Kaffee" + }, + "7": { + "question": "Verkauf von Trinkwasser" + }, + "8": { + "question": "Verkauf von Zeitungen" + }, + "9": { + "question": "Verkauf von Fahrradschläuchen" + }, "10": { "question": "Verkauf von Milch" }, @@ -10019,9 +10043,6 @@ "19": { "question": "Verkauf von Blumen" }, - "2": { - "question": "Verkauf von Süßigkeiten" - }, "20": { "question": "Verkauf von Parkscheinen" }, @@ -10045,27 +10066,6 @@ }, "27": { "question": "Verkauf von Fahrradschlössern" - }, - "3": { - "question": "Verkauf von Lebensmitteln" - }, - "4": { - "question": "Verkauf von Zigaretten" - }, - "5": { - "question": "Verkauf von Kondomen" - }, - "6": { - "question": "Verkauf von Kaffee" - }, - "7": { - "question": "Verkauf von Trinkwasser" - }, - "8": { - "question": "Verkauf von Zeitungen" - }, - "9": { - "question": "Verkauf von Fahrradschläuchen" } } } @@ -10112,6 +10112,30 @@ "1": { "then": "Süßigkeiten werden verkauft" }, + "2": { + "then": "Lebensmittel werden verkauft" + }, + "3": { + "then": "Zigaretten werden verkauft" + }, + "4": { + "then": "Kondome werden verkauft" + }, + "5": { + "then": "Kaffee wird verkauft" + }, + "6": { + "then": "Trinkwasser wird verkauft" + }, + "7": { + "then": "Zeitungen werden verkauft" + }, + "8": { + "then": "Fahrradschläuche werden verkauft" + }, + "9": { + "then": "Milch wird verkauft" + }, "10": { "then": "Brot wird verkauft" }, @@ -10142,9 +10166,6 @@ "19": { "then": "Parkscheine werden verkauft" }, - "2": { - "then": "Lebensmittel werden verkauft" - }, "20": { "then": "Souvenirmünzen werden verkauft" }, @@ -10165,27 +10186,6 @@ }, "26": { "then": "Fahrradschlösser werden verkauft" - }, - "3": { - "then": "Zigaretten werden verkauft" - }, - "4": { - "then": "Kondome werden verkauft" - }, - "5": { - "then": "Kaffee wird verkauft" - }, - "6": { - "then": "Trinkwasser wird verkauft" - }, - "7": { - "then": "Zeitungen werden verkauft" - }, - "8": { - "then": "Fahrradschläuche werden verkauft" - }, - "9": { - "then": "Milch wird verkauft" } }, "question": "Was wird in diesem Automaten verkauft?", @@ -10477,4 +10477,4 @@ "render": "Windrad" } } -} +} \ No newline at end of file diff --git a/langs/layers/en.json b/langs/layers/en.json index 96aaac477..250ee88db 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -35,16 +35,6 @@ "1": { "title": "a freestanding poster box" }, - "10": { - "description": "Used for advertising signs, neon signs, logos & institutional entrance signs", - "title": "a sign" - }, - "11": { - "title": "a sculpture" - }, - "12": { - "title": "a wall painting" - }, "2": { "title": "a poster box mounted on a wall" }, @@ -71,6 +61,16 @@ }, "9": { "title": "a totem" + }, + "10": { + "description": "Used for advertising signs, neon signs, logos & institutional entrance signs", + "title": "a sign" + }, + "11": { + "title": "a sculpture" + }, + "12": { + "title": "a wall painting" } }, "tagRenderings": { @@ -165,9 +165,6 @@ "1": { "then": "This is a board" }, - "10": { - "then": "This is a wall painting" - }, "2": { "then": "This is a column" }, @@ -191,6 +188,9 @@ }, "9": { "then": "This is a totem" + }, + "10": { + "then": "This is a wall painting" } }, "question": "Which type of advertising feature is this?", @@ -205,9 +205,6 @@ "1": { "then": "Board" }, - "10": { - "then": "Wall painting" - }, "2": { "then": "Poster Box" }, @@ -231,6 +228,9 @@ }, "9": { "then": "Totem" + }, + "10": { + "then": "Wall painting" } } } @@ -353,15 +353,6 @@ "1": { "then": "Mural" }, - "10": { - "then": "Azulejo (Spanish decorative tilework)" - }, - "11": { - "then": "Tilework" - }, - "12": { - "then": "Woodcarving" - }, "2": { "then": "Painting" }, @@ -385,6 +376,15 @@ }, "9": { "then": "Relief" + }, + "10": { + "then": "Azulejo (Spanish decorative tilework)" + }, + "11": { + "then": "Tilework" + }, + "12": { + "then": "Woodcarving" } }, "question": "What is the type of this artwork?", @@ -1942,27 +1942,6 @@ "1": { "question": "Has a
Schuko wall plug without ground pin (CEE7/4 type F)
connector" }, - "10": { - "question": "Has a
Type 2 with cable (mennekes)
connector" - }, - "11": { - "question": "Has a
Tesla Supercharger CCS (a branded type2_css)
connector" - }, - "12": { - "question": "Has a
Tesla Supercharger (destination)
connector" - }, - "13": { - "question": "Has a
Tesla supercharger (destination) (A Type 2 with cable branded as tesla)
connector" - }, - "14": { - "question": "Has a
USB to charge phones and small electronics
connector" - }, - "15": { - "question": "Has a
Bosch Active Connect with 3 pins and cable
connector" - }, - "16": { - "question": "Has a
Bosch Active Connect with 5 pins and cable
connector" - }, "2": { "question": "Has a
European wall plug with ground pin (CEE7/4 type E)
connector" }, @@ -1986,6 +1965,27 @@ }, "9": { "question": "Has a
Type 2 CCS (mennekes)
connector" + }, + "10": { + "question": "Has a
Type 2 with cable (mennekes)
connector" + }, + "11": { + "question": "Has a
Tesla Supercharger CCS (a branded type2_css)
connector" + }, + "12": { + "question": "Has a
Tesla Supercharger (destination)
connector" + }, + "13": { + "question": "Has a
Tesla supercharger (destination) (A Type 2 with cable branded as tesla)
connector" + }, + "14": { + "question": "Has a
USB to charge phones and small electronics
connector" + }, + "15": { + "question": "Has a
Bosch Active Connect with 3 pins and cable
connector" + }, + "16": { + "question": "Has a
Bosch Active Connect with 5 pins and cable
connector" } } } @@ -2041,6 +2041,30 @@ "1": { "then": "Schuko wall plug without ground pin (CEE7/4 type F)" }, + "2": { + "then": "European wall plug with ground pin (CEE7/4 type E)" + }, + "3": { + "then": "European wall plug with ground pin (CEE7/4 type E)" + }, + "4": { + "then": "Chademo" + }, + "5": { + "then": "Chademo" + }, + "6": { + "then": "Type 1 with cable (J1772)" + }, + "7": { + "then": "Type 1 with cable (J1772)" + }, + "8": { + "then": "Type 1 without cable (J1772)" + }, + "9": { + "then": "Type 1 without cable (J1772)" + }, "10": { "then": "Type 1 CCS (aka Type 1 Combo)" }, @@ -2071,9 +2095,6 @@ "19": { "then": "Type 2 with cable (mennekes)" }, - "2": { - "then": "European wall plug with ground pin (CEE7/4 type E)" - }, "20": { "then": "Tesla Supercharger CCS (a branded type2_css)" }, @@ -2104,32 +2125,11 @@ "29": { "then": "Bosch Active Connect with 3 pins and cable" }, - "3": { - "then": "European wall plug with ground pin (CEE7/4 type E)" - }, "30": { "then": "Bosch Active Connect with 5 pins and cable" }, "31": { "then": "Bosch Active Connect with 5 pins and cable" - }, - "4": { - "then": "Chademo" - }, - "5": { - "then": "Chademo" - }, - "6": { - "then": "Type 1 with cable (J1772)" - }, - "7": { - "then": "Type 1 with cable (J1772)" - }, - "8": { - "then": "Type 1 without cable (J1772)" - }, - "9": { - "then": "Type 1 without cable (J1772)" } }, "question": "Which charging connections are available here?" @@ -2323,24 +2323,6 @@ "1": { "2": "European wall plug with ground pin (CEE7/4 type E)" }, - "10": { - "2": "Tesla Supercharger CCS (a branded type2_css)" - }, - "11": { - "2": "Tesla Supercharger (destination)" - }, - "12": { - "2": "Tesla supercharger (destination) (A Type 2 with cable branded as tesla)" - }, - "13": { - "2": "USB to charge phones and small electronics" - }, - "14": { - "2": "Bosch Active Connect with 3 pins and cable" - }, - "15": { - "2": "Bosch Active Connect with 5 pins and cable" - }, "2": { "2": "Chademo" }, @@ -2364,6 +2346,24 @@ }, "9": { "2": "Type 2 with cable (mennekes)" + }, + "10": { + "2": "Tesla Supercharger CCS (a branded type2_css)" + }, + "11": { + "2": "Tesla Supercharger (destination)" + }, + "12": { + "2": "Tesla supercharger (destination) (A Type 2 with cable branded as tesla)" + }, + "13": { + "2": "USB to charge phones and small electronics" + }, + "14": { + "2": "Bosch Active Connect with 3 pins and cable" + }, + "15": { + "2": "Bosch Active Connect with 5 pins and cable" } } } @@ -3141,15 +3141,6 @@ "1": { "then": "This cycleway is paved" }, - "10": { - "then": "This cycleway is made of fine gravel" - }, - "11": { - "then": "This cycleway is made of pebblestone" - }, - "12": { - "then": "This cycleway is made from raw ground" - }, "2": { "then": "This cycleway is made of asphalt" }, @@ -3173,6 +3164,15 @@ }, "9": { "then": "This cycleway is made of gravel" + }, + "10": { + "then": "This cycleway is made of fine gravel" + }, + "11": { + "then": "This cycleway is made of pebblestone" + }, + "12": { + "then": "This cycleway is made from raw ground" } }, "question": "What is the surface of the cycleway made from?", @@ -3221,15 +3221,6 @@ "1": { "then": "This cycleway is paved" }, - "10": { - "then": "This cycleway is made of fine gravel" - }, - "11": { - "then": "This cycleway is made of pebblestone" - }, - "12": { - "then": "This cycleway is made from raw ground" - }, "2": { "then": "This cycleway is made of asphalt" }, @@ -3253,6 +3244,15 @@ }, "9": { "then": "This cycleway is made of gravel" + }, + "10": { + "then": "This cycleway is made of fine gravel" + }, + "11": { + "then": "This cycleway is made of pebblestone" + }, + "12": { + "then": "This cycleway is made from raw ground" } }, "question": "What is the surface of the street made from?", @@ -4207,54 +4207,6 @@ } } }, - "10": { - "options": { - "0": { - "question": "No preference towards dogs" - }, - "1": { - "question": "Dogs allowed" - }, - "2": { - "question": "No dogs allowed" - } - } - }, - "11": { - "options": { - "0": { - "question": "Offers internet" - } - } - }, - "12": { - "options": { - "0": { - "question": "Offers electricity" - } - } - }, - "13": { - "options": { - "0": { - "question": "Has a sugar-free offering" - } - } - }, - "14": { - "options": { - "0": { - "question": "Has a gluten free offering" - } - } - }, - "15": { - "options": { - "0": { - "question": "Has a lactose free offering" - } - } - }, "2": { "options": { "0": { @@ -4325,6 +4277,54 @@ "question": "Free to use" } } + }, + "10": { + "options": { + "0": { + "question": "No preference towards dogs" + }, + "1": { + "question": "Dogs allowed" + }, + "2": { + "question": "No dogs allowed" + } + } + }, + "11": { + "options": { + "0": { + "question": "Offers internet" + } + } + }, + "12": { + "options": { + "0": { + "question": "Offers electricity" + } + } + }, + "13": { + "options": { + "0": { + "question": "Has a sugar-free offering" + } + } + }, + "14": { + "options": { + "0": { + "question": "Has a gluten free offering" + } + } + }, + "15": { + "options": { + "0": { + "question": "Has a lactose free offering" + } + } } } }, @@ -4444,6 +4444,30 @@ "1": { "then": "This fitness station has a sign with instructions for a specific exercise." }, + "2": { + "then": "This fitness station has a facility for sit-ups." + }, + "3": { + "then": "This fitness station has a facility for push-ups. Usually consists of one or more low horizontal bars." + }, + "4": { + "then": "This fitness station has bars for stretching." + }, + "5": { + "then": "This fitness station has a station for making hyperextensions." + }, + "6": { + "then": "This fitness station has rings for gymnastic exercises." + }, + "7": { + "then": "This fitness station has a horizontal ladder, also known as monkey bars." + }, + "8": { + "then": "This fitness station has wall bars to climb on." + }, + "9": { + "then": "This fitness station has posts for performing slalom exercises." + }, "10": { "then": "This fitness station has stepping stones." }, @@ -4474,9 +4498,6 @@ "19": { "then": "This fitness station has battling ropes." }, - "2": { - "then": "This fitness station has a facility for sit-ups." - }, "20": { "then": "This fitness station has a stationary bicycle." }, @@ -4491,27 +4512,6 @@ }, "24": { "then": "This fitness station has a slackline." - }, - "3": { - "then": "This fitness station has a facility for push-ups. Usually consists of one or more low horizontal bars." - }, - "4": { - "then": "This fitness station has bars for stretching." - }, - "5": { - "then": "This fitness station has a station for making hyperextensions." - }, - "6": { - "then": "This fitness station has rings for gymnastic exercises." - }, - "7": { - "then": "This fitness station has a horizontal ladder, also known as monkey bars." - }, - "8": { - "then": "This fitness station has wall bars to climb on." - }, - "9": { - "then": "This fitness station has posts for performing slalom exercises." } }, "question": "What kind of equipment does this fitness station have?" @@ -4631,21 +4631,6 @@ "1": { "then": "This is a friture" }, - "10": { - "then": "Chinese dishes are served here" - }, - "11": { - "then": "Greek dishes are served here" - }, - "12": { - "then": "Indian dishes are served here" - }, - "13": { - "then": "Turkish dishes are served here" - }, - "14": { - "then": "Thai dishes are served here" - }, "2": { "then": "Mainly serves pasta" }, @@ -4669,6 +4654,21 @@ }, "9": { "then": "French dishes are served here" + }, + "10": { + "then": "Chinese dishes are served here" + }, + "11": { + "then": "Greek dishes are served here" + }, + "12": { + "then": "Indian dishes are served here" + }, + "13": { + "then": "Turkish dishes are served here" + }, + "14": { + "then": "Thai dishes are served here" } }, "question": "What kind of food is served here?", @@ -5360,6 +5360,30 @@ "1": { "then": "This is a auditorium" }, + "2": { + "then": "This is a bedroom" + }, + "3": { + "then": "This is a chapel" + }, + "4": { + "then": "This is a classroom" + }, + "5": { + "then": "This is a classroom" + }, + "6": { + "then": "This is a computer room" + }, + "7": { + "then": "This is a conference room" + }, + "8": { + "then": "This is a crypt" + }, + "9": { + "then": "This is a kitchen" + }, "10": { "then": "This is a laboratory" }, @@ -5390,9 +5414,6 @@ "19": { "then": "This is a storage room" }, - "2": { - "then": "This is a bedroom" - }, "20": { "then": "This is a technical room" }, @@ -5401,27 +5422,6 @@ }, "22": { "then": "This is a waiting room" - }, - "3": { - "then": "This is a chapel" - }, - "4": { - "then": "This is a classroom" - }, - "5": { - "then": "This is a classroom" - }, - "6": { - "then": "This is a computer room" - }, - "7": { - "then": "This is a conference room" - }, - "8": { - "then": "This is a crypt" - }, - "9": { - "then": "This is a kitchen" } }, "question": "What type of room is this?" @@ -6048,19 +6048,6 @@ } } }, - "10": { - "options": { - "0": { - "question": "All Notes" - }, - "1": { - "question": "Hide import notes" - }, - "2": { - "question": "Show only import Notes" - } - } - }, "2": { "options": { "0": { @@ -6116,6 +6103,19 @@ "question": "Only show open notes" } } + }, + "10": { + "options": { + "0": { + "question": "All Notes" + }, + "1": { + "question": "Hide import notes" + }, + "2": { + "question": "Show only import Notes" + } + } } }, "name": "OpenStreetMap notes", @@ -6440,21 +6440,6 @@ "1": { "then": "This is a normal parking space." }, - "10": { - "then": "This is a parking space reserved for parents with children." - }, - "11": { - "then": "This is a parking space reserved for staff." - }, - "12": { - "then": "This is a parking space reserved for taxis." - }, - "13": { - "then": "This is a parking space reserved for vehicles towing a trailer." - }, - "14": { - "then": "This is a parking space reserved for car sharing." - }, "2": { "then": "This is a disabled parking space." }, @@ -6478,6 +6463,21 @@ }, "9": { "then": "This is parking space reserved for motorcycles." + }, + "10": { + "then": "This is a parking space reserved for parents with children." + }, + "11": { + "then": "This is a parking space reserved for staff." + }, + "12": { + "then": "This is a parking space reserved for taxis." + }, + "13": { + "then": "This is a parking space reserved for vehicles towing a trailer." + }, + "14": { + "then": "This is a parking space reserved for car sharing." } }, "question": "What kind of parking space is this?" @@ -7075,21 +7075,6 @@ "1": { "then": "2 cent coins are accepted" }, - "10": { - "then": "20 centimes coins are accepted" - }, - "11": { - "then": "½ franc coins are accepted" - }, - "12": { - "then": "1 franc coins are accepted" - }, - "13": { - "then": "2 francs coins are accepted" - }, - "14": { - "then": "5 francs coins are accepted" - }, "2": { "then": "5 cent coins are accepted" }, @@ -7113,6 +7098,21 @@ }, "9": { "then": "10 centimes coins are accepted" + }, + "10": { + "then": "20 centimes coins are accepted" + }, + "11": { + "then": "½ franc coins are accepted" + }, + "12": { + "then": "1 franc coins are accepted" + }, + "13": { + "then": "2 francs coins are accepted" + }, + "14": { + "then": "5 francs coins are accepted" } }, "question": "What coins can you use to pay here?" @@ -7125,15 +7125,6 @@ "1": { "then": "10 euro notes are accepted" }, - "10": { - "then": "100 francs notes are accepted" - }, - "11": { - "then": "200 francs notes are accepted" - }, - "12": { - "then": "1000 francs notes are accepted" - }, "2": { "then": "20 euro notes are accepted" }, @@ -7157,6 +7148,15 @@ }, "9": { "then": "50 francs notes are accepted" + }, + "10": { + "then": "100 francs notes are accepted" + }, + "11": { + "then": "200 francs notes are accepted" + }, + "12": { + "then": "1000 francs notes are accepted" } }, "question": "what notes can you use to pay here?" @@ -7610,6 +7610,30 @@ "1": { "question": "Recycling of batteries" }, + "2": { + "question": "Recycling of beverage cartons" + }, + "3": { + "question": "Recycling of cans" + }, + "4": { + "question": "Recycling of clothes" + }, + "5": { + "question": "Recycling of cooking oil" + }, + "6": { + "question": "Recycling of engine oil" + }, + "7": { + "question": "Recycling of fluorescent tubes" + }, + "8": { + "question": "Recycling of green waste" + }, + "9": { + "question": "Recycling of glass bottles" + }, "10": { "question": "Recycling of glass" }, @@ -7640,35 +7664,11 @@ "19": { "question": "Recycling of residual waste" }, - "2": { - "question": "Recycling of beverage cartons" - }, "20": { "question": "Recycling of printer cartridges" }, "21": { "question": "Recycling of bicycles" - }, - "3": { - "question": "Recycling of cans" - }, - "4": { - "question": "Recycling of clothes" - }, - "5": { - "question": "Recycling of cooking oil" - }, - "6": { - "question": "Recycling of engine oil" - }, - "7": { - "question": "Recycling of fluorescent tubes" - }, - "8": { - "question": "Recycling of green waste" - }, - "9": { - "question": "Recycling of glass bottles" } } }, @@ -7736,6 +7736,30 @@ "1": { "then": "Beverage cartons can be recycled here" }, + "2": { + "then": "Cans can be recycled here" + }, + "3": { + "then": "Clothes can be recycled here" + }, + "4": { + "then": "Cooking oil can be recycled here" + }, + "5": { + "then": "Engine oil can be recycled here" + }, + "6": { + "then": "Fluorescent tubes can be recycled here" + }, + "7": { + "then": "Green waste can be recycled here" + }, + "8": { + "then": "Organic waste can be recycled here" + }, + "9": { + "then": "Glass bottles can be recycled here" + }, "10": { "then": "Glass can be recycled here" }, @@ -7766,9 +7790,6 @@ "19": { "then": "Shoes can be recycled here" }, - "2": { - "then": "Cans can be recycled here" - }, "20": { "then": "Small electrical appliances can be recycled here" }, @@ -7783,27 +7804,6 @@ }, "24": { "then": "Bicycles can be recycled here" - }, - "3": { - "then": "Clothes can be recycled here" - }, - "4": { - "then": "Cooking oil can be recycled here" - }, - "5": { - "then": "Engine oil can be recycled here" - }, - "6": { - "then": "Fluorescent tubes can be recycled here" - }, - "7": { - "then": "Green waste can be recycled here" - }, - "8": { - "then": "Organic waste can be recycled here" - }, - "9": { - "then": "Glass bottles can be recycled here" } }, "question": "What can be recycled here?" @@ -8711,12 +8711,6 @@ "1": { "then": "This lamp uses LEDs" }, - "10": { - "then": "This lamp uses high pressure sodium lamps (orange with white)" - }, - "11": { - "then": "This lamp is lit using gas" - }, "2": { "then": "This lamp uses incandescent lighting" }, @@ -8740,6 +8734,12 @@ }, "9": { "then": "This lamp uses low pressure sodium lamps (monochrome orange)" + }, + "10": { + "then": "This lamp uses high pressure sodium lamps (orange with white)" + }, + "11": { + "then": "This lamp is lit using gas" } }, "question": "What kind of lighting does this lamp use?" @@ -9989,6 +9989,30 @@ "1": { "question": "Sale of drinks" }, + "2": { + "question": "Sale of sweets" + }, + "3": { + "question": "Sale of food" + }, + "4": { + "question": "Sale of cigarettes" + }, + "5": { + "question": "Sale of condoms" + }, + "6": { + "question": "Sale of coffee" + }, + "7": { + "question": "Sale of water" + }, + "8": { + "question": "Sale of newspapers" + }, + "9": { + "question": "Sale of bicycle inner tubes" + }, "10": { "question": "Sale of milk" }, @@ -10019,9 +10043,6 @@ "19": { "question": "Sale of flowers" }, - "2": { - "question": "Sale of sweets" - }, "20": { "question": "Sale of parking tickets" }, @@ -10045,27 +10066,6 @@ }, "27": { "question": "Sale of bicycle locks" - }, - "3": { - "question": "Sale of food" - }, - "4": { - "question": "Sale of cigarettes" - }, - "5": { - "question": "Sale of condoms" - }, - "6": { - "question": "Sale of coffee" - }, - "7": { - "question": "Sale of water" - }, - "8": { - "question": "Sale of newspapers" - }, - "9": { - "question": "Sale of bicycle inner tubes" } } } @@ -10112,6 +10112,30 @@ "1": { "then": "Sweets are sold" }, + "2": { + "then": "Food is sold" + }, + "3": { + "then": "Cigarettes are sold" + }, + "4": { + "then": "Condoms are sold" + }, + "5": { + "then": "Coffee is sold" + }, + "6": { + "then": "Drinking water is sold" + }, + "7": { + "then": "Newspapers are sold" + }, + "8": { + "then": "Bicycle inner tubes are sold" + }, + "9": { + "then": "Milk is sold" + }, "10": { "then": "Bread is sold" }, @@ -10142,9 +10166,6 @@ "19": { "then": "Parking tickets are sold" }, - "2": { - "then": "Food is sold" - }, "20": { "then": "Pressed pennies are sold" }, @@ -10165,27 +10186,6 @@ }, "26": { "then": "Bicycle locks are sold" - }, - "3": { - "then": "Cigarettes are sold" - }, - "4": { - "then": "Condoms are sold" - }, - "5": { - "then": "Coffee is sold" - }, - "6": { - "then": "Drinking water is sold" - }, - "7": { - "then": "Newspapers are sold" - }, - "8": { - "then": "Bicycle inner tubes are sold" - }, - "9": { - "then": "Milk is sold" } }, "question": "What does this vending machine sell?", @@ -10477,4 +10477,4 @@ "render": "wind turbine" } } -} +} \ No newline at end of file diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 79f1d1a82..dbbc87fc1 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -27,9 +27,6 @@ "advertising": { "name": "Reclame", "presets": { - "12": { - "title": "een muurschildering" - }, "3": { "description": "Een klein uithangbord voor buurtadvertenties, meestal gericht op voetgangers", "title": "een uithangbord" @@ -50,6 +47,9 @@ "8": { "description": "Een stuk groot, weerbestendig textiel met opgedrukte reclameboodschap die permanent aan de muur hangt", "title": "een spandoek" + }, + "12": { + "title": "een muurschildering" } }, "tagRenderings": { @@ -107,9 +107,6 @@ }, "title": { "mappings": { - "10": { - "then": "Muurschildering" - }, "3": { "then": "Aanplakzuil" }, @@ -127,6 +124,9 @@ }, "9": { "then": "Aanplakzuil" + }, + "10": { + "then": "Muurschildering" } } } @@ -208,15 +208,6 @@ "1": { "then": "Muurschildering" }, - "10": { - "then": "Azulejo (Spaanse siertegels)" - }, - "11": { - "then": "Tegelwerk" - }, - "12": { - "then": "Houtsculptuur" - }, "2": { "then": "Schilderij" }, @@ -240,6 +231,15 @@ }, "9": { "then": "Reliëf" + }, + "10": { + "then": "Azulejo (Spaanse siertegels)" + }, + "11": { + "then": "Tegelwerk" + }, + "12": { + "then": "Houtsculptuur" } }, "question": "Wat voor soort kunstwerk is dit?", @@ -1791,27 +1791,6 @@ "1": { "question": "Heeft een
Schuko stekker zonder aardingspin (CEE7/4 type F)
" }, - "10": { - "question": "Heeft een
Type 2 met kabel (J1772)
" - }, - "11": { - "question": "Heeft een
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "12": { - "question": "Heeft een
Tesla Supercharger (destination)
" - }, - "13": { - "question": "Heeft een
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "14": { - "question": "Heeft een
USB om GSMs en kleine electronica op te laden
" - }, - "15": { - "question": "Heeft een
Bosch Active Connect met 3 pinnen aan een kabel
" - }, - "16": { - "question": "Heeft een
Bosch Active Connect met 5 pinnen aan een kabel
" - }, "2": { "question": "Heeft een
Europese stekker met aardingspin (CEE7/4 type E)
" }, @@ -1835,6 +1814,27 @@ }, "9": { "question": "Heeft een
Type 2 CCS (mennekes)
" + }, + "10": { + "question": "Heeft een
Type 2 met kabel (J1772)
" + }, + "11": { + "question": "Heeft een
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "12": { + "question": "Heeft een
Tesla Supercharger (destination)
" + }, + "13": { + "question": "Heeft een
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "14": { + "question": "Heeft een
USB om GSMs en kleine electronica op te laden
" + }, + "15": { + "question": "Heeft een
Bosch Active Connect met 3 pinnen aan een kabel
" + }, + "16": { + "question": "Heeft een
Bosch Active Connect met 5 pinnen aan een kabel
" } } } @@ -1890,6 +1890,30 @@ "1": { "then": "Schuko stekker zonder aardingspin (CEE7/4 type F)" }, + "2": { + "then": "Europese stekker met aardingspin (CEE7/4 type E)" + }, + "3": { + "then": "Europese stekker met aardingspin (CEE7/4 type E)" + }, + "4": { + "then": "Chademo" + }, + "5": { + "then": "Chademo" + }, + "6": { + "then": "Type 1 met kabel (J1772)" + }, + "7": { + "then": "Type 1 met kabel (J1772)" + }, + "8": { + "then": "Type 1 zonder kabel (J1772)" + }, + "9": { + "then": "Type 1 zonder kabel (J1772)" + }, "10": { "then": "Type 1 CCS (ook gekend als Type 1 Combo)" }, @@ -1920,9 +1944,6 @@ "19": { "then": "Type 2 met kabel (J1772)" }, - "2": { - "then": "Europese stekker met aardingspin (CEE7/4 type E)" - }, "20": { "then": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" }, @@ -1953,32 +1974,11 @@ "29": { "then": "Bosch Active Connect met 3 pinnen aan een kabel" }, - "3": { - "then": "Europese stekker met aardingspin (CEE7/4 type E)" - }, "30": { "then": "Bosch Active Connect met 5 pinnen aan een kabel" }, "31": { "then": "Bosch Active Connect met 5 pinnen aan een kabel" - }, - "4": { - "then": "Chademo" - }, - "5": { - "then": "Chademo" - }, - "6": { - "then": "Type 1 met kabel (J1772)" - }, - "7": { - "then": "Type 1 met kabel (J1772)" - }, - "8": { - "then": "Type 1 zonder kabel (J1772)" - }, - "9": { - "then": "Type 1 zonder kabel (J1772)" } }, "question": "Welke aansluitingen zijn hier beschikbaar?" @@ -2172,24 +2172,6 @@ "1": { "2": "Europese stekker met aardingspin (CEE7/4 type E)" }, - "10": { - "2": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" - }, - "11": { - "2": "Tesla Supercharger (destination)" - }, - "12": { - "2": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)" - }, - "13": { - "2": "USB om GSMs en kleine electronica op te laden" - }, - "14": { - "2": "Bosch Active Connect met 3 pinnen aan een kabel" - }, - "15": { - "2": "Bosch Active Connect met 5 pinnen aan een kabel" - }, "2": { "2": "Chademo" }, @@ -2213,6 +2195,24 @@ }, "9": { "2": "Type 2 met kabel (J1772)" + }, + "10": { + "2": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" + }, + "11": { + "2": "Tesla Supercharger (destination)" + }, + "12": { + "2": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)" + }, + "13": { + "2": "USB om GSMs en kleine electronica op te laden" + }, + "14": { + "2": "Bosch Active Connect met 3 pinnen aan een kabel" + }, + "15": { + "2": "Bosch Active Connect met 5 pinnen aan een kabel" } } } @@ -2978,15 +2978,6 @@ "1": { "then": "Dit fietspad is geplaveid" }, - "10": { - "then": "Dit fietspad is gemaakt van fijn grind" - }, - "11": { - "then": "Dit fietspad is gemaakt van kiezelsteentjes" - }, - "12": { - "then": "Dit fietspad is gemaakt van aarde" - }, "2": { "then": "Dit fietspad is gemaakt van asfalt" }, @@ -3010,6 +3001,15 @@ }, "9": { "then": "Dit fietspad is gemaakt van grind" + }, + "10": { + "then": "Dit fietspad is gemaakt van fijn grind" + }, + "11": { + "then": "Dit fietspad is gemaakt van kiezelsteentjes" + }, + "12": { + "then": "Dit fietspad is gemaakt van aarde" } }, "question": "Waaruit is het oppervlak van het fietspad van gemaakt?", @@ -3058,15 +3058,6 @@ "1": { "then": "Dit fietspad is geplaveid" }, - "10": { - "then": "Dit fietspad is gemaakt van fijn grind" - }, - "11": { - "then": "Dit fietspad is gemaakt van kiezelsteentjes" - }, - "12": { - "then": "Dit fietspad is gemaakt van aarde" - }, "2": { "then": "Dit fietspad is gemaakt van asfalt" }, @@ -3090,6 +3081,15 @@ }, "9": { "then": "Dit fietspad is gemaakt van grind" + }, + "10": { + "then": "Dit fietspad is gemaakt van fijn grind" + }, + "11": { + "then": "Dit fietspad is gemaakt van kiezelsteentjes" + }, + "12": { + "then": "Dit fietspad is gemaakt van aarde" } }, "question": "Waaruit is het oppervlak van de straat gemaakt?", @@ -4138,21 +4138,6 @@ "1": { "then": "Dit is een frituur" }, - "10": { - "then": "Dit is een Chinees restaurant" - }, - "11": { - "then": "Dit is een Grieks restaurant" - }, - "12": { - "then": "Dit is een Indisch restaurant" - }, - "13": { - "then": "Dit is een Turks restaurant (dat meer dan enkel kebab verkoopt)" - }, - "14": { - "then": "Dit is een Thaïs restaurant" - }, "2": { "then": "Dit is een pastazaak" }, @@ -4176,6 +4161,21 @@ }, "9": { "then": "Dit is een Frans restaurant" + }, + "10": { + "then": "Dit is een Chinees restaurant" + }, + "11": { + "then": "Dit is een Grieks restaurant" + }, + "12": { + "then": "Dit is een Indisch restaurant" + }, + "13": { + "then": "Dit is een Turks restaurant (dat meer dan enkel kebab verkoopt)" + }, + "14": { + "then": "Dit is een Thaïs restaurant" } }, "question": "Welk soort gerechten worden hier geserveerd?", @@ -5346,19 +5346,6 @@ } } }, - "10": { - "options": { - "0": { - "question": "Alle Notes" - }, - "1": { - "question": "Verberg import Notes" - }, - "2": { - "question": "Toon enkel import Notes" - } - } - }, "2": { "options": { "0": { @@ -5414,6 +5401,19 @@ "question": "Toon enkel open Notes" } } + }, + "10": { + "options": { + "0": { + "question": "Alle Notes" + }, + "1": { + "question": "Verberg import Notes" + }, + "2": { + "question": "Toon enkel import Notes" + } + } } }, "name": "OpenStreetMap Notes", @@ -5705,21 +5705,6 @@ "1": { "then": "Dit is een normale parkeerplek." }, - "10": { - "then": "Deze parkeerplek is gereserveerd voor ouders met kinderen." - }, - "11": { - "then": "Deze parkeerplek is gereserveerd voor personeel." - }, - "12": { - "then": "Deze parkeerplek is gereserveerd voor taxis." - }, - "13": { - "then": "Deze parkeerplek is gereserveerd voor voertuigen met een aanhanger." - }, - "14": { - "then": "Deze parkeerplek is gereserveerd voor autodelen." - }, "2": { "then": "Dit is een gehandicaptenparkeerplaats." }, @@ -5743,6 +5728,21 @@ }, "9": { "then": "Deze parkeerplek is gereserveerd voor motoren." + }, + "10": { + "then": "Deze parkeerplek is gereserveerd voor ouders met kinderen." + }, + "11": { + "then": "Deze parkeerplek is gereserveerd voor personeel." + }, + "12": { + "then": "Deze parkeerplek is gereserveerd voor taxis." + }, + "13": { + "then": "Deze parkeerplek is gereserveerd voor voertuigen met een aanhanger." + }, + "14": { + "then": "Deze parkeerplek is gereserveerd voor autodelen." } }, "question": "Wat voor parkeerplek is dit?" @@ -6309,21 +6309,6 @@ "1": { "then": "Munten van 2 cent worden geaccepteerd" }, - "10": { - "then": "Munten van 20 rappen worden geaccepteerd" - }, - "11": { - "then": "Munten van ½ frank worden geaccepteerd" - }, - "12": { - "then": "Munten van 1 frank worden geaccepteerd" - }, - "13": { - "then": "Munten van 2 frank worden geaccepteerd" - }, - "14": { - "then": "Munten van 5 frank worden geaccepteerd" - }, "2": { "then": "Munten van 5 cent worden geaccepteerd" }, @@ -6347,6 +6332,21 @@ }, "9": { "then": "Munten van 10 rappen worden geaccepteerd" + }, + "10": { + "then": "Munten van 20 rappen worden geaccepteerd" + }, + "11": { + "then": "Munten van ½ frank worden geaccepteerd" + }, + "12": { + "then": "Munten van 1 frank worden geaccepteerd" + }, + "13": { + "then": "Munten van 2 frank worden geaccepteerd" + }, + "14": { + "then": "Munten van 5 frank worden geaccepteerd" } }, "question": "Met welke munten kan je hier betalen?" @@ -6359,15 +6359,6 @@ "1": { "then": "Biljetten van 10 euro worden geaccepteerd" }, - "10": { - "then": "Biljetten van 100 frank worden geaccepteerd" - }, - "11": { - "then": "Biljetten van 200 frank worden geaccepteerd" - }, - "12": { - "then": "Biljetten van 1000 frank worden geaccepteerd" - }, "2": { "then": "Biljetten van 20 euro worden geaccepteerd" }, @@ -6391,6 +6382,15 @@ }, "9": { "then": "Biljetten van 50 frank worden geaccepteerd" + }, + "10": { + "then": "Biljetten van 100 frank worden geaccepteerd" + }, + "11": { + "then": "Biljetten van 200 frank worden geaccepteerd" + }, + "12": { + "then": "Biljetten van 1000 frank worden geaccepteerd" } }, "question": "Met welke bankbiljetten kan je hier betalen?" @@ -6709,6 +6709,30 @@ "1": { "question": "Recycling van batterijen" }, + "2": { + "question": "Recycling van drankpakken" + }, + "3": { + "question": "Recycling van blikken" + }, + "4": { + "question": "Recycling van kleding" + }, + "5": { + "question": "Recycling van frituurvet" + }, + "6": { + "question": "Recycling van motorolie" + }, + "7": { + "question": "Recycling van tl-buizen" + }, + "8": { + "question": "Recycling van groen afval" + }, + "9": { + "question": "Recycling van glazen flessen" + }, "10": { "question": "Recycling van glas" }, @@ -6739,35 +6763,11 @@ "19": { "question": "Recycling van restafval" }, - "2": { - "question": "Recycling van drankpakken" - }, "20": { "question": "Recycling van inktpatronen" }, "21": { "question": "Recycling van fietsen" - }, - "3": { - "question": "Recycling van blikken" - }, - "4": { - "question": "Recycling van kleding" - }, - "5": { - "question": "Recycling van frituurvet" - }, - "6": { - "question": "Recycling van motorolie" - }, - "7": { - "question": "Recycling van tl-buizen" - }, - "8": { - "question": "Recycling van groen afval" - }, - "9": { - "question": "Recycling van glazen flessen" } } }, @@ -6835,6 +6835,30 @@ "1": { "then": "Drankpakken kunnen hier gerecycled worden" }, + "2": { + "then": "Blikken kunnen hier gerecycled worden" + }, + "3": { + "then": "Kleren kunnen hier gerecycled worden" + }, + "4": { + "then": "Frituurvet kan hier gerecycled worden" + }, + "5": { + "then": "Motorolie kan hier gerecycled worden" + }, + "6": { + "then": "TL-buizen kunnen hier gerecycled worden" + }, + "7": { + "then": "Groen afval kan hier gerecycled worden" + }, + "8": { + "then": "Organisch afval kan hier gerecycled worden" + }, + "9": { + "then": "Glazen flessen kunnen hier gerecycled worden" + }, "10": { "then": "Glas kan hier gerecycled worden" }, @@ -6865,9 +6889,6 @@ "19": { "then": "Schoenen kunnen hier gerecycled worden" }, - "2": { - "then": "Blikken kunnen hier gerecycled worden" - }, "20": { "then": "Kleine elektrische apparaten kunnen hier gerecycled worden" }, @@ -6882,27 +6903,6 @@ }, "24": { "then": "Fietsen (en fietswrakken) kunnen hier gerecycled worden" - }, - "3": { - "then": "Kleren kunnen hier gerecycled worden" - }, - "4": { - "then": "Frituurvet kan hier gerecycled worden" - }, - "5": { - "then": "Motorolie kan hier gerecycled worden" - }, - "6": { - "then": "TL-buizen kunnen hier gerecycled worden" - }, - "7": { - "then": "Groen afval kan hier gerecycled worden" - }, - "8": { - "then": "Organisch afval kan hier gerecycled worden" - }, - "9": { - "then": "Glazen flessen kunnen hier gerecycled worden" } }, "question": "Wat kan hier gerecycled worden?" @@ -7624,12 +7624,6 @@ "1": { "then": "Deze lantaarn gebruikt LEDs" }, - "10": { - "then": "Deze lantaarn gebruikt hogedruknatriumlampen (oranje met wit)" - }, - "11": { - "then": "Deze lantaarn wordt verlicht met gas" - }, "2": { "then": "Deze lantaarn gebruikt gloeilampen" }, @@ -7653,6 +7647,12 @@ }, "9": { "then": "Deze lantaarn gebruikt lagedruknatriumlampen (monochroom oranje)" + }, + "10": { + "then": "Deze lantaarn gebruikt hogedruknatriumlampen (oranje met wit)" + }, + "11": { + "then": "Deze lantaarn wordt verlicht met gas" } }, "question": "Wat voor verlichting gebruikt deze lantaarn?" @@ -8796,6 +8796,30 @@ "1": { "question": "Verkoop van dranken" }, + "2": { + "question": "Verkoop van snoep" + }, + "3": { + "question": "Verkoop van eten" + }, + "4": { + "question": "Verkoop van sigaretten" + }, + "5": { + "question": "Verkoop van condooms" + }, + "6": { + "question": "Verkoop van koffie" + }, + "7": { + "question": "Verkoop van water" + }, + "8": { + "question": "Verkoop van kranten" + }, + "9": { + "question": "Verkoop van fietsbinnenbanden" + }, "10": { "question": "Verkoop van melk" }, @@ -8826,9 +8850,6 @@ "19": { "question": "Verkoop van bloemen" }, - "2": { - "question": "Verkoop van snoep" - }, "23": { "question": "Verkoop van fietslampjes" }, @@ -8843,27 +8864,6 @@ }, "27": { "question": "Verkoop van fietssloten" - }, - "3": { - "question": "Verkoop van eten" - }, - "4": { - "question": "Verkoop van sigaretten" - }, - "5": { - "question": "Verkoop van condooms" - }, - "6": { - "question": "Verkoop van koffie" - }, - "7": { - "question": "Verkoop van water" - }, - "8": { - "question": "Verkoop van kranten" - }, - "9": { - "question": "Verkoop van fietsbinnenbanden" } } } @@ -8904,6 +8904,30 @@ "1": { "then": "Snoep wordt verkocht" }, + "2": { + "then": "Eten wordt verkocht" + }, + "3": { + "then": "Sigaretten worden verkocht" + }, + "4": { + "then": "Condooms worden verkocht" + }, + "5": { + "then": "Koffie wordt verkocht" + }, + "6": { + "then": "Drinkwater wordt verkocht" + }, + "7": { + "then": "Kranten worden verkocht" + }, + "8": { + "then": "Binnenbanden voor fietsen worden verkocht" + }, + "9": { + "then": "Melk wordt verkocht" + }, "10": { "then": "Brood wordt verkocht" }, @@ -8934,9 +8958,6 @@ "19": { "then": "Parkeerkaarten worden verkocht" }, - "2": { - "then": "Eten wordt verkocht" - }, "21": { "then": "Openbaar vervoerkaartjes worden verkocht" }, @@ -8954,27 +8975,6 @@ }, "26": { "then": "Fietssloten worden verkocht" - }, - "3": { - "then": "Sigaretten worden verkocht" - }, - "4": { - "then": "Condooms worden verkocht" - }, - "5": { - "then": "Koffie wordt verkocht" - }, - "6": { - "then": "Drinkwater wordt verkocht" - }, - "7": { - "then": "Kranten worden verkocht" - }, - "8": { - "then": "Binnenbanden voor fietsen worden verkocht" - }, - "9": { - "then": "Melk wordt verkocht" } }, "question": "Wat verkoopt deze verkoopautomaat?", @@ -9267,4 +9267,4 @@ "render": "windturbine" } } -} +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 4c11ea403..6e95d0f72 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5330,9 +5330,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001572", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001572.tgz", - "integrity": "sha512-1Pbh5FLmn5y4+QhNyJE9j3/7dK44dGB83/ZMjv/qJk86TvDbjk0LosiZo0i0WB0Vx607qMX9jYrn1VLHCkN4rw==", + "version": "1.0.30001579", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001579.tgz", + "integrity": "sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA==", "dev": true, "funding": [ { @@ -17636,9 +17636,9 @@ "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" }, "caniuse-lite": { - "version": "1.0.30001572", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001572.tgz", - "integrity": "sha512-1Pbh5FLmn5y4+QhNyJE9j3/7dK44dGB83/ZMjv/qJk86TvDbjk0LosiZo0i0WB0Vx607qMX9jYrn1VLHCkN4rw==", + "version": "1.0.30001579", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001579.tgz", + "integrity": "sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA==", "dev": true }, "canvg": { From 7afe58e6a5f4897d720cd827cf37c3fb30879ede Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Fri, 19 Jan 2024 01:21:47 +0100 Subject: [PATCH 053/195] Bump version number --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8eaa8e838..f558dd5a9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mapcomplete", - "version": "0.36.13", + "version": "0.37.0", "repository": "https://github.com/pietervdvn/MapComplete", "description": "A small website to edit OSM easily", "bugs": "https://github.com/pietervdvn/MapComplete/issues", From 079a3f86940ac51c3533ecd4568c2edf81b48515 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Fri, 19 Jan 2024 17:31:35 +0100 Subject: [PATCH 054/195] Studio: improve error handling, fix renumbering --- .../ThemeConfig/Conversion/Conversion.ts | 15 ++-- .../Conversion/ConversionContext.ts | 26 +++++++ .../ThemeConfig/Conversion/Validation.ts | 68 +++++++++++++------ src/UI/Studio/EditLayer.svelte | 2 +- src/UI/Studio/EditLayerState.ts | 67 ++++++++++++++++-- src/UI/Studio/ErrorIndicatorForRegion.svelte | 2 +- src/UI/Studio/MappingInput.svelte | 9 ++- src/UI/Studio/StudioServer.ts | 5 +- src/UI/Studio/TagRenderingInput.svelte | 15 ++-- src/UI/StudioGUI.svelte | 40 ++++++----- 10 files changed, 187 insertions(+), 62 deletions(-) diff --git a/src/Models/ThemeConfig/Conversion/Conversion.ts b/src/Models/ThemeConfig/Conversion/Conversion.ts index d74028731..867a3d8f8 100644 --- a/src/Models/ThemeConfig/Conversion/Conversion.ts +++ b/src/Models/ThemeConfig/Conversion/Conversion.ts @@ -2,7 +2,6 @@ import { LayerConfigJson } from "../Json/LayerConfigJson" import { Utils } from "../../../Utils" import { QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson" import { ConversionContext } from "./ConversionContext" -import { T } from "vitest/dist/types-aac763a5" export interface DesugaringContext { tagRenderings: Map @@ -11,10 +10,11 @@ export interface DesugaringContext { } export type ConversionMsgLevel = "debug" | "information" | "warning" | "error" + export interface ConversionMessage { - context: ConversionContext - message: string - level: ConversionMsgLevel + readonly context: ConversionContext + readonly message: string + readonly level: ConversionMsgLevel } export abstract class Conversion { @@ -85,6 +85,7 @@ export class Pure extends Conversion { export class Bypass extends DesugaringStep { private readonly _applyIf: (t: T) => boolean private readonly _step: DesugaringStep + constructor(applyIf: (t: T) => boolean, step: DesugaringStep) { super("Applies the step on the object, if the object satisfies the predicate", [], "Bypass") this._applyIf = applyIf @@ -102,7 +103,6 @@ export class Bypass extends DesugaringStep { export class Each extends Conversion { private readonly _step: Conversion private readonly _msg: string - private readonly _filter: (x: X) => boolean constructor(step: Conversion, options?: { msg?: string }) { super( @@ -224,6 +224,7 @@ export class FirstOf extends Conversion { export class Cached extends Conversion { private _step: Conversion private readonly key: string + constructor(step: Conversion) { super("Secretly caches the output for the given input", [], "cached") this._step = step @@ -242,9 +243,11 @@ export class Cached extends Conversion { return converted } } + export class Fuse extends DesugaringStep { - private readonly steps: DesugaringStep[] protected debug = false + private readonly steps: DesugaringStep[] + constructor(doc: string, ...steps: DesugaringStep[]) { super( (doc ?? "") + diff --git a/src/Models/ThemeConfig/Conversion/ConversionContext.ts b/src/Models/ThemeConfig/Conversion/ConversionContext.ts index 2a0e5e848..db4bed4fd 100644 --- a/src/Models/ThemeConfig/Conversion/ConversionContext.ts +++ b/src/Models/ThemeConfig/Conversion/ConversionContext.ts @@ -1,4 +1,5 @@ import { ConversionMessage, ConversionMsgLevel } from "./Conversion" +import { Context } from "maplibre-gl" export class ConversionContext { /** @@ -42,6 +43,31 @@ export class ConversionContext { return new ConversionContext([], msg ? [msg] : [], ["test"]) } + /** + * Does an inline edit of the messages for which a new path is defined + * This is a slight hack + * @param rewritePath + */ + public rewriteMessages( + rewritePath: ( + p: ReadonlyArray + ) => undefined | ReadonlyArray + ): void { + for (let i = 0; i < this.messages.length; i++) { + const m = this.messages[i] + const newPath = rewritePath(m.context.path) + if (!newPath) { + continue + } + const rewrittenContext = new ConversionContext( + this.messages, + newPath, + m.context.operation + ) + this.messages[i] = { ...m, context: rewrittenContext } + } + } + static print(msg: ConversionMessage) { const noString = msg.context.path.filter( (p) => typeof p !== "string" && typeof p !== "number" diff --git a/src/Models/ThemeConfig/Conversion/Validation.ts b/src/Models/ThemeConfig/Conversion/Validation.ts index ed4c79098..2adf0f023 100644 --- a/src/Models/ThemeConfig/Conversion/Validation.ts +++ b/src/Models/ThemeConfig/Conversion/Validation.ts @@ -13,7 +13,10 @@ import { And } from "../../../Logic/Tags/And" import Translations from "../../../UI/i18n/Translations" import FilterConfigJson from "../Json/FilterConfigJson" import DeleteConfig from "../DeleteConfig" -import { QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson" +import { + MappingConfigJson, + QuestionableTagRenderingConfigJson, +} from "../Json/QuestionableTagRenderingConfigJson" import Validators from "../../../UI/InputElement/Validators" import TagRenderingConfig from "../TagRenderingConfig" import { parse as parse_html } from "node-html-parser" @@ -21,9 +24,7 @@ import PresetConfig from "../PresetConfig" import { TagsFilter } from "../../../Logic/Tags/TagsFilter" import { Translatable } from "../Json/Translatable" import { ConversionContext } from "./ConversionContext" -import * as eli from "../../../assets/editor-layer-index.json" import { AvailableRasterLayers } from "../../RasterLayers" -import Back from "../../../assets/svg/Back.svelte" import PointRenderingConfigJson from "../Json/PointRenderingConfigJson" class ValidateLanguageCompleteness extends DesugaringStep { @@ -178,7 +179,7 @@ export class ValidateTheme extends DesugaringStep { if (!json.title) { context.enter("title").err(`The theme ${json.id} does not have a title defined.`) } - if(!json.icon){ + if (!json.icon) { context.enter("icon").err("A theme should have an icon") } if (this._isBuiltin && this._extractImages !== undefined) { @@ -831,6 +832,7 @@ class MiscTagRenderingChecks extends DesugaringStep { json: TagRenderingConfigJson | QuestionableTagRenderingConfigJson, context: ConversionContext ): TagRenderingConfigJson { + console.log(">>> Validating TR", context.path.join("."), json) if (json["special"] !== undefined) { context.err( 'Detected `special` on the top level. Did you mean `{"render":{ "special": ... }}`' @@ -848,13 +850,32 @@ class MiscTagRenderingChecks extends DesugaringStep { CheckTranslation.allowUndefined.convert(json[key], context.enter(key)) } for (let i = 0; i < json.mappings?.length ?? 0; i++) { - const mapping = json.mappings[i] + const mapping: MappingConfigJson = json.mappings[i] CheckTranslation.noUndefined.convert( mapping.then, context.enters("mappings", i, "then") ) if (!mapping.if) { - context.enters("mappings", i).err("No `if` is defined") + console.log( + "Checking mappings", + i, + "if", + mapping.if, + context.path.join("."), + mapping.then + ) + context.enters("mappings", i, "if").err("No `if` is defined") + } + if (mapping.addExtraTags) { + for (let j = 0; j < mapping.addExtraTags.length; j++) { + if (!mapping.addExtraTags[j]) { + context + .enters("mappings", i, "addExtraTags", j) + .err( + "Detected a 'null' or 'undefined' value. Either specify a tag or delete this item" + ) + } + } } const en = mapping?.then?.["en"] if (en && this.detectYesOrNo(en)) { @@ -977,6 +998,9 @@ class MiscTagRenderingChecks extends DesugaringStep { } } + if (context.hasErrors()) { + return undefined + } return json } @@ -996,6 +1020,7 @@ export class ValidateTagRenderings extends Fuse { constructor(layerConfig?: LayerConfigJson, doesImageExist?: DoesImageExist) { super( "Various validation on tagRenderingConfigs", + new MiscTagRenderingChecks(), new DetectShadowedMappings(layerConfig), new DetectConflictingAddExtraTags(), // TODO enable new DetectNonErasedKeysInMappings(), @@ -1003,8 +1028,7 @@ export class ValidateTagRenderings extends Fuse { new On("render", new ValidatePossibleLinks()), new On("question", new ValidatePossibleLinks()), new On("questionHint", new ValidatePossibleLinks()), - new On("mappings", new Each(new On("then", new ValidatePossibleLinks()))), - new MiscTagRenderingChecks() + new On("mappings", new Each(new On("then", new ValidatePossibleLinks()))) ) } } @@ -1107,7 +1131,9 @@ export class PrevalidateLayer extends DesugaringStep { context.enter("pointRendering").err("There are no pointRenderings at all...") } - json.pointRendering?.forEach((pr,i) => this._validatePointRendering.convert(pr, context.enters("pointeRendering", i))) + json.pointRendering?.forEach((pr, i) => + this._validatePointRendering.convert(pr, context.enters("pointeRendering", i)) + ) if (json["mapRendering"]) { context.enter("mapRendering").err("This layer has a legacy 'mapRendering'") @@ -1134,7 +1160,7 @@ export class PrevalidateLayer extends DesugaringStep { } if (json.tagRenderings !== undefined && json.tagRenderings.length > 0) { - new On("tagRendering", new Each(new ValidateTagRenderings(json))) + new On("tagRenderings", new Each(new ValidateTagRenderings(json))) if (json.title === undefined && json.source !== "special:library") { context .enter("title") @@ -1424,29 +1450,33 @@ class ValidatePointRendering extends DesugaringStep { } if (json["markers"]) { - context.enter("markers").err(`Detected a field 'markerS' in pointRendering. It is written as a singular case`) + context + .enter("markers") + .err( + `Detected a field 'markerS' in pointRendering. It is written as a singular case` + ) } if (json.marker && !Array.isArray(json.marker)) { - context.enter("marker").err( - "The marker in a pointRendering should be an array" - ) + context.enter("marker").err("The marker in a pointRendering should be an array") } if (json.location.length == 0) { - context.enter("location").err ( - "A pointRendering should have at least one 'location' to defined where it should be rendered. " - ) + context + .enter("location") + .err( + "A pointRendering should have at least one 'location' to defined where it should be rendered. " + ) } return json - - } } + export class ValidateLayer extends Conversion< LayerConfigJson, { parsed: LayerConfig; raw: LayerConfigJson } > { private readonly _skipDefaultLayers: boolean private readonly _prevalidation: PrevalidateLayer + constructor( path: string, isBuiltin: boolean, diff --git a/src/UI/Studio/EditLayer.svelte b/src/UI/Studio/EditLayer.svelte index 5d9aedbaf..fc74eb3ab 100644 --- a/src/UI/Studio/EditLayer.svelte +++ b/src/UI/Studio/EditLayer.svelte @@ -180,7 +180,7 @@
Advanced functionality - +
diff --git a/src/UI/Studio/EditLayerState.ts b/src/UI/Studio/EditLayerState.ts index 557ec2109..b52e6e85a 100644 --- a/src/UI/Studio/EditLayerState.ts +++ b/src/UI/Studio/EditLayerState.ts @@ -22,6 +22,7 @@ import { LayoutConfigJson } from "../../Models/ThemeConfig/Json/LayoutConfigJson import { PrepareTheme } from "../../Models/ThemeConfig/Conversion/PrepareTheme" import { ConversionContext } from "../../Models/ThemeConfig/Conversion/ConversionContext" import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource" +import { TagRenderingConfigJson } from "../../Models/ThemeConfig/Json/TagRenderingConfigJson" export interface HighlightedTagRendering { path: ReadonlyArray @@ -66,7 +67,6 @@ export abstract class EditJsonState { this.messages = this.setupErrorsForLayers() const layerId = this.getId() - this.highlightedItem.addCallbackD((hl) => console.log("Highlighted item is", hl)) this.configuration .mapD((config) => { if (!this.sendingUpdates) { @@ -110,6 +110,7 @@ export abstract class EditJsonState { public async delete() { await this.server.delete(this.getId().data, this.category) } + public getStoreFor(path: ReadonlyArray): UIEventSource { const key = path.join(".") @@ -172,7 +173,6 @@ export abstract class EditJsonState { public setValueAt(path: ReadonlyArray, v: any) { let entry = this.configuration.data - console.trace("Setting value at", path,"to",v) const isUndefined = v === undefined || v === null || @@ -249,6 +249,62 @@ export abstract class EditJsonState { } } +class ContextRewritingStep extends Conversion { + private readonly _step: Conversion + private readonly _state: DesugaringContext + private readonly _getTagRenderings: (t: T) => TagRenderingConfigJson[] + + constructor( + state: DesugaringContext, + step: Conversion, + getTagRenderings: (t: T) => TagRenderingConfigJson[] + ) { + super( + "When validating a layer, the tagRenderings are first expanded. Some builtin tagRendering-calls (e.g. `contact`) will introduce _multiple_ tagRenderings, causing the count to be off. This class rewrites the error messages to fix this", + [], + "ContextRewritingStep" + ) + this._state = state + this._step = step + this._getTagRenderings = getTagRenderings + } + + convert(json: LayerConfigJson, context: ConversionContext): T { + const converted = this._step.convert(json, context) + const originalIds = json.tagRenderings?.map( + (tr) => (tr)["id"] + ) + if (!originalIds) { + return converted + } + + let newTagRenderings: TagRenderingConfigJson[] + if (converted === undefined) { + const prepared = new PrepareLayer(this._state) + newTagRenderings = ( + prepared.convert(json, context).tagRenderings + ) + } else { + newTagRenderings = this._getTagRenderings(converted) + } + context.rewriteMessages((path) => { + if (path[0] !== "tagRenderings") { + return undefined + } + const newPath = [...path] + const idToSearch = newTagRenderings[newPath[1]].id + const oldIndex = originalIds.indexOf(idToSearch) + if (oldIndex < 0) { + console.warn("Original ID was not found: ", idToSearch) + return undefined // We don't modify the message + } + newPath[1] = oldIndex + return newPath + }) + return converted + } +} + export default class EditLayerState extends EditJsonState { // Needed for the special visualisations public readonly osmConnection: OsmConnection @@ -334,9 +390,10 @@ export default class EditLayerState extends EditJsonState { } protected buildValidation(state: DesugaringContext) { - return new Pipe( - new PrepareLayer(state), - new ValidateLayer("dynamic", false, undefined, true) + return new ContextRewritingStep( + state, + new Pipe(new PrepareLayer(state), new ValidateLayer("dynamic", false, undefined, true)), + (t) => t.raw.tagRenderings ) } diff --git a/src/UI/Studio/ErrorIndicatorForRegion.svelte b/src/UI/Studio/ErrorIndicatorForRegion.svelte index e3fd4c084..b1983db5d 100644 --- a/src/UI/Studio/ErrorIndicatorForRegion.svelte +++ b/src/UI/Studio/ErrorIndicatorForRegion.svelte @@ -2,7 +2,7 @@ import EditLayerState from "./EditLayerState" import { ExclamationIcon } from "@rgossiaux/svelte-heroicons/solid" - export let firstPaths: Set + export let firstPaths: Set export let state: EditLayerState let messagesCount = state.messages.map( (msgs) => diff --git a/src/UI/Studio/MappingInput.svelte b/src/UI/Studio/MappingInput.svelte index f36984085..d210811d4 100644 --- a/src/UI/Studio/MappingInput.svelte +++ b/src/UI/Studio/MappingInput.svelte @@ -11,9 +11,11 @@ import { Utils } from "../../Utils" import ToSvelte from "../Base/ToSvelte.svelte" import { VariableUiElement } from "../Base/VariableUIElement" + import { ExclamationTriangle } from "@babeard/svelte-heroicons/solid/ExclamationTriangle" export let state: EditLayerState export let path: (string | number)[] + let messages = state.messagesFor(path) let tag: UIEventSource = state.getStoreFor([...path, "if"]) let parsedTag = tag.map((t) => (t ? TagUtils.Tag(t) : undefined)) let exampleTags = parsedTag.map((pt) => { @@ -27,7 +29,6 @@ } return o }) - let uploadableOnly: boolean = true let thenText: UIEventSource> = state.getStoreFor([...path, "then"]) let thenTextEn = thenText.mapD((translation) => @@ -71,5 +72,11 @@ No then is set {/if} + {#if $messages.length > 0} +
+ + {$messages.length} errors +
+ {/if}
{/if} diff --git a/src/UI/Studio/StudioServer.ts b/src/UI/Studio/StudioServer.ts index 2c438f01b..1256e0895 100644 --- a/src/UI/Studio/StudioServer.ts +++ b/src/UI/Studio/StudioServer.ts @@ -2,6 +2,7 @@ import { Utils } from "../../Utils" import Constants from "../../Models/Constants" import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson" import { Store } from "../../Logic/UIEventSource" +import { LayoutConfigJson } from "../../Models/ThemeConfig/Json/LayoutConfigJson" export default class StudioServer { private readonly url: string @@ -47,11 +48,13 @@ export default class StudioServer { return layerOverview } + async fetch(layerId: string, category: "layers", uid?: number): Promise + async fetch(layerId: string, category: "themes", uid?: number): Promise async fetch( layerId: string, category: "layers" | "themes", uid?: number - ): Promise { + ): Promise { try { return await Utils.downloadJson(this.urlFor(layerId, category, uid)) } catch (e) { diff --git a/src/UI/Studio/TagRenderingInput.svelte b/src/UI/Studio/TagRenderingInput.svelte index 41e01a220..ee43f20d1 100644 --- a/src/UI/Studio/TagRenderingInput.svelte +++ b/src/UI/Studio/TagRenderingInput.svelte @@ -24,13 +24,13 @@ import { onMount } from "svelte" export let state: EditLayerState - export let schema: ConfigMeta - export let path: (string | number)[] + export let path: ReadonlyArray + let messages = state.messagesFor(path) let expertMode = state.expertMode const store = state.getStoreFor(path) let value = store.data let hasSeenIntro = UIEventSource.asBoolean( - LocalStorageSource.Get("studio-seen-tagrendering-tutorial", "false") + LocalStorageSource.Get("studio-seen-tagrendering-tutorial", "false"), ) onMount(() => { if (!hasSeenIntro.data) { @@ -43,7 +43,7 @@ * Should only be enabled for 'tagrenderings' in the theme, if the source is OSM */ let allowQuestions: Store = state.configuration.mapD( - (config) => path.at(0) === "tagRenderings" && config.source?.geoJson === undefined + (config) => path.at(0) === "tagRenderings" && config.source?.["geoJson"] === undefined, ) let mappingsBuiltin: MappingConfigJson[] = [] @@ -119,7 +119,7 @@ const freeformSchemaAll = ( questionableTagRenderingSchemaRaw.filter( - (schema) => schema.path.length == 2 && schema.path[0] === "freeform" && $allowQuestions + (schema) => schema.path.length == 2 && schema.path[0] === "freeform" && $allowQuestions, ) ) let freeformSchema = $expertMode @@ -128,7 +128,7 @@ const missing: string[] = questionableTagRenderingSchemaRaw .filter( (schema) => - schema.path.length >= 1 && !items.has(schema.path[0]) && !ignored.has(schema.path[0]) + schema.path.length >= 1 && !items.has(schema.path[0]) && !ignored.has(schema.path[0]), ) .map((schema) => schema.path.join(".")) console.log({ state }) @@ -164,7 +164,7 @@ {/if} {#each $mappings ?? [] as mapping, i (mapping)}
- +
+ {/each}
+ {feedback} +
+ {:else} + + {/if} + {:else} + {message_closed} + {/if} + diff --git a/src/UI/SpecialVisualizations.ts b/src/UI/SpecialVisualizations.ts index 5f0d7451d..7d55060da 100644 --- a/src/UI/SpecialVisualizations.ts +++ b/src/UI/SpecialVisualizations.ts @@ -3,11 +3,7 @@ import { FixedUiElement } from "./Base/FixedUiElement" import BaseUIElement from "./BaseUIElement" import Title from "./Base/Title" import Table from "./Base/Table" -import { - RenderingSpecification, - SpecialVisualization, - SpecialVisualizationState, -} from "./SpecialVisualization" +import { RenderingSpecification, SpecialVisualization, SpecialVisualizationState } from "./SpecialVisualization" import { HistogramViz } from "./Popup/HistogramViz" import { MinimapViz } from "./Popup/MinimapViz" import { ShareLinkViz } from "./Popup/ShareLinkViz" @@ -90,6 +86,8 @@ import Qr from "../Utils/Qr" import ComparisonTool from "./Comparison/ComparisonTool.svelte" import SpecialTranslation from "./Popup/TagRendering/SpecialTranslation.svelte" import SpecialVisualisationUtils from "./SpecialVisualisationUtils" +import LoginButton from "./Base/LoginButton.svelte" +import Toggle from "./Input/Toggle" class NearbyImageVis implements SpecialVisualization { // Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests @@ -116,7 +114,7 @@ class NearbyImageVis implements SpecialVisualization { tags: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { const isOpen = args[0] === "open" const readonly = args[1] === "readonly" @@ -183,7 +181,7 @@ class StealViz implements SpecialVisualization { selectedElement: otherFeature, state, layer, - }) + }), ) } if (elements.length === 1) { @@ -191,8 +189,8 @@ class StealViz implements SpecialVisualization { } return new Combine(elements).SetClass("flex flex-col") }, - [state.indexedFeatures.featuresById] - ) + [state.indexedFeatures.featuresById], + ), ) } @@ -231,7 +229,7 @@ export class QuestionViz implements SpecialVisualization { tags: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { const labels = args[0] ?.split(";") @@ -267,37 +265,38 @@ export default class SpecialVisualizations { viz.docs, viz.args.length > 0 ? new Table( - ["name", "default", "description"], - viz.args.map((arg) => { - let defaultArg = arg.defaultValue ?? "_undefined_" - if (defaultArg == "") { - defaultArg = "_empty string_" - } - return [arg.name, defaultArg, arg.doc] - }) - ) + ["name", "default", "description"], + viz.args.map((arg) => { + let defaultArg = arg.defaultValue ?? "_undefined_" + if (defaultArg == "") { + defaultArg = "_empty string_" + } + return [arg.name, defaultArg, arg.doc] + }), + ) : undefined, new Title("Example usage of " + viz.funcName, 4), new FixedUiElement( viz.example ?? - "`{" + - viz.funcName + - "(" + - viz.args.map((arg) => arg.defaultValue).join(",") + - ")}`" + "`{" + + viz.funcName + + "(" + + viz.args.map((arg) => arg.defaultValue).join(",") + + ")}`", ).SetClass("literal-code"), ]) } public static constructSpecification( template: string, - extraMappings: SpecialVisualization[] = [] + extraMappings: SpecialVisualization[] = [], ): RenderingSpecification[] { return SpecialVisualisationUtils.constructSpecification(template, extraMappings) } + public static HelpMessage() { const helpTexts = SpecialVisualizations.specialVisualizations.map((viz) => - SpecialVisualizations.DocumentationFor(viz) + SpecialVisualizations.DocumentationFor(viz), ) return new Combine([ @@ -331,10 +330,10 @@ export default class SpecialVisualizations { }, }, null, - " " - ) + " ", + ), ).SetClass("code"), - 'In other words: use `{ "before": ..., "after": ..., "special": {"type": ..., "argname": ...argvalue...}`. The args are in the `special` block; an argvalue can be a string, a translation or another value. (Refer to class `RewriteSpecial` in case of problems)', + "In other words: use `{ \"before\": ..., \"after\": ..., \"special\": {\"type\": ..., \"argname\": ...argvalue...}`. The args are in the `special` block; an argvalue can be a string, a translation or another value. (Refer to class `RewriteSpecial` in case of problems)", ]).SetClass("flex flex-col"), ...helpTexts, ]).SetClass("flex flex-col") @@ -343,20 +342,20 @@ export default class SpecialVisualizations { // noinspection JSUnusedGlobalSymbols public static renderExampleOfSpecial( state: SpecialVisualizationState, - s: SpecialVisualization + s: SpecialVisualization, ): BaseUIElement { const examples = s.structuredExamples === undefined ? [] : s.structuredExamples().map((e) => { - return s.constr( - state, - new UIEventSource>(e.feature.properties), - e.args, - e.feature, - undefined - ) - }) + return s.constr( + state, + new UIEventSource>(e.feature.properties), + e.args, + e.feature, + undefined, + ) + }) return new Combine([new Title(s.funcName), s.docs, ...examples]) } @@ -396,7 +395,7 @@ export default class SpecialVisualizations { assignTo: state.userRelatedState.language, availableLanguages: state.layout.language, preferredLanguages: state.osmConnection.userDetails.map( - (ud) => ud.languages + (ud) => ud.languages, ), }) }, @@ -421,7 +420,7 @@ export default class SpecialVisualizations { constr( state: SpecialVisualizationState, - tagSource: UIEventSource> + tagSource: UIEventSource>, ): BaseUIElement { return new VariableUiElement( tagSource @@ -431,7 +430,7 @@ export default class SpecialVisualizations { return new SplitRoadWizard(id, state) } return undefined - }) + }), ) }, }, @@ -445,7 +444,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { if (feature.geometry.type !== "Point") { return undefined @@ -468,7 +467,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { if (!layer.deletion) { return undefined @@ -496,7 +495,7 @@ export default class SpecialVisualizations { state: SpecialVisualizationState, tagSource: UIEventSource>, argument: string[], - feature: Feature + feature: Feature, ): BaseUIElement { const [lon, lat] = GeoOperations.centerpointCoordinates(feature) return new SvelteUIElement(CreateNewNote, { @@ -560,7 +559,7 @@ export default class SpecialVisualizations { .map((tags) => tags[args[0]]) .map((wikidata) => { wikidata = Utils.NoEmpty( - wikidata?.split(";")?.map((wd) => wd.trim()) ?? [] + wikidata?.split(";")?.map((wd) => wd.trim()) ?? [], )[0] const entry = Wikidata.LoadWikidataEntry(wikidata) return new VariableUiElement( @@ -570,9 +569,9 @@ export default class SpecialVisualizations { } const response = e["success"] return Translation.fromMap(response.labels) - }) + }), ) - }) + }), ), }, new MapillaryLinkVis(), @@ -586,7 +585,7 @@ export default class SpecialVisualizations { tags: UIEventSource>, _, __, - layer: LayerConfig + layer: LayerConfig, ) => new SvelteUIElement(AllTagsPanel, { tags, layer }), }, { @@ -608,7 +607,7 @@ export default class SpecialVisualizations { return new ImageCarousel( AllImageProviders.LoadImagesFor(tags, imagePrefixes), tags, - state + state, ) }, }, @@ -664,7 +663,7 @@ export default class SpecialVisualizations { { nameKey: nameKey, fallbackName, - } + }, ) return new SvelteUIElement(StarsBarIcon, { score: reviews.average, @@ -697,7 +696,7 @@ export default class SpecialVisualizations { { nameKey: nameKey, fallbackName, - } + }, ) return new SvelteUIElement(ReviewForm, { reviews, state, tags, feature, layer }) }, @@ -729,7 +728,7 @@ export default class SpecialVisualizations { { nameKey: nameKey, fallbackName, - } + }, ) return new SvelteUIElement(AllReviews, { reviews, state, tags, feature, layer }) }, @@ -787,7 +786,7 @@ export default class SpecialVisualizations { tags: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): SvelteUIElement { const keyToUse = args[0] const prefix = args[1] @@ -824,17 +823,17 @@ export default class SpecialVisualizations { return undefined } const allUnits: Unit[] = [].concat( - ...(state?.layout?.layers?.map((lyr) => lyr.units) ?? []) + ...(state?.layout?.layers?.map((lyr) => lyr.units) ?? []), ) const unit = allUnits.filter((unit) => - unit.isApplicableToKey(key) + unit.isApplicableToKey(key), )[0] if (unit === undefined) { return value } const getCountry = () => tagSource.data._country return unit.asHumanLongValue(value, getCountry) - }) + }), ) }, }, @@ -851,7 +850,7 @@ export default class SpecialVisualizations { new Combine([ t.downloadFeatureAsGeojson.SetClass("font-bold text-lg"), t.downloadGeoJsonHelper.SetClass("subtle"), - ]).SetClass("flex flex-col") + ]).SetClass("flex flex-col"), ) .onClick(() => { console.log("Exporting as Geojson") @@ -864,7 +863,7 @@ export default class SpecialVisualizations { title + "_mapcomplete_export.geojson", { mimetype: "application/vnd.geo+json", - } + }, ) }) .SetClass("w-full") @@ -900,7 +899,7 @@ export default class SpecialVisualizations { constr: (state) => { return new SubtleButton( Svg.delete_icon_svg().SetStyle("height: 1.5rem"), - Translations.t.general.removeLocationHistory + Translations.t.general.removeLocationHistory, ).onClick(() => { state.historicalUserLocations.features.setData([]) state.selectedElement.setData(undefined) @@ -938,10 +937,10 @@ export default class SpecialVisualizations { .filter((c) => c.text !== "") .map( (c, i) => - new NoteCommentElement(c, state, i, comments.length) - ) + new NoteCommentElement(c, state, i, comments.length), + ), ).SetClass("flex flex-col") - }) + }), ), }, { @@ -975,7 +974,7 @@ export default class SpecialVisualizations { tagsSource: UIEventSource>, _: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ) => new VariableUiElement( tagsSource.map((tags) => { @@ -993,7 +992,7 @@ export default class SpecialVisualizations { feature, layer, }).SetClass("px-1") - }) + }), ), }, { @@ -1009,8 +1008,8 @@ export default class SpecialVisualizations { let challenge = Stores.FromPromise( Utils.downloadJsonCached( `${Maproulette.defaultEndpoint}/challenge/${parentId}`, - 24 * 60 * 60 * 1000 - ) + 24 * 60 * 60 * 1000, + ), ) return new VariableUiElement( @@ -1035,7 +1034,7 @@ export default class SpecialVisualizations { } else { return [title, new List(listItems)] } - }) + }), ) }, docs: "Fetches the metadata of MapRoulette campaign that this task is part of and shows those details (namely `title`, `description` and `instruction`).\n\nThis reads the property `mr_challengeId` to detect the parent campaign.", @@ -1049,15 +1048,15 @@ export default class SpecialVisualizations { "\n" + "```json\n" + "{\n" + - ' "id": "mark_duplicate",\n' + - ' "render": {\n' + - ' "special": {\n' + - ' "type": "maproulette_set_status",\n' + - ' "message": {\n' + - ' "en": "Mark as not found or false positive"\n' + + " \"id\": \"mark_duplicate\",\n" + + " \"render\": {\n" + + " \"special\": {\n" + + " \"type\": \"maproulette_set_status\",\n" + + " \"message\": {\n" + + " \"en\": \"Mark as not found or false positive\"\n" + " },\n" + - ' "status": "2",\n' + - ' "image": "close"\n' + + " \"status\": \"2\",\n" + + " \"image\": \"close\"\n" + " }\n" + " }\n" + "}\n" + @@ -1086,10 +1085,15 @@ export default class SpecialVisualizations { doc: "The property name containing the maproulette id", defaultValue: "mr_taskId", }, + { + name: "ask_feedback", + doc: "If not an empty string, this will be used as question to ask some additional feedback. A text field will be added", + defaultValue: "" + } ], constr: (state, tagsSource, args) => { - let [message, image, message_closed, statusToSet, maproulette_id_key] = args + let [message, image, message_closed, statusToSet, maproulette_id_key, askFeedback] = args if (image === "") { image = "confirm" } @@ -1105,6 +1109,7 @@ export default class SpecialVisualizations { message_closed, statusToSet, maproulette_id_key, + askFeedback }) }, }, @@ -1124,8 +1129,8 @@ export default class SpecialVisualizations { const fsBboxed = new BBoxFeatureSourceForLayer(fs, bbox) return new StatisticsPanel(fsBboxed) }, - [state.mapProperties.bounds] - ) + [state.mapProperties.bounds], + ), ) }, }, @@ -1191,7 +1196,7 @@ export default class SpecialVisualizations { constr( state: SpecialVisualizationState, tagSource: UIEventSource>, - args: string[] + args: string[], ): BaseUIElement { let [text, href, classnames, download, ariaLabel] = args if (download === "") { @@ -1203,13 +1208,13 @@ export default class SpecialVisualizations { (tags) => new SvelteUIElement(Link, { text: Utils.SubstituteKeys(text, tags), - href: Utils.SubstituteKeys(href, tags).replaceAll(/ /g, '%20') /* Chromium based browsers eat the spaces */, + href: Utils.SubstituteKeys(href, tags).replaceAll(/ /g, "%20") /* Chromium based browsers eat the spaces */, classnames, download: Utils.SubstituteKeys(download, tags), ariaLabel: Utils.SubstituteKeys(ariaLabel, tags), newTab, - }) - ) + }), + ), ) }, }, @@ -1231,7 +1236,7 @@ export default class SpecialVisualizations { }, }, null, - " " + " ", ) + "\n```", args: [ @@ -1255,7 +1260,7 @@ export default class SpecialVisualizations { featureTags: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ) { const [key, tr, classesRaw] = args let classes = classesRaw ?? "" @@ -1280,7 +1285,7 @@ export default class SpecialVisualizations { elements.push(subsTr) } return elements - }) + }), ) }, }, @@ -1300,7 +1305,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { return new VariableUiElement( tagSource.map((tags) => { @@ -1312,7 +1317,7 @@ export default class SpecialVisualizations { console.error("Cannot create a translation for", v, "due to", e) return JSON.stringify(v) } - }) + }), ) }, }, @@ -1332,7 +1337,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { const key = argument[0] const validator = new FediverseValidator() @@ -1342,7 +1347,7 @@ export default class SpecialVisualizations { .map((fediAccount) => { fediAccount = validator.reformat(fediAccount) const [_, username, host] = fediAccount.match( - FediverseValidator.usernameAtServer + FediverseValidator.usernameAtServer, ) const normalLink = new SvelteUIElement(Link, { @@ -1354,10 +1359,10 @@ export default class SpecialVisualizations { const loggedInContributorMastodon = state.userRelatedState?.preferencesAsTags?.data?.[ "_mastodon_link" - ] + ] console.log( "LoggedinContributorMastodon", - loggedInContributorMastodon + loggedInContributorMastodon, ) if (!loggedInContributorMastodon) { return normalLink @@ -1373,7 +1378,7 @@ export default class SpecialVisualizations { newTab: true, }).SetClass("button"), ]) - }) + }), ) }, }, @@ -1393,7 +1398,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { return new FixedUiElement("{" + args[0] + "}") }, @@ -1414,7 +1419,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { const key = argument[0] ?? "value" return new VariableUiElement( @@ -1432,12 +1437,12 @@ export default class SpecialVisualizations { } catch (e) { return new FixedUiElement( "Could not parse this tag: " + - JSON.stringify(value) + - " due to " + - e + JSON.stringify(value) + + " due to " + + e, ).SetClass("alert") } - }) + }), ) }, }, @@ -1458,7 +1463,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { const giggityUrl = argument[0] return new SvelteUIElement(Giggity, { tags: tagSource, state, giggityUrl }) @@ -1474,12 +1479,12 @@ export default class SpecialVisualizations { _: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { const tags = (( state )).geolocation.currentUserLocation.features.map( - (features) => features[0]?.properties + (features) => features[0]?.properties, ) return new Combine([ new SvelteUIElement(OrientationDebugPanel, {}), @@ -1501,7 +1506,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { return new SvelteUIElement(MarkAsFavourite, { tags: tagSource, @@ -1521,7 +1526,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { return new SvelteUIElement(MarkAsFavouriteMini, { tags: tagSource, @@ -1541,7 +1546,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { return new SvelteUIElement(DirectionIndicator, { state, feature }) }, @@ -1556,7 +1561,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { return new VariableUiElement( tagSource @@ -1578,9 +1583,9 @@ export default class SpecialVisualizations { `${window.location.protocol}//${window.location.host}${window.location.pathname}?${layout}lat=${lat}&lon=${lon}&z=15` + `#${id}` return new Img(new Qr(url).toImageElement(75)).SetStyle( - "width: 75px" + "width: 75px", ) - }) + }), ) }, }, @@ -1600,7 +1605,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { const key = args[0] === "" ? "_direction:centerpoint" : args[0] return new VariableUiElement( @@ -1611,11 +1616,11 @@ export default class SpecialVisualizations { }) .mapD((value) => { const dir = GeoOperations.bearingToHuman( - GeoOperations.parseBearing(value) + GeoOperations.parseBearing(value), ) console.log("Human dir", dir) return Translations.t.general.visualFeedback.directionsAbsolute[dir] - }) + }), ) }, }, @@ -1650,7 +1655,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { const url = args[0] const postprocessVelopark = args[2] === "velopark" @@ -1666,6 +1671,17 @@ export default class SpecialVisualizations { }) }, }, + { + funcName: "login_button", + args: [ + ], + docs: "Show a login button", + needsUrls: [], + constr(state: SpecialVisualizationState, tagSource: UIEventSource>, args: string[], feature: Feature, layer: LayerConfig): BaseUIElement { + return new Toggle(undefined, + new SvelteUIElement(LoginButton), state.osmConnection.isLoggedIn) + }, + }, ] specialVisualizations.push(new AutoApplyButton(specialVisualizations)) @@ -1677,7 +1693,7 @@ export default class SpecialVisualizations { throw ( "Invalid special visualisation found: funcName is undefined for " + invalid.map((sp) => sp.i).join(", ") + - '. Did you perhaps type \n funcName: "funcname" // type declaration uses COLON\ninstead of:\n funcName = "funcName" // value definition uses EQUAL' + ". Did you perhaps type \n funcName: \"funcname\" // type declaration uses COLON\ninstead of:\n funcName = \"funcName\" // value definition uses EQUAL" ) } From 8be41571fa1371494bbbb977f5a6ad321ec16d1c Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 15 Feb 2024 01:07:50 +0100 Subject: [PATCH 188/195] Velopark: add report generating script --- scripts/velopark/compare.ts | 72 +++++++++++++++++++++++++++ scripts/velopark/veloParkToGeojson.ts | 29 +++++++---- 2 files changed, 90 insertions(+), 11 deletions(-) create mode 100644 scripts/velopark/compare.ts diff --git a/scripts/velopark/compare.ts b/scripts/velopark/compare.ts new file mode 100644 index 000000000..4bab31dc4 --- /dev/null +++ b/scripts/velopark/compare.ts @@ -0,0 +1,72 @@ +import Script from "../Script" +import fs from "fs" +import { Feature, FeatureCollection } from "geojson" +import { GeoOperations } from "../../src/Logic/GeoOperations" +import * as os from "os" +// vite-node scripts/velopark/compare.ts -- scripts/velopark/velopark_all_2024-02-14T12\:18\:41.772Z.geojson ~/Projecten/OSM/Fietsberaad/2024-02-02\ Fietsenstallingen_OSM_met_velopark_ref.geojson +class Compare extends Script { + + compare(veloId: string, osmParking: Feature, veloParking: Feature): {distance: number, ref: string, osmid: string, diffs: { + osm: string, velopark: string, key: string + }[] }{ + const osmCenterpoint = GeoOperations.centerpointCoordinates(osmParking) + const veloparkCenterpoint = GeoOperations.centerpointCoordinates(veloParking) + const distance = Math.round(GeoOperations.distanceBetween(osmCenterpoint, veloparkCenterpoint)) + const diffs: { osm: string, velopark: string, key: string}[] = [] + + const allKeys = new Set(Object.keys(osmParking.properties).concat(Object.keys(veloParking.properties))) + for (const key of allKeys) { + if(osmParking.properties[key] === veloParking.properties[key]){ + continue + } + if(Number(osmParking.properties[key]) === veloParking.properties[key]){ + continue + } + if(veloParking.properties[key] === undefined){ + continue + } + diffs.push({ + key, + osm: osmParking.properties[key], + velopark: veloParking.properties[key] + }) + } + return { + ref: veloId, + osmid: osmParking.properties["@id"], + distance, diffs + } + } + async main(args: string[]): Promise { + let [velopark, osm, key] = args + key ??= "ref:velopark" + const veloparkData: FeatureCollection = JSON.parse(fs.readFileSync(velopark, "utf-8")) + const osmData : FeatureCollection = JSON.parse(fs.readFileSync(osm, "utf-8")) + + const veloparkById : Record = {} + for (const parking of veloparkData.features) { + veloparkById[parking.properties[key]] = parking + } + + const diffs = [] + for (const parking of osmData.features) { + const veloId = parking.properties[key] + const veloparking = veloparkById[veloId] + if(veloparking === undefined){ + console.error("No velopark entry found for", veloId) + continue + } + diffs.push(this.compare(veloId, parking, veloparking)) + } + + fs.writeFileSync("report_diff.json",JSON.stringify(diffs)) + + + } + constructor() { + super("Compares a velopark geojson with OSM geojson. Usage: `compare velopark.geojson osm.geojson [key-to-compare-on]`. If key-to-compare-on is not given, `ref:velopark` will be used") + } + +} + +new Compare().run() diff --git a/scripts/velopark/veloParkToGeojson.ts b/scripts/velopark/veloParkToGeojson.ts index 0157e9b1f..dcd200a32 100644 --- a/scripts/velopark/veloParkToGeojson.ts +++ b/scripts/velopark/veloParkToGeojson.ts @@ -15,6 +15,20 @@ class VeloParkToGeojson extends Script { ) } + exportTo(filename: string, features){ + fs.writeFileSync( + filename+"_" + new Date().toISOString() + ".geojson", + JSON.stringify( + { + type: "FeatureCollection", + features, + }, + null, + " " + ) + ) + } + async main(args: string[]): Promise { console.log("Downloading velopark data") // Download data for NIS-code 1000. 1000 means: all of belgium @@ -38,12 +52,15 @@ class VeloParkToGeojson extends Script { ) console.log("OpenStreetMap contains", seenIds.size, "bicycle parkings with a velopark ref") const allVelopark = data.map((f) => VeloparkLoader.convert(f)) + this.exportTo("velopark_all", allVelopark) + const features = allVelopark.filter((f) => !seenIds.has(f.properties["ref:velopark"])) const allProperties = new Set() for (const feature of features) { Object.keys(feature.properties).forEach((k) => allProperties.add(k)) } + this.exportTo("velopark_noncynced",features) allProperties.delete("ref:velopark") for (const feature of features) { allProperties.forEach((k) => { @@ -51,17 +68,7 @@ class VeloParkToGeojson extends Script { }) } - fs.writeFileSync( - "velopark_id_only_export_" + new Date().toISOString() + ".geojson", - JSON.stringify( - { - type: "FeatureCollection", - features, - }, - null, - " " - ) - ) + this.exportTo("velopark_nonsynced_id_only", features) } } From 592adfdf2a331b557c8df650a61b5864b07fbbfa Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 15 Feb 2024 03:11:10 +0100 Subject: [PATCH 189/195] Reviews: hopefully fix #1782, add review overview --- langs/en.json | 5 +- src/Logic/State/UserRelatedState.ts | 6 +- src/Logic/UIEventSource.ts | 3 +- src/Logic/Web/MangroveReviews.ts | 81 +++++++++++++++++++---- src/Models/ThemeViewState.ts | 1 - src/UI/Favourites/FavouriteSummary.svelte | 2 +- src/UI/Favourites/Favourites.svelte | 2 +- src/UI/Reviews/ReviewForm.svelte | 28 ++++++-- src/UI/Reviews/ReviewsOverview.svelte | 40 +++++++++++ src/UI/Reviews/SingleReview.svelte | 31 +++++++-- src/UI/ThemeViewGUI.svelte | 5 ++ 11 files changed, 172 insertions(+), 32 deletions(-) create mode 100644 src/UI/Reviews/ReviewsOverview.svelte diff --git a/langs/en.json b/langs/en.json index b2a5760c3..213f7fff7 100644 --- a/langs/en.json +++ b/langs/en.json @@ -671,6 +671,7 @@ "reviewPlaceholder": "Describe your experience…", "reviewing_as": "Reviewing as {nickname}", "reviewing_as_anonymous": "Reviewing as anonymous", + "reviews_bug": "Expected more reviews? Some reviews are not displayed due to a bug.", "save": "Save review", "saved": "Review saved. Thanks for sharing!", "saving_review": "Saving…", @@ -678,7 +679,9 @@ "title_singular": "One review", "too_long": "At most {max} characters are allowed. Your review has {amount} characters.", "tos": "If you create a review, you agree to the TOS and privacy policy of Mangrove.reviews", - "write_a_comment": "Leave a review…" + "write_a_comment": "Leave a review…", + "your_reviews": "Your previous reviews", + "your_reviews_empty": "We couldn't find any of your previous reviews" }, "split": { "cancel": "Cancel", diff --git a/src/Logic/State/UserRelatedState.ts b/src/Logic/State/UserRelatedState.ts index 5cfe3416d..307f5e104 100644 --- a/src/Logic/State/UserRelatedState.ts +++ b/src/Logic/State/UserRelatedState.ts @@ -73,7 +73,6 @@ export default class UserRelatedState { constructor( osmConnection: OsmConnection, - availableLanguages?: string[], layout?: LayoutConfig, featureSwitches?: FeatureSwitchState, mapProperties?: MapProperties @@ -365,6 +364,11 @@ export default class UserRelatedState { [translationMode] ) + this.mangroveIdentity.getKeyId().addCallbackAndRun(kid => { + amendedPrefs.data["mangrove_kid"] = kid + amendedPrefs.ping() + }) + const usersettingMetaTagging = new ThemeMetaTagging() osmConnection.userDetails.addCallback((userDetails) => { for (const k in userDetails) { diff --git a/src/Logic/UIEventSource.ts b/src/Logic/UIEventSource.ts index ed48130ff..70aa5da87 100644 --- a/src/Logic/UIEventSource.ts +++ b/src/Logic/UIEventSource.ts @@ -306,7 +306,8 @@ export abstract class Store implements Readable { export class ImmutableStore extends Store { public readonly data: T - + static FALSE = new ImmutableStore(false) + static TRUE = new ImmutableStore(true) constructor(data: T) { super() this.data = data diff --git a/src/Logic/Web/MangroveReviews.ts b/src/Logic/Web/MangroveReviews.ts index 3aa9582f6..426257bac 100644 --- a/src/Logic/Web/MangroveReviews.ts +++ b/src/Logic/Web/MangroveReviews.ts @@ -5,10 +5,12 @@ import { Feature, Position } from "geojson" import { GeoOperations } from "../GeoOperations" export class MangroveIdentity { - public readonly keypair: Store - public readonly key_id: Store + private readonly keypair: Store + private readonly mangroveIdentity: UIEventSource + private readonly key_id: Store constructor(mangroveIdentity: UIEventSource) { + this.mangroveIdentity = mangroveIdentity const key_id = new UIEventSource(undefined) this.key_id = key_id const keypairEventSource = new UIEventSource(undefined) @@ -23,13 +25,7 @@ export class MangroveIdentity { key_id.setData(pem) }) - try { - if (!Utils.runningFromConsole && (mangroveIdentity.data ?? "") === "") { - MangroveIdentity.CreateIdentity(mangroveIdentity).then((_) => {}) - } - } catch (e) { - console.error("Could not create identity: ", e) - } + } /** @@ -44,8 +40,61 @@ export class MangroveIdentity { // Identity has been loaded via osmPreferences by now - we don't overwrite return } + console.log("Creating a new Mangrove identity!") identity.setData(JSON.stringify(jwk)) } + + /** + * Only called to create a review. + */ + async getKeypair(): Promise { + if(this.keypair.data ?? "" === ""){ + // We want to create a review, but it seems like no key has been setup at this moment + // We create the key + try { + if (!Utils.runningFromConsole && (this.mangroveIdentity.data ?? "") === "") { + await MangroveIdentity.CreateIdentity(this.mangroveIdentity) + } + } catch (e) { + console.error("Could not create identity: ", e) + } + } + return this.keypair.data + } + + getKeyId(): Store { + return this.key_id + } + + private allReviewsById : UIEventSource<(Review & {kid: string, signature: string})[]>= undefined + + + /** + * Gets all reviews that are made for the current identity. + */ + public getAllReviews(): Store<(Review & {kid: string, signature: string})[]>{ + if(this.allReviewsById !== undefined){ + return this.allReviewsById + } + this.allReviewsById = new UIEventSource( []) + this.key_id.map(pem => { + if(pem === undefined){ + return [] + } + MangroveReviews.getReviews({ + kid: pem + }).then(allReviews => { + this.allReviewsById.setData(allReviews.reviews.map(r => ({ + ...r, ...r.payload + }))) + }) + }) + return this.allReviewsById + } + + addReview(review: Review & {kid, signature}) { + this.allReviewsById?.setData(this.allReviewsById?.data?.concat([review])) + } } /** @@ -176,26 +225,30 @@ export default class FeatureReviews { * The given review is uploaded to mangrove.reviews and added to the list of known reviews */ public async createReview(review: Omit): Promise { - if(review.opinion.length > FeatureReviews .REVIEW_OPINION_MAX_LENGTH){ + if(review.opinion !== undefined && review.opinion.length > FeatureReviews .REVIEW_OPINION_MAX_LENGTH){ throw "Opinion too long, should be at most "+FeatureReviews.REVIEW_OPINION_MAX_LENGTH+" characters long" } const r: Review = { sub: this.subjectUri.data, ...review, } - const keypair: CryptoKeyPair = this._identity.keypair.data + const keypair: CryptoKeyPair = await this._identity.getKeypair() const jwt = await MangroveReviews.signReview(keypair, r) const kid = await MangroveReviews.publicToPem(keypair.publicKey) await MangroveReviews.submitReview(jwt) - this._reviews.data.push({ + const reviewWithKid = { ...r, kid, signature: jwt, madeByLoggedInUser: new ImmutableStore(true), - }) + } + this._reviews.data.push( reviewWithKid) this._reviews.ping() + this._identity.addReview(reviewWithKid) } + + /** * Adds given reviews to the 'reviews'-UI-eventsource * @param reviews @@ -235,7 +288,7 @@ export default class FeatureReviews { ...review, kid: reviewData.kid, signature: reviewData.signature, - madeByLoggedInUser: this._identity.key_id.map((user_key_id) => { + madeByLoggedInUser: this._identity.getKeyId().map((user_key_id) => { return reviewData.kid === user_key_id }), }) diff --git a/src/Models/ThemeViewState.ts b/src/Models/ThemeViewState.ts index 4895ee1aa..f5c576281 100644 --- a/src/Models/ThemeViewState.ts +++ b/src/Models/ThemeViewState.ts @@ -171,7 +171,6 @@ export default class ThemeViewState implements SpecialVisualizationState { }) this.userRelatedState = new UserRelatedState( this.osmConnection, - layout?.language, layout, this.featureSwitches, this.mapProperties diff --git a/src/UI/Favourites/FavouriteSummary.svelte b/src/UI/Favourites/FavouriteSummary.svelte index 4a2bd7fb7..a505419f4 100644 --- a/src/UI/Favourites/FavouriteSummary.svelte +++ b/src/UI/Favourites/FavouriteSummary.svelte @@ -29,7 +29,7 @@ center() } - const titleIconBlacklist = ["osmlink", "sharelink", "favourite_title_icon"] + let titleIconBlacklist = ["osmlink", "sharelink", "favourite_title_icon"] {#if favLayer !== undefined} diff --git a/src/UI/Favourites/Favourites.svelte b/src/UI/Favourites/Favourites.svelte index 3996e6eb3..406e216d0 100644 --- a/src/UI/Favourites/Favourites.svelte +++ b/src/UI/Favourites/Favourites.svelte @@ -48,7 +48,7 @@
console.log("Got keypress", e)}> - + {#each $favourites as feature (feature.properties.id)} diff --git a/src/UI/Reviews/ReviewForm.svelte b/src/UI/Reviews/ReviewForm.svelte index d6f06e40a..f37722d89 100644 --- a/src/UI/Reviews/ReviewForm.svelte +++ b/src/UI/Reviews/ReviewForm.svelte @@ -35,9 +35,9 @@ let _state: "ask" | "saving" | "done" = "ask" - const connection = state.osmConnection + let connection = state.osmConnection - const hasError: Store = opinion.mapD(op => { + let hasError: Store = opinion.mapD(op => { const tooLong = op.length > FeatureReviews.REVIEW_OPINION_MAX_LENGTH if (tooLong) { return "too_long" @@ -45,6 +45,8 @@ return undefined }) + let uploadFailed: string = undefined + async function save() { if (hasError.data) { return @@ -63,13 +65,24 @@ console.log("Testing - not actually saving review", review) await Utils.waitFor(1000) } else { - await reviews.createReview(review) + try { + + await reviews.createReview(review) + } catch (e) { + console.error("Could not create review due to", e) + uploadFailed = "" + e + } } _state = "done" } - -{#if _state === "done"} +{#if uploadFailed} +
+ + + {uploadFailed} +
+{:else if _state === "done"} {:else if _state === "saving"} @@ -109,8 +122,9 @@ /> {#if $hasError === "too_long"}
- - + +
{/if} diff --git a/src/UI/Reviews/ReviewsOverview.svelte b/src/UI/Reviews/ReviewsOverview.svelte new file mode 100644 index 000000000..bd1de598b --- /dev/null +++ b/src/UI/Reviews/ReviewsOverview.svelte @@ -0,0 +1,40 @@ + + + +
+ + + +
+ + + {#if $reviews?.length > 0} +
console.log("Got keypress", e)}> + {#each $reviews as review (review.sub)} + + {/each} +
+ {:else} + + {/if} + +
+ + +
+
diff --git a/src/UI/Reviews/SingleReview.svelte b/src/UI/Reviews/SingleReview.svelte index 104a58fef..4e66a2edd 100644 --- a/src/UI/Reviews/SingleReview.svelte +++ b/src/UI/Reviews/SingleReview.svelte @@ -1,22 +1,43 @@ -
-
+
+ {#if showSub} + + {/if} +
+

+ +

+
From 9db0dcf3873c717ab18b8e44087670c7cb47c412 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 15 Feb 2024 03:23:48 +0100 Subject: [PATCH 190/195] Fix #1677 --- src/UI/ThemeViewGUI.svelte | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/UI/ThemeViewGUI.svelte b/src/UI/ThemeViewGUI.svelte index df8587765..f76355d46 100644 --- a/src/UI/ThemeViewGUI.svelte +++ b/src/UI/ThemeViewGUI.svelte @@ -357,13 +357,14 @@ {#if ($showCrosshair === "yes" && $currentZoom >= 17) || $showCrosshair === "always" || $visualFeedback}
>
{/if} + -
i !== undefined)}> From d4bfd3894ac0830232936bbb4111c2977f0535d4 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 15 Feb 2024 03:25:28 +0100 Subject: [PATCH 191/195] Move comment out of block opening --- src/UI/ThemeViewGUI.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/UI/ThemeViewGUI.svelte b/src/UI/ThemeViewGUI.svelte index f76355d46..ca13fbc30 100644 --- a/src/UI/ThemeViewGUI.svelte +++ b/src/UI/ThemeViewGUI.svelte @@ -356,9 +356,10 @@ {#if ($showCrosshair === "yes" && $currentZoom >= 17) || $showCrosshair === "always" || $visualFeedback} +
+ style="height: 100vh" >
From 3461ed60990fc91e399ef3c4cd082029db2059dd Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 15 Feb 2024 16:02:22 +0100 Subject: [PATCH 192/195] Fix: don't measure compass if alpha is null, fix #1787 --- src/Sensors/Orientation.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Sensors/Orientation.ts b/src/Sensors/Orientation.ts index ca71cf2b5..e9d28ffd3 100644 --- a/src/Sensors/Orientation.ts +++ b/src/Sensors/Orientation.ts @@ -77,6 +77,9 @@ export class Orientation { } private update(event: DeviceOrientationEvent) { + if(event.alpha === null || event.beta === null || event.gamma === null){ + return + } this.gotMeasurement.setData(true) // IF the phone is lying flat, then: // alpha is the compass direction (but not absolute) From 327a2860c7c8107a20f5b356091328d598f5a2fd Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 15 Feb 2024 16:20:47 +0100 Subject: [PATCH 193/195] UX: fix #1786 --- src/UI/ThemeViewGUI.svelte | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/UI/ThemeViewGUI.svelte b/src/UI/ThemeViewGUI.svelte index ca13fbc30..70c229c75 100644 --- a/src/UI/ThemeViewGUI.svelte +++ b/src/UI/ThemeViewGUI.svelte @@ -265,12 +265,15 @@ {#if state.lastClickObject.hasPresets || state.lastClickObject.hasNoteLayer}