From 67f1bcbe206155c04e073f6d0451825a3a537d22 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Mon, 16 Dec 2024 13:57:13 +0100 Subject: [PATCH 01/31] Remove empty file --- scripts/scrapeOsm.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 scripts/scrapeOsm.ts diff --git a/scripts/scrapeOsm.ts b/scripts/scrapeOsm.ts deleted file mode 100644 index e69de29bb..000000000 From 6cb16877e8e27a70c1e1c327d965be1e0ab45788 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 17 Dec 2024 19:03:05 +0100 Subject: [PATCH 02/31] Fix: imagepreview: improve typing; fix comparison tool --- src/UI/Comparison/ComparisonTable.svelte | 2 +- src/UI/Image/AttributedImage.svelte | 11 +++++++---- src/UI/Image/ImageOperations.svelte | 2 +- src/UI/Image/ImagePreview.svelte | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/UI/Comparison/ComparisonTable.svelte b/src/UI/Comparison/ComparisonTable.svelte index d528f849c..ff6c7bfae 100644 --- a/src/UI/Comparison/ComparisonTable.svelte +++ b/src/UI/Comparison/ComparisonTable.svelte @@ -137,7 +137,7 @@ diff --git a/src/UI/Image/AttributedImage.svelte b/src/UI/Image/AttributedImage.svelte index e2c677386..851e4231d 100644 --- a/src/UI/Image/AttributedImage.svelte +++ b/src/UI/Image/AttributedImage.svelte @@ -28,7 +28,7 @@ export let imgClass: string = undefined export let state: SpecialVisualizationState = undefined export let attributionFormat: "minimal" | "medium" | "large" = "medium" - export let previewedImage: UIEventSource = undefined + export let previewedImage: UIEventSource> = undefined export let canZoom = previewedImage !== undefined let loaded = false let showBigPreview = new UIEventSource(false) @@ -37,13 +37,13 @@ if (!shown) { previewedImage?.set(undefined) } - }) + }), ) if (previewedImage) { onDestroy( previewedImage.addCallbackAndRun((previewedImage) => { - showBigPreview.set(previewedImage !== undefined && previewedImage?.id === image.id) - }) + showBigPreview.set(previewedImage !== undefined && (previewedImage?.id ?? previewedImage?.url) === (image.id ?? image.url)) + }), ) } @@ -89,6 +89,8 @@ /> + + {#if image.status !== undefined && image.status !== "ready" && image.status !== "hidden"}
@@ -113,6 +115,7 @@ class={imgClass ?? ""} class:cursor-zoom-in={canZoom} on:click={() => { + console.log("Setting",image.url) previewedImage?.set(image) }} on:error={() => { diff --git a/src/UI/Image/ImageOperations.svelte b/src/UI/Image/ImageOperations.svelte index 9a4df51fa..a27b53eda 100644 --- a/src/UI/Image/ImageOperations.svelte +++ b/src/UI/Image/ImageOperations.svelte @@ -15,7 +15,7 @@ import Translations from "../i18n/Translations" import DotMenu from "../Base/DotMenu.svelte" - export let image: ProvidedImage + export let image: Partial & ({ id: string, url: string }) export let clss: string = undefined let isLoaded = new UIEventSource(false) diff --git a/src/UI/Image/ImagePreview.svelte b/src/UI/Image/ImagePreview.svelte index bf966cfcf..0dabcebce 100644 --- a/src/UI/Image/ImagePreview.svelte +++ b/src/UI/Image/ImagePreview.svelte @@ -8,7 +8,7 @@ import Zoomcontrol from "../Zoomcontrol" import { onDestroy } from "svelte" - export let image: ProvidedImage + export let image: Partial let panzoomInstance = undefined let panzoomEl: HTMLElement export let isLoaded: UIEventSource = undefined From 375913492cf6da22389d27a2458e817fa5c19d37 Mon Sep 17 00:00:00 2001 From: Midgard Date: Wed, 18 Dec 2024 01:41:40 +0100 Subject: [PATCH 03/31] Fix: order of commands in npm run init ELI should be downloaded before generate, because it depends on that file. After clearing the generated assets directory, npm run init failed, this commit fixes that. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 23153a906..66eaba818 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ } }, "scripts": { - "init": "npm ci && npm run prep:layeroverview && npm run generate && npm run download:editor-layer-index && npm run generate:layouts && npm run clean", + "init": "npm ci && npm run prep:layeroverview && npm run download:editor-layer-index && npm run generate && npm run generate:layouts && npm run clean", "start": "npm run generate:layeroverview && npm run strt", "strt": "vite --host | sed 's/localhost:/127.0.0.1:/g'", "build": "./scripts/build.sh", From c88ffdae5513e57cdbaf702116714496d6fb3380 Mon Sep 17 00:00:00 2001 From: Midgard Date: Wed, 18 Dec 2024 02:12:00 +0100 Subject: [PATCH 04/31] Fix: remove checked-in file that is covered by .gitignore --- .../generated/editor-layer-index-global.json | 131 ------------------ 1 file changed, 131 deletions(-) delete mode 100644 src/assets/generated/editor-layer-index-global.json diff --git a/src/assets/generated/editor-layer-index-global.json b/src/assets/generated/editor-layer-index-global.json deleted file mode 100644 index f6818d833..000000000 --- a/src/assets/generated/editor-layer-index-global.json +++ /dev/null @@ -1,131 +0,0 @@ -[ - { - "name": "CyclOSM", - "id": "cyclosm", - "url": "https://{switch:a,b,c}.tile-cyclosm.openstreetmap.fr/cyclosm/{zoom}/{x}/{y}.png", - "attribution": { - "text": "Rendering: CyclOSM (hosted by OpenStreetMap France) © Map data OpenStreetMap contributors", - "url": "https://www.cyclosm.org/" - }, - "type": "tms", - "category": "osmbasedmap", - "max_zoom": 20 - }, - { - "name": "Esri World Imagery", - "id": "EsriWorldImagery", - "url": "https://{switch:services,server}.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/{zoom}/{y}/{x}", - "attribution": { - "required": true, - "text": "Terms & Feedback", - "url": "https://wiki.openstreetmap.org/wiki/Esri" - }, - "type": "tms", - "category": "photo", - "max_zoom": 22, - "default": true - }, - { - "name": "Esri World Imagery (Clarity) Beta", - "id": "EsriWorldImageryClarity", - "url": "https://clarity.maptiles.arcgis.com/arcgis/rest/services/World_Imagery/MapServer/tile/{zoom}/{y}/{x}", - "attribution": { - "required": true, - "text": "Terms & Feedback", - "url": "https://wiki.openstreetmap.org/wiki/Esri" - }, - "type": "tms", - "category": "photo", - "max_zoom": 22, - "default": true - }, - { - "name": "Mapbox Satellite", - "id": "Mapbox", - "url": "https://{switch:a,b,c,d}.tiles.mapbox.com/v4/mapbox.satellite/{zoom}/{x}/{y}.jpg?access_token=pk.eyJ1Ijoib3BlbnN0cmVldG1hcCIsImEiOiJjbGZkempiNDkyandvM3lwY3M4MndpdWdzIn0.QnvRv52n3qffVEKmQa9vJA", - "attribution": { - "required": true, - "text": "Terms & Feedback", - "url": "https://www.mapbox.com/about/maps" - }, - "type": "tms", - "category": "photo", - "max_zoom": 22, - "default": true - }, - { - "name": "OpenAerialMap Mosaic, by Kontur.io", - "id": "OpenAerialMapMosaic", - "url": "https://apps.kontur.io/raster-tiler/oam/mosaic/{zoom}/{x}/{y}.png", - "type": "tms", - "category": "photo", - "min_zoom": 1, - "max_zoom": 31, - "default": true - }, - { - "name": "OpenStreetMap (Basque Style)", - "id": "osmfr-basque", - "url": "https://tile.openstreetmap.bzh/eu/{zoom}/{x}/{y}.png", - "attribution": { - "required": true, - "text": "Tiles © OpenStreetMap France, data © OpenStreetMap contributors, ODbL", - "url": "https://www.openstreetmap.org/" - }, - "type": "tms", - "category": "osmbasedmap", - "max_zoom": 20 - }, - { - "name": "OpenStreetMap (Breton Style)", - "id": "osmfr-breton", - "url": "https://tile.openstreetmap.bzh/br/{zoom}/{x}/{y}.png", - "attribution": { - "required": true, - "text": "Tiles © OpenStreetMap France, data © OpenStreetMap contributors, ODbL", - "url": "https://www.openstreetmap.org/" - }, - "type": "tms", - "category": "osmbasedmap", - "max_zoom": 20 - }, - { - "name": "OpenStreetMap (French Style)", - "id": "osmfr", - "url": "https://{switch:a,b,c}.tile.openstreetmap.fr/osmfr/{zoom}/{x}/{y}.png", - "attribution": { - "required": true, - "text": "Tiles © cquest@Openstreetmap France, data © OpenStreetMap contributors, ODBL", - "url": "https://www.openstreetmap.org/" - }, - "type": "tms", - "category": "osmbasedmap", - "max_zoom": 20 - }, - { - "name": "OpenStreetMap (HOT Style)", - "id": "HDM_HOT", - "url": "https://{switch:a,b,c}.tile.openstreetmap.fr/hot/{zoom}/{x}/{y}.png", - "attribution": { - "required": true, - "text": "© OpenStreetMap contributors, tiles courtesy of Humanitarian OpenStreetMap Team", - "url": "https://www.hotosm.org/" - }, - "type": "tms", - "category": "osmbasedmap", - "max_zoom": 20 - }, - { - "name": "OpenStreetMap (Occitan Style)", - "id": "osmfr-occitan", - "url": "https://tile.openstreetmap.bzh/oc/{zoom}/{x}/{y}.png", - "attribution": { - "required": true, - "text": "Tiles © OpenStreetMap France, data © OpenStreetMap contributors, ODbL", - "url": "https://www.openstreetmap.org/" - }, - "type": "tms", - "category": "osmbasedmap", - "max_zoom": 20 - } -] \ No newline at end of file From 8c1a73c9d50824b668dd0a31b435bbed2b15ffc1 Mon Sep 17 00:00:00 2001 From: Midgard Date: Wed, 18 Dec 2024 02:12:58 +0100 Subject: [PATCH 05/31] Fix: deduplicate .gitignore and reorder --- .gitignore | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 715afe780..eeb8ce127 100644 --- a/.gitignore +++ b/.gitignore @@ -4,9 +4,10 @@ node_modules .cache/* .idea/* scratch +src/assets/editor-layer-index.json +src/assets/generated/ assets/editor-layer-index.json assets/generated/* -src/assets/generated/ assets/layers/favourite/favourite.json public/*.webmanifest /*.html @@ -20,8 +21,6 @@ missing_translations.txt .DS_Store Svg.ts data/ -src/assets/generated/nsi_stats/brand.json -src/assets/generated/nsi_stats/brand.summarized.json Folder.DotSettings.user index_*.ts @@ -29,7 +28,6 @@ index_*.ts *.doctest.ts service-worker.js .env -src/assets/editor-layer-index.json error_changeset_* From 48305273e52ac85667b85b69ac2f6d13b0de8d19 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 19 Dec 2024 13:59:40 +0100 Subject: [PATCH 06/31] Fix: fix inspector --- inspector.html | 2 +- langs/en.json | 23 ++++- langs/layers/nl.json | 42 +++++++- langs/themes/nl.json | 114 ++++++++++++++++++++- langs/zh_Hant.json | 54 +++++----- src/UI/History/History.svelte | 7 +- src/UI/History/PreviouslySpiedUsers.svelte | 34 ++++-- src/UI/InspectorGUI.svelte | 9 +- src/UI/InspectorGUI.ts | 4 +- 9 files changed, 238 insertions(+), 51 deletions(-) diff --git a/inspector.html b/inspector.html index 8f6f088bd..3f63817fc 100644 --- a/inspector.html +++ b/inspector.html @@ -11,7 +11,7 @@
Loading inspector...
- + diff --git a/langs/en.json b/langs/en.json index 73114b29b..f235fb9e8 100644 --- a/langs/en.json +++ b/langs/en.json @@ -616,7 +616,28 @@ "title": "MapComplete" }, "inspector": { - "menu": "Inspect a contributor" + "aggregateView": "Aggregate", + "answeredCountTimes": "Answered {count} times", + "backToIndex": "Back to the map overview", + "createdBy": "Created by {contributor}", + "earlierInspected": "See contributors you've inspected before", + "images": "Created images", + "load": "Inspect changes for map area", + "mapView": "Map", + "menu": "Inspect a contributor", + "onlyGeometry": "Only changes in geometry", + "previouslySpied": { + "addLabel": "Add a label", + "allChanges": "Load all changes for users with this label", + "label": "Label", + "noLabels": "No labels", + "remove": "Remove", + "time": "Time of last inspection", + "title": "Earlier inspected constributors", + "username": "Username" + }, + "tableView": "Overview", + "title": "Inspect a contributor" }, "move": { "cancel": "Select a different reason", diff --git a/langs/layers/nl.json b/langs/layers/nl.json index eeb48492c..74bf33124 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -2036,6 +2036,9 @@ }, "title": { "mappings": { + "0": { + "then": "{name}" + }, "1": { "then": "Vogelkijkhut {name}" }, @@ -6340,6 +6343,11 @@ } }, "title": { + "mappings": { + "0": { + "then": "{name}" + } + }, "render": "Natuurgebied" } }, @@ -6871,6 +6879,21 @@ "render": "Picknicktafel" } }, + "play_forest": { + "description": "Een speelbos is een vrij toegankelijke zone in een bos", + "name": "Speelbossen", + "title": { + "mappings": { + "0": { + "then": "{name}" + }, + "1": { + "then": "Speelbos {name}" + } + }, + "render": "Speelbos" + } + }, "playground": { "deletion": { "nonDeleteMappings": { @@ -8428,6 +8451,9 @@ }, "title": { "mappings": { + "0": { + "then": "{name}" + }, "1": { "then": "Voetpad" }, @@ -10580,13 +10606,25 @@ } }, "village_green": { - "description": "Een laag die dorpsgroen toont (gemeenschapsgroen, maar niet echt een park)" + "description": "Een laag die dorpsgroen toont (gemeenschapsgroen, maar niet echt een park)", + "name": "Speelweide", + "title": { + "mappings": { + "0": { + "then": "{name}" + } + }, + "render": "Speelweide" + } }, "visitor_information_centre": { "description": "Een bezoekerscentrum biedt informatie over een specifieke attractie of bezienswaardigheid waar het is gevestigd.", "name": "Bezoekerscentrum", "title": { "mappings": { + "0": { + "then": "{name:nl}" + }, "1": { "then": "{name}" } @@ -10816,4 +10854,4 @@ "render": "windturbine" } } -} +} \ No newline at end of file diff --git a/langs/themes/nl.json b/langs/themes/nl.json index 47a5f8968..3f55d0345 100644 --- a/langs/themes/nl.json +++ b/langs/themes/nl.json @@ -653,8 +653,37 @@ "building type": { "question": "Wat voor soort gebouw is dit?" }, + "grb-fixme": { + "mappings": { + "0": { + "then": "Geen fixme" + } + }, + "question": "Wat zegt de fixme?", + "render": "De fixme is {fixme}" + }, + "grb-housenumber": { + "mappings": { + "0": { + "then": "Geen huisnummer" + } + }, + "question": "Wat is het huisnummer?", + "render": "Het huisnummer is {addr:housenumber}" + }, + "grb-min-level": { + "question": "Hoeveel verdiepingen ontbreken?", + "render": "Dit gebouw begint maar op de {building:min_level} verdieping" + }, "grb-reference": { "render": "Werd geïmporteerd vanuit GRB, het referentienummer is {source:geometry:ref}" + }, + "grb-street": { + "question": "Wat is de straat?", + "render": "De straat is {addr:street}" + }, + "grb-unit": { + "render": "De wooneenheid-aanduiding is {addr:unit} " } } }, @@ -671,8 +700,35 @@ } } } + }, + "5": { + "override": { + "tagRenderings+": { + "0": { + "mappings": { + "0": { + "then": "Geen omliggend OSM-gebouw gevonden" + } + } + }, + "3": { + "mappings": { + "0": { + "then": "Geen omliggend OSM-gebouw gevonden. Een omliggend gebouw is nodig om dit punt als adres punt toe te voegen.
Importeer eerst de gebouwen. Vernieuw dan de pagina om losse adressen toe te voegen
" + } + }, + "render": { + "special": { + "text": "Voeg dit adres als een nieuw adrespunt toe" + } + } + } + } + } } - } + }, + "shortDescription": "Grb import helper tool", + "title": "GRB import helper" }, "guideposts": { "description": "Wegwijzers (ook wel handwijzer genoemd) zijn vaak te vinden langs officiële wandel-, fiets-, ski- of paardrijroutes om de richtingen naar verschillende bestemmingen aan te geven. Vaak zijn ze vernoemd naar een regio of plaats en geven ze de hoogte aan.\n\nDe positie van een wegwijzer kan door een wandelaar/fietser/renner/skiër worden gebruikt als bevestiging van de huidige positie, vooral als ze een gedrukte kaart zonder GPS-ontvanger gebruiken. ", @@ -1108,6 +1164,11 @@ }, "title": "Dierenartsen, hondenloopzones en andere huisdiervriendelijke plaatsen" }, + "play_forests": { + "description": "Een speelbos is een zone in een bos die vrij toegankelijk is voor spelende kinderen. Deze wordt in bossen van het Agentschap Natuur en bos altijd aangeduid met het overeenkomstige bord.", + "shortDescription": "Deze kaart toont speelbossen", + "title": "Speelbossen" + }, "playgrounds": { "description": "Op deze kaart vind je speeltuinen en kan je zelf meer informatie en foto's toevoegen", "shortDescription": "Een kaart met speeltuinen", @@ -1181,6 +1242,47 @@ "description": "Alles om te skiën", "title": "Skipistes en kabelbanen" }, + "speelplekken": { + "description": "

Welkom bij de Groendoener!

De Zuidrand dat is spelen, ravotten, chillen, wandelen,… in het groen. Meer dan 200 grote en kleine speelplekken liggen er in parken, in bossen en op pleintjes te wachten om ontdekt te worden. De verschillende speelplekken werden getest én goedgekeurd door kinder- en jongerenreporters uit de Zuidrand. Met leuke challenges dagen de reporters jou uit om ook op ontdekking te gaan. Klik op een speelplek op de kaart, bekijk het filmpje en ga op verkenning!

Het project groendoener kadert binnen het strategisch project Beleefbare Open Ruimte in de Antwerpse Zuidrand en is een samenwerking tussen het departement Leefmilieu van provincie Antwerpen, Sportpret vzw, een OpenStreetMap-België Consultent en Createlli vzw. Het project kwam tot stand met steun van Departement Omgeving van de Vlaamse Overheid.
", + "layers": { + "6": { + "name": "Wandelroutes van provincie Antwerpen", + "tagRenderings": { + "walk-description": { + "render": "

Korte beschrijving:

{description}" + }, + "walk-length": { + "render": "Deze wandeling is {_length:km}km lang" + }, + "walk-operator": { + "question": "Wie beheert deze wandeling en plaatst dus de signalisatiebordjes?" + }, + "walk-operator-email": { + "question": "Naar wie kan men emailen bij problemen rond signalisatie?", + "render": "Bij problemen met signalisatie kan men emailen naar {operator:email}" + }, + "walk-type": { + "mappings": { + "0": { + "then": "Dit is een internationale wandelroute" + }, + "1": { + "then": "Dit is een nationale wandelroute" + }, + "2": { + "then": "Dit is een regionale wandelroute" + }, + "3": { + "then": "Dit is een lokale wandelroute" + } + } + } + } + } + }, + "shortDescription": "Speelplekken in de Antwerpse Zuidrand", + "title": "Welkom bij de groendoener!" + }, "sport_pitches": { "description": "Een sportveld is een ingerichte plaats met infrastructuur om een sport te beoefenen", "shortDescription": "Deze kaart toont sportvelden", @@ -1301,6 +1403,10 @@ }, "title": "Straatverlichting" }, + "street_lighting_assen": { + "description": "Op deze kaart vind je alles over straatlantaarns + een dataset van Assen", + "title": "Straatverlichting - Assen" + }, "surveillance": { "description": "Op deze open kaart kan je bewakingscamera's vinden.", "shortDescription": "Bewakingscameras en dergelijke", @@ -1414,9 +1520,13 @@ "description": "Kaart met afvalbakken en recyclingfaciliteiten.", "title": "Afval" }, + "waste_assen": { + "description": "Kaart met afvalbakken en recyclingfaciliteiten + een dataset voor Assen.", + "title": "Afval - Assen" + }, "waste_basket": { "description": "Op deze kaart vind je afvalbakken bij jou in de buurt. Als er een afvalbak ontbreekt op deze kaart, kun je deze zelf toevoegen", "shortDescription": "Een kaart met vuilnisbakken", "title": "Vuilnisbakken" } -} +} \ No newline at end of file diff --git a/langs/zh_Hant.json b/langs/zh_Hant.json index 389b736ef..1d0773ac7 100644 --- a/langs/zh_Hant.json +++ b/langs/zh_Hant.json @@ -193,18 +193,19 @@ "openOsmchaLastWeek": "檢視最近 7 天的編輯", "openPanoramax": "在這邊開啟 Panoramax", "openThemeDocumentation": "開啟專題地圖 {name} 的文件", + "panoramaxHelp": "Panoramax 是收集街景照片的線上服務,並且以自由授權釋出。貢獻者能夠使用這些照片來改進開放街圖", + "panoramaxLicenseCCBYSA": "你的圖片會以 CC-BY-SA 釋出 - 每個人都能夠在提及你名字的情形下再利用你的圖片", "seeOnMapillary": "在 Mapillary 觀看這張影像", "themeBy": "由 {author} 維護主題", "title": "版權與署名", - "translatedBy": "MapComplete 由 {contributors} 翻譯,而且還有 {hiddenCount} 更多貢獻者", - "panoramaxHelp": "Panoramax 是收集街景照片的線上服務,並且以自由授權釋出。貢獻者能夠使用這些照片來改進開放街圖", - "panoramaxLicenseCCBYSA": "你的圖片會以 CC-BY-SA 釋出 - 每個人都能夠在提及你名字的情形下再利用你的圖片" + "translatedBy": "MapComplete 由 {contributors} 翻譯,而且還有 {hiddenCount} 更多貢獻者" }, "back": "返回", "backToIndex": "回到所有主題地圖的總覽頁面", "backgroundMap": "選擇背景圖層", "backgroundSwitch": "切換背景", "cancel": "取消", + "clearPendingChanges": "清除待處理的變動", "confirm": "確認", "customThemeIntro": "這些是先前使用者創造的主題。", "customThemeTitle": "客製化主題", @@ -232,6 +233,7 @@ "downloadGeojson": "下載可視資料為 GeoJSON", "downloadGpx": "下載為 GPX 檔案", "downloadGpxHelper": "GPX 檔案能被大部分導航裝置或 app 使用", + "downloadImage": "下載圖片", "exporting": "匯出中…", "includeMetaData": "包括 metadata (上次編輯者、計算數值等)", "licenseInfo": "

著作權聲明

提供的資料採用 ODbL 授權釋出。可以用任何目標再利用資料,但是需
  • 標明 © 開放街圖貢獻者
  • 任何變動必須相同方式授權
請閱讀完整的 著作權聲明。", @@ -241,8 +243,7 @@ }, "title": "下載", "toMuch": "有很多圖徵可以下載了", - "uploadGpx": "上傳軌跡到開放街圖", - "downloadImage": "下載圖片" + "uploadGpx": "上傳軌跡到開放街圖" }, "enableGeolocationForSafari": "你沒有看到要求地理位置權限的跳出視窗?", "enableGeolocationForSafariLink": "學習如何在設定當中啟用地理位置權限", @@ -251,9 +252,9 @@ "example": "例子", "examples": "例子", "filterPanel": { + "allTypes": "所有類型", "disableAll": "關閉所有", - "enableAll": "啟用所有", - "allTypes": "所有類型" + "enableAll": "啟用所有" }, "geopermissionDenied": "使用地理位置要求已經被拒絕", "histogram": { @@ -263,10 +264,10 @@ "background": "改變背景", "filter": "篩選資料", "jumpToLocation": "到你目前的位置", + "locationNotAvailable": "無法取得 GPS 位置,裝置有取得位置資訊還是在隧道內?", "menu": "選單", "zoomIn": "放大", - "zoomOut": "縮小", - "locationNotAvailable": "無法取得 GPS 位置,裝置有取得位置資訊還是在隧道內?" + "zoomOut": "縮小" }, "layerSelection": { "title": "選擇圖層", @@ -372,24 +373,24 @@ "save": "儲存", "screenToSmall": "在新視窗中開啟 {theme}", "search": { - "error": "有狀況發生了。", - "nothing": "沒有找到。", - "recents": "最近看到的地方", - "search": "搜尋地點", - "searching": "搜尋中…", + "activeFilters": "啟用篩選", + "clearFilters": "清除篩選", + "deleteSearchHistory": "刪除位置歷史", + "deleteThemeHistory": "刪除先前觀看的主題", "editSearchSyncSettings": "編輯同步設定", "editThemeSync": "編輯同步設定", + "error": "有狀況發生了。", "instructions": "使用搜尋欄位來搜尋位置、篩選或是其他主題地圖", "locations": "位置", "nMoreFilters": "{n} 更多", + "nothing": "沒有找到。", "nothingFor": "尋找 {term} 沒有結果", "otherMaps": "其他地圖", "pickFilter": "選擇篩選", "recentThemes": "最近觀看的地圖", - "activeFilters": "啟用篩選", - "clearFilters": "清除篩選", - "deleteSearchHistory": "刪除位置歷史", - "deleteThemeHistory": "刪除先前觀看的主題" + "recents": "最近看到的地方", + "search": "搜尋地點", + "searching": "搜尋中…" }, "searchAnswer": "搜尋選項", "seeIndex": "查看所有專題地圖的概覽", @@ -519,8 +520,7 @@ "readMore": "閱讀剩下的條目內容", "searchToShort": "你的搜尋檢索太短了,請輸入長一點的文字", "searchWikidata": "在 Wikidata 搜尋" - }, - "clearPendingChanges": "清除待處理的變動" + } }, "hotkeyDocumentation": { "action": "行動", @@ -761,11 +761,11 @@ }, "unknown": { "clear": "清除答案", + "explanation": "如果答案不正確但實際數值不清楚的話,則清除這一些資訊。不會清除其他資訊。", "keep": "保留答案", "markUnknown": "標示為未知", "removedKeys": "下列鍵會被移除:", - "title": "要標記為未知嗎?", - "explanation": "如果答案不正確但實際數值不清楚的話,則清除這一些資訊。不會清除其他資訊。" + "title": "要標記為未知嗎?" }, "userinfo": { "notLoggedIn": "你已經登出了" @@ -827,6 +827,9 @@ "description": "正數、整數", "noZero": "並不允許零" }, + "regex": { + "description": "正規表示式" + }, "slope": { "inputExplanation": "將你手機放在地上,並且指向上坡方向。" }, @@ -838,18 +841,15 @@ }, "tooLong": "文字太長了,最多允許 255 字元,你現在還有 {count} 字元。", "url": { + "aggregator": "{host} 是第三方網站,如果可能請搜尋官方網站。", "description": "連接到網站", "feedback": "這不是有效的網址", - "aggregator": "{host} 是第三方網站,如果可能請搜尋官方網站。", "spamSite": "{host} 被視為低品質網站,並不被允許使用。" }, "wikidata": { "description": "Wikidata 編號", "empty": "請輸入一些 Wikidata 項目", "startsWithQ": "維基數據編號以 Q 開頭後面接數字" - }, - "regex": { - "description": "正規表示式" } } -} +} \ No newline at end of file diff --git a/src/UI/History/History.svelte b/src/UI/History/History.svelte index d878f22fe..66792cefc 100644 --- a/src/UI/History/History.svelte +++ b/src/UI/History/History.svelte @@ -30,10 +30,11 @@ return true } console.log( - "Checking if ", + "Checking if", step.tags["_last_edit:contributor"], "is contained in", - onlyShowChangesBy + onlyShowChangesBy, + usernames.has(step.tags["_last_edit:contributor"]) ) return usernames.has(step.tags["_last_edit:contributor"]) }) @@ -49,7 +50,7 @@ * These layers are only shown if there are tag changes as well */ const ignoreLayersIfNoChanges: ReadonlySet = new Set(["walls_and_buildings"]) - const t = Translations.t.inspector.previousContributors + const t = Translations.t.inspector {#if !$allGeometry || !ignoreLayersIfNoChanges.has($lastStep?.layer?.id)} diff --git a/src/UI/History/PreviouslySpiedUsers.svelte b/src/UI/History/PreviouslySpiedUsers.svelte index a37e1262e..a9029a85a 100644 --- a/src/UI/History/PreviouslySpiedUsers.svelte +++ b/src/UI/History/PreviouslySpiedUsers.svelte @@ -5,7 +5,8 @@ import { createEventDispatcher } from "svelte" import { XCircleIcon } from "@babeard/svelte-heroicons/solid" import AccordionSingle from "../Flowbite/AccordionSingle.svelte" - import Dropdown from "../Base/Dropdown.svelte" + import Translations from "../i18n/Translations" + import Tr from "../Base/Tr.svelte" export let osmConnection: OsmConnection export let inspectedContributors: UIEventSource< @@ -41,23 +42,32 @@ inspectedContributors.data.sort((a, b) => (a[key] ?? "").localeCompare(b[key] ?? "")) inspectedContributors.ping() } + + const t = Translations.t.inspector.previouslySpied - + + + + + + + - {#each $inspectedContributors as c} @@ -85,7 +95,8 @@
Labels
{#if $labels.length === 0} - No labels +
+ {:else} {#each $labels as label}
@@ -102,7 +113,8 @@ ) }} > - See all changes for these users +
+ {/each} @@ -115,7 +127,7 @@ class:disabled={!(labelField?.length > 0)} class="disabled shrink-0" > - Add label + diff --git a/src/UI/InspectorGUI.svelte b/src/UI/InspectorGUI.svelte index 11e7625c3..c07d65c8e 100644 --- a/src/UI/InspectorGUI.svelte +++ b/src/UI/InspectorGUI.svelte @@ -28,6 +28,7 @@ import Translations from "./i18n/Translations" import Tr from "./Base/Tr.svelte" + console.log("Loading inspector GUI") let username = QueryParameters.GetQueryParameter("user", undefined, "Inspect this user") let step = new UIEventSource<"waiting" | "loading" | "done">("waiting") let map = new UIEventSource(undefined) @@ -121,7 +122,7 @@ const t = Translations.t.inspector -
+

@@ -192,7 +193,7 @@ selectedElement.set(undefined)} />

- + {/if} @@ -218,7 +219,9 @@
-
Earlier inspected constributors
+
+
+ Date: Thu, 19 Dec 2024 18:34:39 +0100 Subject: [PATCH 07/31] Themes(shops): add optometrist service --- assets/layers/shops/shops.json | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/assets/layers/shops/shops.json b/assets/layers/shops/shops.json index 2995dda96..936c6eff7 100644 --- a/assets/layers/shops/shops.json +++ b/assets/layers/shops/shops.json @@ -523,6 +523,40 @@ } ] }, + { + "id": "optometrist_service", + "condition": { + "or": [ + "shop=optician" + ] + }, + "question": { + "en": "Is it possible to get an eye exam here by a professional optometrist?", + "nl": "Kan men hier een oogtest laten uitvoeren door een professionele optometrist?" + }, + "mappings": [ + { + "if": "healthcare=optometrist", + "addExtraTags": [ + "not:healthcare=optometrist" + ], + "then": { + "en": "Offers eye exams by professional optometrists", + "nl": "Hier kan men een oogtest door een professionele optometrist laten uitvoeren" + } + }, + { + "if": "not:healtcare=optometrist", + "addExtraTags": [ + "healthcare=optometrist" + ], + "then": { + "en": "No certified eye exam possible here", + "nl": "Hier kan men geen oog-examen laten uitvoeren door een optometrist" + } + } + ] + }, { "id": "key_cutter", "question": { From 2a6e118b5ba91ed4e3dfd9737cbf3ab96f937cff Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sun, 22 Dec 2024 01:21:20 +0100 Subject: [PATCH 08/31] Fix: clean up minzoomVisible in some themes (gh#2279) --- assets/layers/waste_basket/waste_basket.json | 8 ++------ assets/layers/waste_disposal/waste_disposal.json | 3 +-- assets/themes/postboxes/postboxes.json | 1 - assets/themes/waste/waste.json | 3 +-- 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/assets/layers/waste_basket/waste_basket.json b/assets/layers/waste_basket/waste_basket.json index 1d35dbb3c..ced6f1579 100644 --- a/assets/layers/waste_basket/waste_basket.json +++ b/assets/layers/waste_basket/waste_basket.json @@ -27,11 +27,7 @@ "cs": "Jedná se o veřejný odpadkový koš, odpadkový koš, kam můžete vyhodit odpadky." }, "source": { - "osmTags": { - "and": [ - "amenity=waste_basket" - ] - } + "osmTags": "amenity=waste_basket" }, "minzoom": 17, "title": { @@ -489,4 +485,4 @@ "enableRelocation": false, "enableImproveAccuracy": true } -} +} \ No newline at end of file diff --git a/assets/layers/waste_disposal/waste_disposal.json b/assets/layers/waste_disposal/waste_disposal.json index da2a0c29c..7710aa5c1 100644 --- a/assets/layers/waste_disposal/waste_disposal.json +++ b/assets/layers/waste_disposal/waste_disposal.json @@ -27,7 +27,6 @@ "osmTags": "amenity=waste_disposal" }, "minzoom": 12, - "minzoomVisible": 12, "title": { "render": { "en": "Waste Disposal", @@ -324,4 +323,4 @@ "enableImproveAccuracy": true, "enableRelocation": true } -} +} \ No newline at end of file diff --git a/assets/themes/postboxes/postboxes.json b/assets/themes/postboxes/postboxes.json index 296b7ca2c..1bd9e2a74 100644 --- a/assets/themes/postboxes/postboxes.json +++ b/assets/themes/postboxes/postboxes.json @@ -61,7 +61,6 @@ "builtin": "shops", "override": { "minzoom": 18, - "minzoomVisible": 18, "description": { "en": "Add a new post partner to the map in an existing shop", "de": "Hinzufügen eines neuen Post-Partners auf der Karte in einem bestehenden Geschäft", diff --git a/assets/themes/waste/waste.json b/assets/themes/waste/waste.json index e93cf7f51..0994e3fab 100644 --- a/assets/themes/waste/waste.json +++ b/assets/themes/waste/waste.json @@ -40,8 +40,7 @@ { "builtin": "waste_basket", "override": { - "minzoom": 16, - "minzoomVisible": 16 + "minzoom": 16 } }, "recycling", From 80776f5943704b36dfc7d5047e4b0aa8be6f552d Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 26 Dec 2024 17:38:50 +0100 Subject: [PATCH 09/31] Themes(shops): change 'optomotetrist'-service into multiple other services --- assets/layers/shops/shops.json | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/assets/layers/shops/shops.json b/assets/layers/shops/shops.json index 936c6eff7..a11dec9b7 100644 --- a/assets/layers/shops/shops.json +++ b/assets/layers/shops/shops.json @@ -525,34 +525,37 @@ }, { "id": "optometrist_service", +x² "#": "Quite often, opticians also sell hearing aids and vice versa. They often also have a certified optometrist or audiologist", "condition": { "or": [ - "shop=optician" + "shop=optician", + "shop=hearing_aid", + "shop=medical_supply" ] }, "question": { - "en": "Is it possible to get an eye exam here by a professional optometrist?", - "nl": "Kan men hier een oogtest laten uitvoeren door een professionele optometrist?" + "en": "Are medical services available here?", + "nl": "Zijn hier medische services beschikbaar?" }, + "multiAnswer": true, "mappings": [ { "if": "healthcare=optometrist", + "ifnot": "not:healtcare=optometrist", "addExtraTags": [ "not:healthcare=optometrist" ], "then": { - "en": "Offers eye exams by professional optometrists", - "nl": "Hier kan men een oogtest door een professionele optometrist laten uitvoeren" + "en": "This shop offers eye exams by certified optometrists", + "nl": "Hier kan men een oogtest door een erkende optometrist laten uitvoeren" } }, { - "if": "not:healtcare=optometrist", - "addExtraTags": [ - "healthcare=optometrist" - ], + "if": "healthcare=audiologist", + "ifnot": "not:healthcare=audiologist", "then": { - "en": "No certified eye exam possible here", - "nl": "Hier kan men geen oog-examen laten uitvoeren door een optometrist" + "en": "This shop offers hearing tests by a certified audiologist", + "nl": "HIer kan men een hoortest laten uitvoeren door een erkende audioloog" } } ] From 4925e216b8d273868787c7b057391c10ab474267 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 26 Dec 2024 17:39:23 +0100 Subject: [PATCH 10/31] Themes(shops): change 'optomotetrist'-service into multiple other services --- assets/layers/shops/shops.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/layers/shops/shops.json b/assets/layers/shops/shops.json index a11dec9b7..f6b2b7786 100644 --- a/assets/layers/shops/shops.json +++ b/assets/layers/shops/shops.json @@ -525,7 +525,7 @@ }, { "id": "optometrist_service", -x² "#": "Quite often, opticians also sell hearing aids and vice versa. They often also have a certified optometrist or audiologist", + "#": "Quite often, opticians also sell hearing aids and vice versa. They often also have a certified optometrist or audiologist", "condition": { "or": [ "shop=optician", From ff53354c96e0d96761ba508bfc7dfae616e5cf52 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 26 Dec 2024 17:52:47 +0100 Subject: [PATCH 11/31] Chore: translation reset --- langs/layers/en.json | 12 ++++++++++++ langs/layers/nl.json | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/langs/layers/en.json b/langs/layers/en.json index 19974bd70..9491780d5 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -1636,6 +1636,7 @@ } }, "question": "What is the type of this bicycle parking?", + "questionHint": "This is about the physical device one places their bicycle in and uses to lock", "render": "This is a bicycle parking of the type: {bicycle_parking}" }, "Capacity": { @@ -9742,6 +9743,17 @@ }, "question": "Does this shop offer key cutting?" }, + "optometrist_service": { + "mappings": { + "0": { + "then": "This shop offers eye exams by certified optometrists" + }, + "1": { + "then": "This shop offers hearing tests by a certified audiologist" + } + }, + "question": "Are medical services available here?" + }, "organic": { "mappings": { "0": { diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 140a409c2..fc60afc0d 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -1600,6 +1600,7 @@ } }, "question": "Van welk type is deze fietsparking?", + "questionHint": "Dit gaat over het voorwerp waarin je je fiets plaats en mee op slot zet", "render": "Dit is een fietsparking van het type: {bicycle_parking}" }, "Capacity": { @@ -8325,6 +8326,17 @@ } } }, + "optometrist_service": { + "mappings": { + "0": { + "then": "Hier kan men een oogtest door een erkende optometrist laten uitvoeren" + }, + "1": { + "then": "HIer kan men een hoortest laten uitvoeren door een erkende audioloog" + } + }, + "question": "Zijn hier medische services beschikbaar?" + }, "organic": { "mappings": { "0": { From ceabc5573c4f7efbe684f1483c04e5184a2326bd Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 26 Dec 2024 19:38:33 +0100 Subject: [PATCH 12/31] Studio: fix #2316 --- src/UI/Base/If.svelte | 22 ++++++++++--------- .../TagRendering/TagRenderingQuestion.svelte | 7 +++--- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/UI/Base/If.svelte b/src/UI/Base/If.svelte index 2644653ec..5c87f6495 100644 --- a/src/UI/Base/If.svelte +++ b/src/UI/Base/If.svelte @@ -5,16 +5,18 @@ /** * For some stupid reason, it is very hard to let {#if} work together with UIEventSources, so we wrap then here */ - export let condition: Store - let _c = condition.data - onDestroy( - condition.addCallback((c) => { - /* Do _not_ abbreviate this as `.addCallback(c => _c = c)`. This is the same as writing `.addCallback(c => {return _c = c})`, - which will _unregister_ the callback if `c = true`! */ - _c = c - return false - }) - ) + export let condition: Store | undefined + let _c = condition?.data + if (condition !== undefined) { + onDestroy( + condition.addCallback((c) => { + /* Do _not_ abbreviate this as `.addCallback(c => _c = c)`. This is the same as writing `.addCallback(c => {return _c = c})`, + which will _unregister_ the callback if `c = true`! */ + _c = c + return false + }) + ) + } {#if _c} diff --git a/src/UI/Popup/TagRendering/TagRenderingQuestion.svelte b/src/UI/Popup/TagRendering/TagRenderingQuestion.svelte index 2662ac7f1..bed0fd484 100644 --- a/src/UI/Popup/TagRendering/TagRenderingQuestion.svelte +++ b/src/UI/Popup/TagRendering/TagRenderingQuestion.svelte @@ -33,7 +33,6 @@ import Markdown from "../../Base/Markdown.svelte" import { Utils } from "../../../Utils" import type { UploadableTag } from "../../../Logic/Tags/TagTypes" - import { Modal } from "flowbite-svelte" import Popup from "../../Base/Popup.svelte" import If from "../../Base/If.svelte" import DotMenu from "../../Base/DotMenu.svelte" @@ -341,7 +340,7 @@ .catch(console.error) } - let disabledInTheme = state.userRelatedState.getThemeDisabled(state.theme.id, layer?.id) + let disabledInTheme = state.userRelatedState?.getThemeDisabled(state.theme.id, layer?.id) ?? new UIEventSource([]) let menuIsOpened = new UIEventSource(false) function disableQuestion() { @@ -361,7 +360,7 @@ {#if question !== undefined && $apiState !== "readonly" && $apiState !== "offline"}
- {#if layer.isNormal()} + {#if layer?.isNormal()} @@ -559,7 +558,7 @@
v === "yes" || v === "full" || v === "always" )} > From 90626c632aee2be3b6d0a5e31d514b92ceccc4ab Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 26 Dec 2024 20:01:03 +0100 Subject: [PATCH 13/31] Themes(cycleways_and_roads): use 'width' instead of 'width:carriageway', fix #2318 --- .../cycleways_and_roads.json | 21 +++++++++++-------- langs/layers/ca.json | 4 ++-- langs/layers/cs.json | 4 ++-- langs/layers/de.json | 4 ++-- langs/layers/en.json | 4 ++-- langs/layers/es.json | 4 ++-- langs/layers/fr.json | 4 ++-- langs/layers/nl.json | 4 ++-- 8 files changed, 26 insertions(+), 23 deletions(-) diff --git a/assets/layers/cycleways_and_roads/cycleways_and_roads.json b/assets/layers/cycleways_and_roads/cycleways_and_roads.json index 60a743c60..56f74cdec 100644 --- a/assets/layers/cycleways_and_roads/cycleways_and_roads.json +++ b/assets/layers/cycleways_and_roads/cycleways_and_roads.json @@ -273,6 +273,9 @@ "width": "8" } ], + "calculatedTags": [ + "width:= ! ! (feat.properties['width:carriageway']) ? feat.properties['width:carriageway'] : feat.properties['width'] " + ], "tagRenderings": [ "images", { @@ -1289,16 +1292,16 @@ ] }, "render": { - "en": "The carriage width of this road is {width:carriageway}m", - "nl": "De breedte van deze rijbaan in deze straat is {width:carriageway}m", - "de": "Die Fahrbahnbreite dieser Straße beträgt {width:carriageway}m", - "fr": "La largeur de cette chaussée est de {width:carriageway}m", - "ca": "L'amplada dels carrils d'aquesta carretera és {width:carriageway}m", - "cs": "Šířka vozovky této silnice je {width:carriageway}m", - "es": "El ancho de la calzada de esta carretera es de {width:carriageway}m" + "en": "The carriage width of this road is {width}m", + "nl": "De breedte van deze rijbaan in deze straat is {width}m", + "de": "Die Fahrbahnbreite dieser Straße beträgt {width}m", + "fr": "La largeur de cette chaussée est de {width}m", + "ca": "L'amplada dels carrils d'aquesta carretera és {width}m", + "cs": "Šířka vozovky této silnice je {width}m", + "es": "El ancho de la calzada de esta carretera es de {width}m" }, "freeform": { - "key": "width:carriageway", + "key": "width", "type": "distance", "helperArgs": [ "20", @@ -1314,7 +1317,7 @@ "cs": "Jaká je šířka vozovky této silnice (v metrech)?", "es": "¿Cuál es el ancho de la calzada de esta carretera (en metros)?" }, - "id": "width:carriageway", + "id": "width", "questionHint": { "en": "This is measured curb to curb and thus includes the width of parallell parking lanes", "nl": "Dit is gemeten van stoepsteen tot stoepsteen, dus inclusief een parallelle parkeerstrook", diff --git a/langs/layers/ca.json b/langs/layers/ca.json index 4c871f3b2..40d207118 100644 --- a/langs/layers/ca.json +++ b/langs/layers/ca.json @@ -2889,10 +2889,10 @@ }, "question": "Aquesta carretera està il·luminada?" }, - "width:carriageway": { + "width": { "question": "Quina és l'amplada dels carrils d'aquesta carretera (en metres)?", "questionHint": "Això es mesura de vorera a vorera i, per tant, inclou l'amplada dels aparcaments en línia", - "render": "L'amplada dels carrils d'aquesta carretera és {width:carriageway}m" + "render": "L'amplada dels carrils d'aquesta carretera és {width}m" } }, "title": { diff --git a/langs/layers/cs.json b/langs/layers/cs.json index 1b916cd7b..8d6c793b4 100644 --- a/langs/layers/cs.json +++ b/langs/layers/cs.json @@ -3694,10 +3694,10 @@ }, "question": "Je tato ulice osvětlená?" }, - "width:carriageway": { + "width": { "question": "Jaká je šířka vozovky této silnice (v metrech)?", "questionHint": "Jedná se o měření od krajnice ke krajnici a to včetně šířky paralelních parkovacích pruhů", - "render": "Šířka vozovky této silnice je {width:carriageway}m" + "render": "Šířka vozovky této silnice je {width}m" } }, "title": { diff --git a/langs/layers/de.json b/langs/layers/de.json index 7214fcbc9..a3fb02655 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -3958,10 +3958,10 @@ }, "question": "Ist diese Straße beleuchtet?" }, - "width:carriageway": { + "width": { "question": "Wie groß ist die Fahrbahnbreite dieser Straße (in Metern)?", "questionHint": "Diese wird von Bordstein zu Bordstein gemessen und schließt daher die Breite von parallelen Parkspuren ein", - "render": "Die Fahrbahnbreite dieser Straße beträgt {width:carriageway}m" + "render": "Die Fahrbahnbreite dieser Straße beträgt {width}m" } }, "title": { diff --git a/langs/layers/en.json b/langs/layers/en.json index 9491780d5..5bef75b8a 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -4009,10 +4009,10 @@ }, "question": "Is this street lit?" }, - "width:carriageway": { + "width": { "question": "What is the carriage width of this road (in meters)?", "questionHint": "This is measured curb to curb and thus includes the width of parallell parking lanes", - "render": "The carriage width of this road is {width:carriageway}m" + "render": "The carriage width of this road is {width}m" } }, "title": { diff --git a/langs/layers/es.json b/langs/layers/es.json index 9633344eb..c91e7fe29 100644 --- a/langs/layers/es.json +++ b/langs/layers/es.json @@ -3950,10 +3950,10 @@ }, "question": "¿Está iluminada esta calle?" }, - "width:carriageway": { + "width": { "question": "¿Cuál es el ancho de la calzada de esta carretera (en metros)?", "questionHint": "Esto se mide de bordillo a bordillo e incluye el ancho de los carriles de aparcamiento paralelos", - "render": "El ancho de la calzada de esta carretera es de {width:carriageway}m" + "render": "El ancho de la calzada de esta carretera es de {width}m" } }, "title": { diff --git a/langs/layers/fr.json b/langs/layers/fr.json index 3a918d648..4865e37f5 100644 --- a/langs/layers/fr.json +++ b/langs/layers/fr.json @@ -2795,10 +2795,10 @@ }, "question": "Cette rue est-elle éclairée ?" }, - "width:carriageway": { + "width": { "question": "Quelle est la largeur de cette chaussée (en mètres) ?", "questionHint": "Elle est mesurée d'une bordure à l'autre et inclut donc la largeur des voies de stationnement parallèles", - "render": "La largeur de cette chaussée est de {width:carriageway}m" + "render": "La largeur de cette chaussée est de {width}m" } }, "title": { diff --git a/langs/layers/nl.json b/langs/layers/nl.json index fc60afc0d..a45565b83 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -3964,10 +3964,10 @@ }, "question": "Is deze weg verlicht?" }, - "width:carriageway": { + "width": { "question": "Hoe breed is de rijbaan in deze straat (in meters)?", "questionHint": "Dit is gemeten van stoepsteen tot stoepsteen, dus inclusief een parallelle parkeerstrook", - "render": "De breedte van deze rijbaan in deze straat is {width:carriageway}m" + "render": "De breedte van deze rijbaan in deze straat is {width}m" } }, "title": { From 33efe4f2c25eda7dc179e572313e02b72de0e96a Mon Sep 17 00:00:00 2001 From: Midgard Date: Wed, 18 Dec 2024 01:41:40 +0100 Subject: [PATCH 14/31] Fix: order of commands in npm run init ELI should be downloaded before generate, because it depends on that file. After clearing the generated assets directory, npm run init failed, this commit fixes that. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 23153a906..66eaba818 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ } }, "scripts": { - "init": "npm ci && npm run prep:layeroverview && npm run generate && npm run download:editor-layer-index && npm run generate:layouts && npm run clean", + "init": "npm ci && npm run prep:layeroverview && npm run download:editor-layer-index && npm run generate && npm run generate:layouts && npm run clean", "start": "npm run generate:layeroverview && npm run strt", "strt": "vite --host | sed 's/localhost:/127.0.0.1:/g'", "build": "./scripts/build.sh", From bccd74408659cf2377bbfa98e1d02b8e2b186937 Mon Sep 17 00:00:00 2001 From: Midgard Date: Wed, 18 Dec 2024 02:12:00 +0100 Subject: [PATCH 15/31] Fix: remove checked-in file that is covered by .gitignore --- .../generated/editor-layer-index-global.json | 131 ------------------ 1 file changed, 131 deletions(-) delete mode 100644 src/assets/generated/editor-layer-index-global.json diff --git a/src/assets/generated/editor-layer-index-global.json b/src/assets/generated/editor-layer-index-global.json deleted file mode 100644 index f6818d833..000000000 --- a/src/assets/generated/editor-layer-index-global.json +++ /dev/null @@ -1,131 +0,0 @@ -[ - { - "name": "CyclOSM", - "id": "cyclosm", - "url": "https://{switch:a,b,c}.tile-cyclosm.openstreetmap.fr/cyclosm/{zoom}/{x}/{y}.png", - "attribution": { - "text": "Rendering: CyclOSM (hosted by OpenStreetMap France) © Map data OpenStreetMap contributors", - "url": "https://www.cyclosm.org/" - }, - "type": "tms", - "category": "osmbasedmap", - "max_zoom": 20 - }, - { - "name": "Esri World Imagery", - "id": "EsriWorldImagery", - "url": "https://{switch:services,server}.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/{zoom}/{y}/{x}", - "attribution": { - "required": true, - "text": "Terms & Feedback", - "url": "https://wiki.openstreetmap.org/wiki/Esri" - }, - "type": "tms", - "category": "photo", - "max_zoom": 22, - "default": true - }, - { - "name": "Esri World Imagery (Clarity) Beta", - "id": "EsriWorldImageryClarity", - "url": "https://clarity.maptiles.arcgis.com/arcgis/rest/services/World_Imagery/MapServer/tile/{zoom}/{y}/{x}", - "attribution": { - "required": true, - "text": "Terms & Feedback", - "url": "https://wiki.openstreetmap.org/wiki/Esri" - }, - "type": "tms", - "category": "photo", - "max_zoom": 22, - "default": true - }, - { - "name": "Mapbox Satellite", - "id": "Mapbox", - "url": "https://{switch:a,b,c,d}.tiles.mapbox.com/v4/mapbox.satellite/{zoom}/{x}/{y}.jpg?access_token=pk.eyJ1Ijoib3BlbnN0cmVldG1hcCIsImEiOiJjbGZkempiNDkyandvM3lwY3M4MndpdWdzIn0.QnvRv52n3qffVEKmQa9vJA", - "attribution": { - "required": true, - "text": "Terms & Feedback", - "url": "https://www.mapbox.com/about/maps" - }, - "type": "tms", - "category": "photo", - "max_zoom": 22, - "default": true - }, - { - "name": "OpenAerialMap Mosaic, by Kontur.io", - "id": "OpenAerialMapMosaic", - "url": "https://apps.kontur.io/raster-tiler/oam/mosaic/{zoom}/{x}/{y}.png", - "type": "tms", - "category": "photo", - "min_zoom": 1, - "max_zoom": 31, - "default": true - }, - { - "name": "OpenStreetMap (Basque Style)", - "id": "osmfr-basque", - "url": "https://tile.openstreetmap.bzh/eu/{zoom}/{x}/{y}.png", - "attribution": { - "required": true, - "text": "Tiles © OpenStreetMap France, data © OpenStreetMap contributors, ODbL", - "url": "https://www.openstreetmap.org/" - }, - "type": "tms", - "category": "osmbasedmap", - "max_zoom": 20 - }, - { - "name": "OpenStreetMap (Breton Style)", - "id": "osmfr-breton", - "url": "https://tile.openstreetmap.bzh/br/{zoom}/{x}/{y}.png", - "attribution": { - "required": true, - "text": "Tiles © OpenStreetMap France, data © OpenStreetMap contributors, ODbL", - "url": "https://www.openstreetmap.org/" - }, - "type": "tms", - "category": "osmbasedmap", - "max_zoom": 20 - }, - { - "name": "OpenStreetMap (French Style)", - "id": "osmfr", - "url": "https://{switch:a,b,c}.tile.openstreetmap.fr/osmfr/{zoom}/{x}/{y}.png", - "attribution": { - "required": true, - "text": "Tiles © cquest@Openstreetmap France, data © OpenStreetMap contributors, ODBL", - "url": "https://www.openstreetmap.org/" - }, - "type": "tms", - "category": "osmbasedmap", - "max_zoom": 20 - }, - { - "name": "OpenStreetMap (HOT Style)", - "id": "HDM_HOT", - "url": "https://{switch:a,b,c}.tile.openstreetmap.fr/hot/{zoom}/{x}/{y}.png", - "attribution": { - "required": true, - "text": "© OpenStreetMap contributors, tiles courtesy of Humanitarian OpenStreetMap Team", - "url": "https://www.hotosm.org/" - }, - "type": "tms", - "category": "osmbasedmap", - "max_zoom": 20 - }, - { - "name": "OpenStreetMap (Occitan Style)", - "id": "osmfr-occitan", - "url": "https://tile.openstreetmap.bzh/oc/{zoom}/{x}/{y}.png", - "attribution": { - "required": true, - "text": "Tiles © OpenStreetMap France, data © OpenStreetMap contributors, ODbL", - "url": "https://www.openstreetmap.org/" - }, - "type": "tms", - "category": "osmbasedmap", - "max_zoom": 20 - } -] \ No newline at end of file From 9d6036be8797df4c92b2f0a02fcb349e1877f819 Mon Sep 17 00:00:00 2001 From: Midgard Date: Wed, 18 Dec 2024 02:12:58 +0100 Subject: [PATCH 16/31] Fix: deduplicate .gitignore and reorder --- .gitignore | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 715afe780..eeb8ce127 100644 --- a/.gitignore +++ b/.gitignore @@ -4,9 +4,10 @@ node_modules .cache/* .idea/* scratch +src/assets/editor-layer-index.json +src/assets/generated/ assets/editor-layer-index.json assets/generated/* -src/assets/generated/ assets/layers/favourite/favourite.json public/*.webmanifest /*.html @@ -20,8 +21,6 @@ missing_translations.txt .DS_Store Svg.ts data/ -src/assets/generated/nsi_stats/brand.json -src/assets/generated/nsi_stats/brand.summarized.json Folder.DotSettings.user index_*.ts @@ -29,7 +28,6 @@ index_*.ts *.doctest.ts service-worker.js .env -src/assets/editor-layer-index.json error_changeset_* From cda798cd64e7c0f04785ec19f532afde8468feab Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Wed, 18 Dec 2024 01:21:07 +0100 Subject: [PATCH 17/31] chore: automated housekeeping... --- Docs/URL_Parameters.md | 8 ++++---- src/assets/contributors.json | 2 +- src/assets/translators.json | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Docs/URL_Parameters.md b/Docs/URL_Parameters.md index 968e14b09..bbd609d7c 100644 --- a/Docs/URL_Parameters.md +++ b/Docs/URL_Parameters.md @@ -346,7 +346,7 @@ No default value set The initial/current zoom level -This documentation is defined in the source code at [InitialMapPositioning.ts](/src/Logic/Actors/InitialMapPositioning.ts#L42) +This documentation is defined in the source code at [InitialMapPositioning.ts](/src/Logic/Actors/InitialMapPositioning.ts#L43) The default value is _1_ @@ -354,7 +354,7 @@ The default value is _1_ The initial/current latitude -This documentation is defined in the source code at [InitialMapPositioning.ts](/src/Logic/Actors/InitialMapPositioning.ts#L42) +This documentation is defined in the source code at [InitialMapPositioning.ts](/src/Logic/Actors/InitialMapPositioning.ts#L43) The default value is _0_ @@ -362,7 +362,7 @@ The default value is _0_ The initial/current longitude of the app -This documentation is defined in the source code at [InitialMapPositioning.ts](/src/Logic/Actors/InitialMapPositioning.ts#L42) +This documentation is defined in the source code at [InitialMapPositioning.ts](/src/Logic/Actors/InitialMapPositioning.ts#L43) The default value is _0_ @@ -410,7 +410,7 @@ The default value is _0_ The mode the application starts in, e.g. 'map', 'dashboard' or 'statistics' -This documentation is defined in the source code at [generateDocs.ts](ervdvn/git2/MapComplete/scripts/generateDocs.ts#L436) +This documentation is defined in the source code at [generateDocs.ts](ervdvn/git/MapComplete/scripts/generateDocs.ts#L436) The default value is _map_ diff --git a/src/assets/contributors.json b/src/assets/contributors.json index 98446521a..c270d25f3 100644 --- a/src/assets/contributors.json +++ b/src/assets/contributors.json @@ -1,7 +1,7 @@ { "contributors": [ { - "commits": 8792, + "commits": 8786, "contributor": "Pieter Vander Vennet" }, { diff --git a/src/assets/translators.json b/src/assets/translators.json index 5c017a5bd..5a902b0ad 100644 --- a/src/assets/translators.json +++ b/src/assets/translators.json @@ -741,4 +741,4 @@ "contributor": "Carlos Ramos Carreño" } ] -} \ No newline at end of file +} From e7f0291038735b1e61580568cccbfedbcfa744da Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 26 Dec 2024 22:47:48 +0100 Subject: [PATCH 18/31] Chore: improve typing --- src/Logic/Osm/OsmObjectDownloader.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Logic/Osm/OsmObjectDownloader.ts b/src/Logic/Osm/OsmObjectDownloader.ts index b0e9f9cec..9b1644dbb 100644 --- a/src/Logic/Osm/OsmObjectDownloader.ts +++ b/src/Logic/Osm/OsmObjectDownloader.ts @@ -127,7 +127,9 @@ export default class OsmObjectDownloader { * Beware: their geometry will be incomplete! */ public async DownloadReferencingWays(id: string): Promise { - const data = await Utils.downloadJsonCached(`${this.backend}api/0.6/${id}/ways`, 60 * 1000) + const data = await Utils.downloadJsonCached<{ + elements: { id: number }[] + }>(`${this.backend}api/0.6/${id}/ways`, 60 * 1000) return data.elements.map((wayInfo) => new OsmWay(wayInfo.id, wayInfo)) } @@ -136,7 +138,7 @@ export default class OsmObjectDownloader { * Beware: their geometry will be incomplete! */ public async DownloadReferencingRelations(id: string): Promise { - const data = await Utils.downloadJsonCached( + const data = await Utils.downloadJsonCached<{ elements: { id: number }[] }>( `${this.backend}api/0.6/${id}/relations`, 60 * 1000 ) From 5284d94427df74bd8fed85f9d506d147d068cdb3 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 26 Dec 2024 23:19:47 +0100 Subject: [PATCH 19/31] Fix: various small fixes --- src/Logic/ImageProviders/AllImageProviders.ts | 1 - src/Logic/Web/NameSuggestionIndex.ts | 4 ++-- src/UI/Base/ToSvelte.svelte | 3 ++- src/UI/Popup/DeleteFlow/DeleteFlowState.ts | 16 +++++++--------- src/UI/Popup/TagRendering/Questionbox.svelte | 2 +- .../Popup/TagRendering/SpecialTranslation.svelte | 6 +++++- 6 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/Logic/ImageProviders/AllImageProviders.ts b/src/Logic/ImageProviders/AllImageProviders.ts index 96552b06d..8db2a8d01 100644 --- a/src/Logic/ImageProviders/AllImageProviders.ts +++ b/src/Logic/ImageProviders/AllImageProviders.ts @@ -91,7 +91,6 @@ export default class AllImageProviders { However, we override them if a custom image tag is set, e.g. 'image:menu' */ const prefixes = tagKey ?? imageProvider.defaultKeyPrefixes - console.log("Prefixes are", tagKey, prefixes) const singleSource = tags.bindD((tags) => imageProvider.getRelevantUrls(tags, prefixes)) allSources.push(singleSource) singleSource.addCallbackAndRunD((_) => { diff --git a/src/Logic/Web/NameSuggestionIndex.ts b/src/Logic/Web/NameSuggestionIndex.ts index 7133c1247..1560fd189 100644 --- a/src/Logic/Web/NameSuggestionIndex.ts +++ b/src/Logic/Web/NameSuggestionIndex.ts @@ -312,8 +312,8 @@ export default class NameSuggestionIndex { } const hasSpecial = - i.locationSet.include?.some((i) => i.endsWith(".geojson") || Array.isArray(i)) || - i.locationSet.exclude?.some((i) => i.endsWith(".geojson") || Array.isArray(i)) + i.locationSet.include?.some((i) => Array.isArray(i) || i.endsWith(".geojson")) || + i.locationSet.exclude?.some((i) => Array.isArray(i) || i.endsWith(".geojson")) if (!hasSpecial) { return false } diff --git a/src/UI/Base/ToSvelte.svelte b/src/UI/Base/ToSvelte.svelte index c6c742071..2828ef42c 100644 --- a/src/UI/Base/ToSvelte.svelte +++ b/src/UI/Base/ToSvelte.svelte @@ -29,6 +29,7 @@ html?.remove() uiElement?.Destroy() }) + {#if isSvelte} @@ -42,6 +43,6 @@ {:else} {/if} -{:else} +{:else if elem !== undefined} {/if} diff --git a/src/UI/Popup/DeleteFlow/DeleteFlowState.ts b/src/UI/Popup/DeleteFlow/DeleteFlowState.ts index 97bd82876..cfe7e8d72 100644 --- a/src/UI/Popup/DeleteFlow/DeleteFlowState.ts +++ b/src/UI/Popup/DeleteFlow/DeleteFlowState.ts @@ -43,14 +43,13 @@ export class DeleteFlowState { console.log("Checking deleteability (internet?", useTheInternet, ")") const t = Translations.t.delete const id = this._id - const self = this if (!id.startsWith("node")) { this.canBeDeleted.setData(false) this.canBeDeletedReason.setData(t.isntAPoint) return } - // Does the currently logged in user have enough experience to delete this point? + // Does the currently logged-in user have enough experience to delete this point? const deletingPointsOfOtherAllowed = this._osmConnection.userDetails.map((ud) => { if (ud === undefined) { return undefined @@ -74,10 +73,10 @@ export class DeleteFlowState { // Not yet downloaded return null } - const userId = self._osmConnection.userDetails.data.uid + const userId = this._osmConnection.userDetails.data.uid return !previous.some((editor) => editor !== userId) }, - [self._osmConnection.userDetails] + [this._osmConnection.userDetails] ) // User allowed OR only edited by self? @@ -96,14 +95,13 @@ export class DeleteFlowState { if (allByMyself.data === null && useTheInternet) { // We kickoff the download here as it hasn't yet been downloaded. Note that this is mapped onto 'all by myself' above - const hist = this.objectDownloader - .downloadHistory(id) - .map((versions) => + UIEventSource.FromPromise(this.objectDownloader + .downloadHistory(id)) + .mapD((versions) => versions.map((version) => Number(version.tags["_last_edit:contributor:uid"]) ) - ) - hist.addCallbackAndRunD((hist) => previousEditors.setData(hist)) + ).addCallbackAndRunD((hist) => previousEditors.setData(hist)) } if (allByMyself.data === true) { diff --git a/src/UI/Popup/TagRendering/Questionbox.svelte b/src/UI/Popup/TagRendering/Questionbox.svelte index 83d21adfa..44515c746 100644 --- a/src/UI/Popup/TagRendering/Questionbox.svelte +++ b/src/UI/Popup/TagRendering/Questionbox.svelte @@ -12,7 +12,6 @@ import Translations from "../../i18n/Translations.js" import { Utils } from "../../../Utils" import { onDestroy } from "svelte" - import TagRenderingQuestion from "./TagRenderingQuestion.svelte" import TagRenderingQuestionDynamic from "./TagRenderingQuestionDynamic.svelte" export let layer: LayerConfig @@ -54,6 +53,7 @@ let skippedQuestions = new UIEventSource>(new Set()) let layerDisabledForTheme = state.userRelatedState.getThemeDisabled(state.theme.id, layer.id) layerDisabledForTheme.addCallbackAndRunD((disabled) => { + console.log("Disabled questions are ", disabled) skippedQuestions.set(new Set(disabled.concat(Array.from(skippedQuestions.data)))) }) let questionboxElem: HTMLDivElement diff --git a/src/UI/Popup/TagRendering/SpecialTranslation.svelte b/src/UI/Popup/TagRendering/SpecialTranslation.svelte index 417654a84..e83b647d1 100644 --- a/src/UI/Popup/TagRendering/SpecialTranslation.svelte +++ b/src/UI/Popup/TagRendering/SpecialTranslation.svelte @@ -49,9 +49,13 @@ function createVisualisation(specpart: Exclude): BaseUIElement { { try { - return specpart.func + const uiEl = specpart.func .constr(state, tags, specpart.args, feature, layer) ?.SetClass(specpart.style) + if (uiEl === undefined) { + console.error("Invalid special translation") + } + return uiEl } catch (e) { console.error( "Could not construct a special visualisation with specification", From b0612ac6b143e69eace4846cd7419c598836365e Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Tue, 31 Dec 2024 20:45:18 +0100 Subject: [PATCH 20/31] Config: add 404 and make .html-extension optional --- Docs/ServerConfig/hetzner/Caddyfile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Docs/ServerConfig/hetzner/Caddyfile b/Docs/ServerConfig/hetzner/Caddyfile index 71f0fd162..5cc5c95d8 100644 --- a/Docs/ServerConfig/hetzner/Caddyfile +++ b/Docs/ServerConfig/hetzner/Caddyfile @@ -8,10 +8,18 @@ builds.mapcomplete.org { dev.mapcomplete.org { root * public/develop/ + try_files {path}.html file_server header { +Permissions-Policy "interest-cohort=()" } + handle_errors { + @404 { + expression {http.error.status_code} == 404 + } + rewrite @404 /404.html + file_server + } } single.mapcomplete.org { From 4239221f3504c6458150478f0e4bed94cdf40bdb Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Wed, 1 Jan 2025 16:42:56 +0100 Subject: [PATCH 21/31] Config: improve 404-page --- 404.html | 78 +++++++++++++++++++----------------------- src/UI/NotFound.svelte | 9 ++--- 2 files changed, 40 insertions(+), 47 deletions(-) diff --git a/404.html b/404.html index 58def5685..abeeabcc5 100644 --- a/404.html +++ b/404.html @@ -1,57 +1,49 @@ - - - - - - - + + + + + + + - MapComplete - page not found - - - - + MapComplete - page not found + + + + - - - - - - - - - - + + + + + + + + + + - + - -
- - - - -
- -
- Not found... -
- +
- + diff --git a/src/UI/NotFound.svelte b/src/UI/NotFound.svelte index 0ff564919..01124ed47 100644 --- a/src/UI/NotFound.svelte +++ b/src/UI/NotFound.svelte @@ -2,14 +2,16 @@ import Tr from "./Base/Tr.svelte" import Translations from "./i18n/Translations.ts" import BackButton from "./Base/BackButton.svelte" + import Not_found from "../assets/svg/Not_found.svelte" console.log("???") -
-
+
+ +

+ { window.location = "index.html" }} @@ -19,4 +21,3 @@ - From 515819ee6cf7fd199f09985a323b827e913ca035 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Wed, 1 Jan 2025 16:50:21 +0100 Subject: [PATCH 22/31] Config: improve 404-page: use signup guy image --- assets/svg/license_info.json | 10 ++++++++++ assets/svg/world.svg | 1 + assets/svg/world.svg.license | 2 ++ src/UI/NotFound.svelte | 26 +++++++++++++++----------- src/assets/svg/World.svelte | 4 ++++ 5 files changed, 32 insertions(+), 11 deletions(-) create mode 100644 assets/svg/world.svg create mode 100644 assets/svg/world.svg.license create mode 100644 src/assets/svg/World.svelte diff --git a/assets/svg/license_info.json b/assets/svg/license_info.json index b5712434e..5ed16d9cd 100644 --- a/assets/svg/license_info.json +++ b/assets/svg/license_info.json @@ -1318,5 +1318,15 @@ "sources": [ "https://www.wikipedia.org/" ] + }, + { + "path": "world.svg", + "license": "GPL 2.0", + "authors": [ + "https://github.com/hiddewie" + ], + "sources": [ + "https://github.com/openstreetmap/openstreetmap-website/blob/d38f6484e1aa201284b7eac19dbcc7fad856b88a/assets/sign-up-illustration.svg" + ] } ] \ No newline at end of file diff --git a/assets/svg/world.svg b/assets/svg/world.svg new file mode 100644 index 000000000..ffa41b9f7 --- /dev/null +++ b/assets/svg/world.svg @@ -0,0 +1 @@ + diff --git a/assets/svg/world.svg.license b/assets/svg/world.svg.license new file mode 100644 index 000000000..e3ef47e6c --- /dev/null +++ b/assets/svg/world.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: https://github.com/hiddewie +SPDX-License-Identifier: GPL 2.0 \ No newline at end of file diff --git a/src/UI/NotFound.svelte b/src/UI/NotFound.svelte index 01124ed47..9bbdfb6bf 100644 --- a/src/UI/NotFound.svelte +++ b/src/UI/NotFound.svelte @@ -3,21 +3,25 @@ import Translations from "./i18n/Translations.ts" import BackButton from "./Base/BackButton.svelte" import Not_found from "../assets/svg/Not_found.svelte" + import World from "../assets/svg/World.svelte" + console.log("???") -
- +
+
+

-

+ - { + + { window.location = "index.html" }} - > -
-
- - - + > +
+
+ + + diff --git a/src/assets/svg/World.svelte b/src/assets/svg/World.svelte new file mode 100644 index 000000000..f2d897754 --- /dev/null +++ b/src/assets/svg/World.svelte @@ -0,0 +1,4 @@ + + \ No newline at end of file From c2dad6ebc473261041ee7c7d92955a09a173f1c4 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 2 Jan 2025 01:51:47 +0100 Subject: [PATCH 23/31] UX: don't show 'personal' if not logged in --- src/UI/AllThemesGui.svelte | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/UI/AllThemesGui.svelte b/src/UI/AllThemesGui.svelte index 9681c01da..99d2331d1 100644 --- a/src/UI/AllThemesGui.svelte +++ b/src/UI/AllThemesGui.svelte @@ -89,7 +89,7 @@ } let officialSearched: Store = filtered( - new ImmutableStore(officialThemes) + osmConnection.isLoggedIn.map(loggedIn => loggedIn ? officialThemes : officialThemes.filter(th => th.id !== "personal")) ) let hiddenSearched: Store = filtered(visitedHiddenThemes) let customSearched: Store = filtered(customThemes) @@ -102,6 +102,9 @@ } }) + /** + * Opens the first search candidate + */ function applySearch() { const didRedirect = SearchUtils.applySpecialSearch(search.data) if (didRedirect) { From 4fcdd8ba5a9cbfb06e160a77f51b59f5ee302914 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 2 Jan 2025 03:02:06 +0100 Subject: [PATCH 24/31] Fix: remove obsolete "SetClass"-calls which don't work anyway --- src/UI/SpecialVisualizations.ts | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/UI/SpecialVisualizations.ts b/src/UI/SpecialVisualizations.ts index 37e5eeec6..86c63ece7 100644 --- a/src/UI/SpecialVisualizations.ts +++ b/src/UI/SpecialVisualizations.ts @@ -315,7 +315,7 @@ export class QuestionViz implements SpecialVisualization { state, onlyForLabels: labels, notForLabels: blacklist, - }).SetClass("w-full") + }) } } @@ -435,7 +435,7 @@ export default class SpecialVisualizations { return new SvelteUIElement(AddNewPoint, { state, coordinate: { lon, lat }, - }).SetClass("w-full h-full overflow-auto") + }) }, }, { @@ -564,7 +564,7 @@ export default class SpecialVisualizations { state, feature, layer, - }).SetClass("p-0 m-0") + }) }, }, new ShareLinkViz(), @@ -1065,7 +1065,7 @@ export default class SpecialVisualizations { constr: (state) => { return new SubtleButton( - new SvelteUIElement(Trash).SetClass("h-6"), + new SvelteUIElement(Trash), Translations.t.general.removeLocationHistory ).onClick(() => { state.historicalUserLocations.features.setData([]) @@ -1161,8 +1161,6 @@ export default class SpecialVisualizations { feature, layer, }) - .SetClass("px-1") - .setSpan() }) ), }, @@ -1435,11 +1433,7 @@ export default class SpecialVisualizations { name: "tagrendering", doc: "An entire tagRenderingConfig", required: true, - }, - { - name: "classes", - doc: "CSS-classes to apply on every individual item. Seperated by `space`", - }, + } ], constr( state: SpecialVisualizationState, @@ -1448,8 +1442,7 @@ export default class SpecialVisualizations { feature: Feature, layer: LayerConfig ) { - const [key, tr, classesRaw] = args - let classes = classesRaw ?? "" + const [key, tr] = args const translation = new Translation({ "*": tr }) return new VariableUiElement( featureTags.map((tags) => { @@ -1476,7 +1469,7 @@ export default class SpecialVisualizations { state, feature, layer, - }).SetClass(classes) + }) elements.push(subsTr) } return elements @@ -1689,7 +1682,7 @@ export default class SpecialVisualizations { state, layer, feature, - }).SetClass("w-full h-full") + }) }, }, { From a79557c87c528a8ed40e5883f175be0a93dbcb94 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 2 Jan 2025 03:38:15 +0100 Subject: [PATCH 25/31] Performance: load NSI when needed, should decrease bundle size --- scripts/build.sh | 2 + src/Logic/Web/NameSuggestionIndex.ts | 93 +++++++++++++------ .../Conversion/MiscTagRenderingChecks.ts | 20 ++-- src/Models/ThemeConfig/TagRenderingConfig.ts | 6 +- .../TagRenderingAnswerDynamic.svelte | 23 +++-- .../TagRenderingEditableDynamic.svelte | 27 +++--- .../TagRenderingQuestionDynamic.svelte | 27 +++--- 7 files changed, 122 insertions(+), 76 deletions(-) diff --git a/scripts/build.sh b/scripts/build.sh index 3d3186b73..fa5638783 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -24,6 +24,8 @@ else exit 1 fi +cp node_modules/name-suggestion-index/dist/nsi.json public/assets/data/nsi +cp node_modules/name-suggestion-index/dist/wikidata.min.json public/assets/data/nsi export NODE_OPTIONS=--max-old-space-size=16000 which vite diff --git a/src/Logic/Web/NameSuggestionIndex.ts b/src/Logic/Web/NameSuggestionIndex.ts index 1560fd189..94d245591 100644 --- a/src/Logic/Web/NameSuggestionIndex.ts +++ b/src/Logic/Web/NameSuggestionIndex.ts @@ -1,6 +1,3 @@ -import * as nsi from "../../../node_modules/name-suggestion-index/dist/nsi.json" -import * as nsiWD from "../../../node_modules/name-suggestion-index/dist/wikidata.min.json" - import * as nsiFeatures from "../../../node_modules/name-suggestion-index/dist/featureCollection.json" import { LocationConflation } from "@rapideditor/location-conflation" import type { Feature, MultiPolygon } from "geojson" @@ -56,27 +53,53 @@ export interface NSIItem { } export default class NameSuggestionIndex { - private static readonly nsiFile: Readonly = nsi - private static readonly nsiWdFile: Readonly< + public static readonly supportedTypes = ["brand", + "flag", + "operator", + "transit"] as const + private readonly nsiFile: Readonly + private readonly nsiWdFile: Readonly< Record< string, { logos: { wikidata?: string; facebook?: string } } > - > = nsiWD["wikidata"] + > private static loco = new LocationConflation(nsiFeatures) // Some additional boundaries - private static _supportedTypes: string[] + private _supportedTypes: string[] - public static supportedTypes(): string[] { + constructor(nsiFile: Readonly, nsiWdFile: Readonly< + Record< + string, + { + logos: { wikidata?: string; facebook?: string } + } + >>) { + this.nsiFile = nsiFile + this.nsiWdFile = nsiWdFile + } + + private static inited: NameSuggestionIndex = undefined + + public static async getNsiIndex(): Promise { + if (NameSuggestionIndex.inited) { + return NameSuggestionIndex.inited + } + const [nsi, nsiWd] = await Promise.all(["assets/data/nsi/nsi.json", "assets/data/nsi/wikidata.min.json"].map(url => Utils.downloadJsonCached(url, 1000 * 60 * 60 * 24 * 30))) + NameSuggestionIndex.inited = new NameSuggestionIndex(nsi, nsiWd["wikidata"]) + return NameSuggestionIndex.inited + } + + public supportedTypes(): string[] { if (this._supportedTypes) { return this._supportedTypes } - const keys = Object.keys(NameSuggestionIndex.nsiFile.nsi) + const keys = Object.keys(this.nsiFile.nsi) const all = keys.map( - (k) => NameSuggestionIndex.nsiFile.nsi[k].properties.path.split("/")[0] + (k) => this.nsiFile.nsi[k].properties.path.split("/")[0], ) this._supportedTypes = Utils.Dedup(all).map((s) => { if (s.endsWith("s")) { @@ -99,13 +122,13 @@ export default class NameSuggestionIndex { try { return Utils.downloadJsonCached>( `./assets/data/nsi/stats/${type}.${c.toUpperCase()}.json`, - 24 * 60 * 60 * 1000 + 24 * 60 * 60 * 1000, ) } catch (e) { console.error("Could not fetch " + type + " statistics due to", e) return undefined } - }) + }), ) stats = Utils.NoNull(stats) if (stats.length === 1) { @@ -123,7 +146,10 @@ export default class NameSuggestionIndex { return merged } - public static isSvg(nsiItem: NSIItem, type: string): boolean | undefined { + public isSvg(nsiItem: NSIItem, type: string): boolean | undefined { + if (this.nsiWdFile === undefined) { + throw "nsiWdi file is not loaded, cannot determine if " + nsiItem.id + " has an SVG image" + } const logos = this.nsiWdFile[nsiItem?.tags?.[type + ":wikidata"]]?.logos if (!logos) { return undefined @@ -138,7 +164,7 @@ export default class NameSuggestionIndex { return false } - public static async generateMappings( + public async generateMappings( type: string, tags: Record, country: string[], @@ -148,7 +174,7 @@ export default class NameSuggestionIndex { * If set, sort by frequency instead of alphabetically */ sortByFrequency: boolean - } + }, ): Promise { const mappings: (Mapping & { frequency: number })[] = [] const frequencies = await NameSuggestionIndex.fetchFrequenciesFor(type, country) @@ -157,12 +183,12 @@ export default class NameSuggestionIndex { continue } const value = tags[key] - const actualBrands = NameSuggestionIndex.getSuggestionsForKV( + const actualBrands = this.getSuggestionsForKV( type, key, value, country.join(";"), - location + location, ) if (!actualBrands) { continue @@ -177,7 +203,7 @@ export default class NameSuggestionIndex { if (hasIcon) { // Using works fine without an extension for JPG and PNG, but _not_ svg :( icon = "./assets/data/nsi/logos/" + nsiItem.id - if (NameSuggestionIndex.isSvg(nsiItem, type)) { + if (this.isSvg(nsiItem, type)) { icon = icon + ".svg" } } @@ -207,13 +233,13 @@ export default class NameSuggestionIndex { return mappings } - public static supportedTags( - type: "operator" | "brand" | "flag" | "transit" | string + public supportedTags( + type: "operator" | "brand" | "flag" | "transit" | string, ): Record { const tags: Record = {} - const keys = Object.keys(NameSuggestionIndex.nsiFile.nsi) + const keys = Object.keys(this.nsiFile.nsi) for (const key of keys) { - const nsiItem = NameSuggestionIndex.nsiFile.nsi[key] + const nsiItem = this.nsiFile.nsi[key] const path = nsiItem.properties.path const [osmType, osmkey, osmvalue] = path.split("/") if (type !== osmType && type + "s" !== osmType) { @@ -231,9 +257,9 @@ export default class NameSuggestionIndex { * Returns a list of all brands/operators * @param type */ - public static allPossible(type: "brand" | "operator"): NSIItem[] { + public allPossible(type: "brand" | "operator"): NSIItem[] { const options: NSIItem[] = [] - const tags = NameSuggestionIndex.supportedTags(type) + const tags = this.supportedTags(type) for (const osmKey in tags) { const values = tags[osmKey] for (const osmValue of values) { @@ -249,14 +275,14 @@ export default class NameSuggestionIndex { * @param country: a string containing one or more country codes, separated by ";" * @param location: center point of the feature, should be [lon, lat] */ - public static getSuggestionsFor( + public getSuggestionsFor( type: string, tags: { key: string; value: string }[], country: string = undefined, - location: [number, number] = undefined + location: [number, number] = undefined, ): NSIItem[] { return tags.flatMap((tag) => - this.getSuggestionsForKV(type, tag.key, tag.value, country, location) + this.getSuggestionsForKV(type, tag.key, tag.value, country, location), ) } @@ -274,15 +300,15 @@ export default class NameSuggestionIndex { * @param country: a string containing one or more country codes, separated by ";" * @param location: center point of the feature, should be [lon, lat] */ - public static getSuggestionsForKV( + public getSuggestionsForKV( type: string, key: string, value: string, country: string = undefined, - location: [number, number] = undefined + location: [number, number] = undefined, ): NSIItem[] { const path = `${type}s/${key}/${value}` - const entry = NameSuggestionIndex.nsiFile.nsi[path] + const entry = this.nsiFile.nsi[path] const countries = country?.split(";") ?? [] return entry?.items?.filter((i) => { if (i.locationSet.include.indexOf("001") >= 0) { @@ -335,4 +361,11 @@ export default class NameSuggestionIndex { return false }) } + + public static async generateMappings(key: string, tags: Exclude, undefined | null>, country: string[], center: [number, number], options: { + sortByFrequency: boolean + }): Promise { + const nsi = await NameSuggestionIndex.getNsiIndex() + return nsi.generateMappings(key, tags, country, center, options) + } } diff --git a/src/Models/ThemeConfig/Conversion/MiscTagRenderingChecks.ts b/src/Models/ThemeConfig/Conversion/MiscTagRenderingChecks.ts index c37f2a4b7..eabb1e5a9 100644 --- a/src/Models/ThemeConfig/Conversion/MiscTagRenderingChecks.ts +++ b/src/Models/ThemeConfig/Conversion/MiscTagRenderingChecks.ts @@ -1,17 +1,13 @@ import { DesugaringStep } from "./Conversion" import { TagRenderingConfigJson } from "../Json/TagRenderingConfigJson" import { LayerConfigJson } from "../Json/LayerConfigJson" -import { - MappingConfigJson, - QuestionableTagRenderingConfigJson, -} from "../Json/QuestionableTagRenderingConfigJson" +import { MappingConfigJson, QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson" import { ConversionContext } from "./ConversionContext" import { Translation } from "../../../UI/i18n/Translation" -import NameSuggestionIndex from "../../../Logic/Web/NameSuggestionIndex" import { TagUtils } from "../../../Logic/Tags/TagUtils" -import { Tag } from "../../../Logic/Tags/Tag" import Validators from "../../../UI/InputElement/Validators" import { CheckTranslation } from "./Validation" +import NameSuggestionIndex from "../../../Logic/Web/NameSuggestionIndex" export class MiscTagRenderingChecks extends DesugaringStep { private readonly _layerConfig: LayerConfigJson @@ -197,11 +193,11 @@ export class MiscTagRenderingChecks extends DesugaringStep= 0 + this._layerConfig?.source?.["osmTags"] && + NameSuggestionIndex.supportedTypes.indexOf( json.freeform.key) >= 0 ) { - const tags = TagUtils.TagD(this._layerConfig?.source?.osmTags)?.usedTags() - const suggestions = NameSuggestionIndex.getSuggestionsFor(json.freeform.key, tags) + const tags = TagUtils.TagD(this._layerConfig?.source?.["osmTags"])?.usedTags() + /* const suggestions = nameSuggestionIndexBundled.getSuggestionsFor(json.freeform.key, tags) if (suggestions === undefined) { context .enters("freeform", "type") @@ -209,8 +205,8 @@ export class MiscTagRenderingChecks extends DesugaringStep new Tag(t.key, t.value).asHumanString()).join(" ; ") ) - } - } else if (json.freeform.type === "nsi") { + }*/ + } else if (json.freeform["type"] === "nsi") { context .enters("freeform", "type") .warn( diff --git a/src/Models/ThemeConfig/TagRenderingConfig.ts b/src/Models/ThemeConfig/TagRenderingConfig.ts index 1a33ed5d3..77f5f91e2 100644 --- a/src/Models/ThemeConfig/TagRenderingConfig.ts +++ b/src/Models/ThemeConfig/TagRenderingConfig.ts @@ -999,7 +999,7 @@ export class TagRenderingConfigUtils { tags: UIEventSource>, feature?: Feature ): Store { - const isNSI = NameSuggestionIndex.supportedTypes().indexOf(config.freeform?.key) >= 0 + const isNSI = NameSuggestionIndex.supportedTypes.indexOf( config.freeform?.key) >= 0 if (!isNSI) { return new ImmutableStore(config) } @@ -1019,8 +1019,8 @@ export class TagRenderingConfigUtils { ) ) }) - return extraMappings.map((extraMappings) => { - if (!extraMappings || extraMappings.length == 0) { + return extraMappings.mapD((extraMappings) => { + if (extraMappings.length == 0) { return config } const clone: TagRenderingConfig = Object.create(config) diff --git a/src/UI/Popup/TagRendering/TagRenderingAnswerDynamic.svelte b/src/UI/Popup/TagRendering/TagRenderingAnswerDynamic.svelte index 97184366f..56168d48e 100644 --- a/src/UI/Popup/TagRendering/TagRenderingAnswerDynamic.svelte +++ b/src/UI/Popup/TagRendering/TagRenderingAnswerDynamic.svelte @@ -7,6 +7,7 @@ import { UIEventSource } from "../../../Logic/UIEventSource" import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" import TagRenderingAnswer from "./TagRenderingAnswer.svelte" + import Loading from "../../Base/Loading.svelte" export let tags: UIEventSource | undefined> @@ -20,12 +21,16 @@ let dynamicConfig = TagRenderingConfigUtils.withNameSuggestionIndex(config, tags, selectedElement) - +{#if $dynamicConfig === undefined} + +{:else} + +{/if} diff --git a/src/UI/Popup/TagRendering/TagRenderingEditableDynamic.svelte b/src/UI/Popup/TagRendering/TagRenderingEditableDynamic.svelte index c4fe15c7b..aeeadd204 100644 --- a/src/UI/Popup/TagRendering/TagRenderingEditableDynamic.svelte +++ b/src/UI/Popup/TagRendering/TagRenderingEditableDynamic.svelte @@ -7,6 +7,7 @@ import type { SpecialVisualizationState } from "../../SpecialVisualization" import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" import TagRenderingEditable from "./TagRenderingEditable.svelte" + import Loading from "../../Base/Loading.svelte" export let config: TagRenderingConfig export let tags: UIEventSource> @@ -23,14 +24,18 @@ let dynamicConfig = TagRenderingConfigUtils.withNameSuggestionIndex(config, tags, selectedElement) - +{#if $dynamicConfig} + +{:else} + +{/if} diff --git a/src/UI/Popup/TagRendering/TagRenderingQuestionDynamic.svelte b/src/UI/Popup/TagRendering/TagRenderingQuestionDynamic.svelte index 15cb4f19b..a0ca38077 100644 --- a/src/UI/Popup/TagRendering/TagRenderingQuestionDynamic.svelte +++ b/src/UI/Popup/TagRendering/TagRenderingQuestionDynamic.svelte @@ -18,6 +18,7 @@ import { twJoin } from "tailwind-merge" import Tr from "../../Base/Tr.svelte" import { TrashIcon } from "@rgossiaux/svelte-heroicons/solid" + import Loading from "../../Base/Loading.svelte" export let config: TagRenderingConfig export let tags: UIEventSource> @@ -31,14 +32,18 @@ let dynamicConfig = TagRenderingConfigUtils.withNameSuggestionIndex(config, tags, selectedElement) - - - +{#if $dynamicConfig } + + + +{:else} + +{/if} From 46ac71639596831926224c05d3f507d99321a5e9 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 2 Jan 2025 03:38:39 +0100 Subject: [PATCH 26/31] Fix: fix classes argument --- src/UI/SpecialVisualizations.ts | 238 ++++++++++++++++---------------- 1 file changed, 122 insertions(+), 116 deletions(-) diff --git a/src/UI/SpecialVisualizations.ts b/src/UI/SpecialVisualizations.ts index 86c63ece7..1e3a37fa9 100644 --- a/src/UI/SpecialVisualizations.ts +++ b/src/UI/SpecialVisualizations.ts @@ -125,7 +125,7 @@ class NearbyImageVis implements SpecialVisualization { tags: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): SvelteUIElement { const isOpen = args[0] === "open" const readonly = args[1] === "readonly" || args[1] === "yes" @@ -192,7 +192,7 @@ class StealViz implements SpecialVisualization { selectedElement: otherFeature, state, layer, - }) + }), ) } if (elements.length === 1) { @@ -200,8 +200,8 @@ class StealViz implements SpecialVisualization { } return new Combine(elements).SetClass("flex flex-col") }, - [state.indexedFeatures.featuresById] - ) + [state.indexedFeatures.featuresById], + ), ) } @@ -253,11 +253,11 @@ class CloseNoteViz implements SpecialVisualization { public constr( state: SpecialVisualizationState, tags: UIEventSource>, - args: string[] + args: string[], ): SvelteUIElement { const { text, icon, idkey, comment, minZoom, zoomButton } = Utils.ParseVisArgs( this.args, - args + args, ) return new SvelteUIElement(CloseNoteButton, { @@ -298,7 +298,7 @@ export class QuestionViz implements SpecialVisualization { tags: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): SvelteUIElement { const labels = args[0] ?.split(";") @@ -330,7 +330,7 @@ export default class SpecialVisualizations { for (const specialVisualization of SpecialVisualizations.specialVisualizations) { SpecialVisualizations.specialVisualisationsDict.set( specialVisualization.funcName, - specialVisualization + specialVisualization, ) } } @@ -350,15 +350,15 @@ export default class SpecialVisualizations { viz.docs, viz.args.length > 0 ? MarkdownUtils.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, "#### Example usage of " + viz.funcName, "" + example + "", @@ -367,18 +367,18 @@ export default class SpecialVisualizations { public static constructSpecification( template: string, - extraMappings: SpecialVisualization[] = [] + extraMappings: SpecialVisualization[] = [], ): RenderingSpecification[] { return SpecialVisualisationUtils.constructSpecification( template, SpecialVisualizations.specialVisualisationsDict, - extraMappings + extraMappings, ) } public static HelpMessage(): string { const helpTexts: string[] = SpecialVisualizations.specialVisualizations.map((viz) => - SpecialVisualizations.DocumentationFor(viz) + SpecialVisualizations.DocumentationFor(viz), ) const firstPart = new Combine([ @@ -411,10 +411,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") .AsMarkdown() @@ -452,10 +452,10 @@ export default class SpecialVisualizations { assignTo: state.userRelatedState.language, availableLanguages: languages, preferredLanguages: state.osmConnection.userDetails.map( - (ud) => ud.languages + (ud) => ud.languages, ), }) - }) + }), ) }, }, @@ -494,7 +494,7 @@ export default class SpecialVisualizations { state: SpecialVisualizationState, tagSource: UIEventSource>, args: string[], - feature: Feature + feature: Feature, ): SvelteUIElement { return new SvelteUIElement(MinimapViz, { state, args, feature, tagSource }) }, @@ -506,7 +506,7 @@ export default class SpecialVisualizations { constr( state: SpecialVisualizationState, - tagSource: UIEventSource> + tagSource: UIEventSource>, ): BaseUIElement { return new VariableUiElement( tagSource @@ -516,7 +516,7 @@ export default class SpecialVisualizations { return new SvelteUIElement(SplitRoadWizard, { id, state }) } return undefined - }) + }), ) }, }, @@ -530,7 +530,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { if (feature.geometry.type !== "Point") { return undefined @@ -553,7 +553,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { if (!layer.deletion) { return undefined @@ -579,7 +579,7 @@ export default class SpecialVisualizations { tags: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ) { if (feature.geometry.type !== "LineString") { return undefined @@ -611,7 +611,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, { @@ -674,7 +674,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( @@ -684,9 +684,9 @@ export default class SpecialVisualizations { } const response = e["success"] return Translation.fromMap(response.labels) - }) + }), ) - }) + }), ), }, new MapillaryLinkVis(), @@ -700,7 +700,7 @@ export default class SpecialVisualizations { tags: UIEventSource>, _, __, - layer: LayerConfig + layer: LayerConfig, ) => new SvelteUIElement(AllTagsPanel, { tags, layer }), }, { @@ -784,7 +784,7 @@ export default class SpecialVisualizations { nameKey: nameKey, fallbackName, }, - state.featureSwitchIsTesting + state.featureSwitchIsTesting, ) return new SvelteUIElement(StarsBarIcon, { score: reviews.average, @@ -818,7 +818,7 @@ export default class SpecialVisualizations { nameKey: nameKey, fallbackName, }, - state.featureSwitchIsTesting + state.featureSwitchIsTesting, ) return new SvelteUIElement(ReviewForm, { reviews, state, tags, feature, layer }) }, @@ -849,7 +849,7 @@ export default class SpecialVisualizations { nameKey: nameKey, fallbackName, }, - state.featureSwitchIsTesting + state.featureSwitchIsTesting, ) return new SvelteUIElement(AllReviews, { reviews, state, tags, feature, layer }) }, @@ -875,7 +875,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { return new Combine([ SpecialVisualizations.specialVisualisationsDict["create_review"].constr( @@ -883,14 +883,14 @@ export default class SpecialVisualizations { tagSource, args, feature, - layer + layer, ), SpecialVisualizations.specialVisualisationsDict["list_reviews"].constr( state, tagSource, args, feature, - layer + layer, ), ]) }, @@ -908,7 +908,7 @@ export default class SpecialVisualizations { constr( state: SpecialVisualizationState, _: UIEventSource>, - argument: string[] + argument: string[], ): BaseUIElement { const [text] = argument return new SvelteUIElement(ImportReviewIdentity, { state, text }) @@ -965,7 +965,7 @@ export default class SpecialVisualizations { constr( state: SpecialVisualizationState, tags: UIEventSource>, - args: string[] + args: string[], ): SvelteUIElement { const keyToUse = args[0] const prefix = args[1] @@ -1002,17 +1002,17 @@ export default class SpecialVisualizations { return undefined } const allUnits: Unit[] = [].concat( - ...(state?.theme?.layers?.map((lyr) => lyr.units) ?? []) + ...(state?.theme?.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) - }) + }), ) }, }, @@ -1066,7 +1066,7 @@ export default class SpecialVisualizations { constr: (state) => { return new SubtleButton( new SvelteUIElement(Trash), - Translations.t.general.removeLocationHistory + Translations.t.general.removeLocationHistory, ).onClick(() => { state.historicalUserLocations.features.setData([]) state.selectedElement.setData(undefined) @@ -1107,10 +1107,10 @@ export default class SpecialVisualizations { new SvelteUIElement(NoteCommentElement, { comment, state, - }) - ) + }), + ), ).SetClass("flex flex-col") - }) + }), ), }, { @@ -1143,7 +1143,7 @@ export default class SpecialVisualizations { tagsSource: UIEventSource>, _: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ) => new VariableUiElement( tagsSource.map((tags) => { @@ -1161,7 +1161,7 @@ export default class SpecialVisualizations { feature, layer, }) - }) + }), ), }, { @@ -1177,8 +1177,8 @@ export default class SpecialVisualizations { const challenge = Stores.FromPromise( Utils.downloadJsonCached( `${Maproulette.defaultEndpoint}/challenge/${parentId}`, - 24 * 60 * 60 * 1000 - ) + 24 * 60 * 60 * 1000, + ), ) return new VariableUiElement( @@ -1203,7 +1203,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.", @@ -1217,15 +1217,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" + @@ -1301,7 +1301,7 @@ export default class SpecialVisualizations { (l) => l.name !== null && l.title && - state.perLayer.get(l.id) !== undefined + state.perLayer.get(l.id) !== undefined, ) .map( (l) => { @@ -1311,8 +1311,8 @@ export default class SpecialVisualizations { const fsBboxed = new BBoxFeatureSourceForLayer(fs, bbox) return new StatisticsPanel(fsBboxed) }, - [state.mapProperties.bounds] - ) + [state.mapProperties.bounds], + ), ) }, }, @@ -1382,7 +1382,7 @@ export default class SpecialVisualizations { constr( state: SpecialVisualizationState, tagSource: UIEventSource>, - args: string[] + args: string[], ): SvelteUIElement { let [text, href, classnames, download, ariaLabel, icon] = args if (download === "") { @@ -1420,7 +1420,7 @@ export default class SpecialVisualizations { }, }, null, - " " + " ", ) + "\n```", args: [ @@ -1433,16 +1433,21 @@ export default class SpecialVisualizations { name: "tagrendering", doc: "An entire tagRenderingConfig", required: true, - } + }, + { + name: "classes", + doc: "CSS-classes to apply on every individual item. Seperated by `space`", + }, ], constr( state: SpecialVisualizationState, featureTags: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ) { - const [key, tr] = args + const [key, tr, classesRaw] = args + let classes = classesRaw ?? "" const translation = new Translation({ "*": tr }) return new VariableUiElement( featureTags.map((tags) => { @@ -1457,7 +1462,7 @@ export default class SpecialVisualizations { "Could not create a special visualization for multi(", args.join(", ") + ")", "no properties found for object", - feature.properties.id + feature.properties.id, ) return undefined } @@ -1469,11 +1474,12 @@ export default class SpecialVisualizations { state, feature, layer, + clss: classes ?? "", }) elements.push(subsTr) } return elements - }) + }), ) }, }, @@ -1493,7 +1499,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { return new VariableUiElement( tagSource.map((tags) => { @@ -1505,7 +1511,7 @@ export default class SpecialVisualizations { console.error("Cannot create a translation for", v, "due to", e) return JSON.stringify(v) } - }) + }), ) }, }, @@ -1525,7 +1531,7 @@ export default class SpecialVisualizations { tags: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { const key = argument[0] return new SvelteUIElement(FediverseLink, { key, tags, state }) @@ -1547,7 +1553,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { return new FixedUiElement("{" + args[0] + "}") }, @@ -1568,7 +1574,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { const key = argument[0] ?? "value" return new VariableUiElement( @@ -1586,12 +1592,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") } - }) + }), ) }, }, @@ -1612,7 +1618,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 }) @@ -1628,12 +1634,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, {}), @@ -1655,7 +1661,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { return new SvelteUIElement(MarkAsFavourite, { tags: tagSource, @@ -1675,7 +1681,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { return new SvelteUIElement(MarkAsFavouriteMini, { tags: tagSource, @@ -1695,7 +1701,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { return new SvelteUIElement(DirectionIndicator, { state, feature }) }, @@ -1708,7 +1714,7 @@ export default class SpecialVisualizations { state: SpecialVisualizationState, tags: UIEventSource>, argument: string[], - feature: Feature + feature: Feature, ): SvelteUIElement { return new SvelteUIElement(QrCode, { state, tags, feature }) }, @@ -1727,7 +1733,7 @@ export default class SpecialVisualizations { constr( state: SpecialVisualizationState, tagSource: UIEventSource>, - args: string[] + args: string[], ): BaseUIElement { const key = args[0] === "" ? "_direction:centerpoint" : args[0] return new VariableUiElement( @@ -1738,11 +1744,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] - }) + }), ) }, }, @@ -1772,7 +1778,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { const url = args[0] const readonly = args[3] === "yes" @@ -1798,12 +1804,12 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { return new Toggle( undefined, new SvelteUIElement(LoginButton, { osmConnection: state.osmConnection }), - state.osmConnection.isLoggedIn + state.osmConnection.isLoggedIn, ) }, }, @@ -1841,7 +1847,7 @@ export default class SpecialVisualizations { tags: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { const key = argument[0] ?? "website" const useProxy = argument[1] !== "no" @@ -1849,7 +1855,7 @@ export default class SpecialVisualizations { const isClosed = (argument[4] ?? "yes") === "yes" const countryStore: Store = tags.mapD( - (tags) => tags._country + (tags) => tags._country, ) const sourceUrl: Store = tags.mapD((tags) => { if (!tags[key] || tags[key] === "undefined") { @@ -1871,24 +1877,24 @@ export default class SpecialVisualizations { const features = await LinkedDataLoader.fetchVeloparkEntry( url, - loadAll + loadAll, ) const feature = features.find( - (f) => f.properties["ref:velopark"] === url + (f) => f.properties["ref:velopark"] === url, ) ?? features[0] const properties = feature.properties properties["ref:velopark"] = url console.log( "Got properties from velopark:", - properties + properties, ) return properties } catch (e) { console.error(e) throw e } - })() + })(), ) } if (country === undefined) { @@ -1900,29 +1906,29 @@ export default class SpecialVisualizations { return await LinkedDataLoader.fetchJsonLd( url, { country }, - useProxy ? "proxy" : "fetch-lod" + useProxy ? "proxy" : "fetch-lod", ) } catch (e) { console.log( "Could not get with proxy/download LOD, attempting to download directly. Error for ", url, "is", - e + e, ) return await LinkedDataLoader.fetchJsonLd( url, { country }, - "fetch-raw" + "fetch-raw", ) } - })() + })(), ) }, - [countryStore] + [countryStore], ) externalData.addCallbackAndRunD((lod) => - console.log("linked_data_from_website received the following data:", lod) + console.log("linked_data_from_website received the following data:", lod), ) return new Toggle( @@ -1937,7 +1943,7 @@ export default class SpecialVisualizations { collapsed: isClosed, }), undefined, - sourceUrl.map((url) => !!url) + sourceUrl.map((url) => !!url), ) }, }, @@ -1957,7 +1963,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { const text = argument[0] const cssClasses = argument[1] @@ -1979,7 +1985,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { const translation = tagSource.map((tags) => { const layer = state.theme.getMatchingLayer(tags) @@ -2011,7 +2017,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): SvelteUIElement { return new SvelteUIElement(ClearCaches, { msg: argument[0] ?? "Clear local caches", @@ -2036,7 +2042,7 @@ export default class SpecialVisualizations { tags: UIEventSource>, argument: string[], selectedElement: Feature, - layer: LayerConfig + layer: LayerConfig, ): SvelteUIElement { const [header, labelsStr] = argument const labels = labelsStr.split(";").map((x) => x.trim()) @@ -2059,7 +2065,7 @@ export default class SpecialVisualizations { tags: UIEventSource>, argument: string[], selectedElement: Feature, - layer: LayerConfig + layer: LayerConfig, ): SvelteUIElement { const t = Translations.t.preset_type const question: QuestionableTagRenderingConfigJson = { @@ -2099,7 +2105,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig + layer: LayerConfig, ): BaseUIElement { const text = argument[0] return new SubtleButton(undefined, text).onClick(() => { @@ -2130,7 +2136,7 @@ export default class SpecialVisualizations { "Invalid special visualisation found: funcName is undefined or doesn't match " + regex + 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 335906b48176ebff68bbee98a75a2021600efef9 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 2 Jan 2025 03:56:42 +0100 Subject: [PATCH 27/31] Refactoring: move 'GetDefaultIcon' into its own Svelte-class --- scripts/generateLayerOverview.ts | 2 +- src/Models/ThemeConfig/LayerConfig.ts | 102 ++++++++---------- .../ThemeConfig/PointRenderingConfig.ts | 9 -- src/UI/BigComponents/Filterview.svelte | 3 +- src/UI/History/History.svelte | 3 +- src/UI/Map/DefaultIcon.svelte | 27 +++++ src/UI/Map/DynamicMarker.svelte | 2 +- src/UI/Popup/DisabledQuestionsLayer.svelte | 3 +- .../TagRendering/TagRenderingMapping.svelte | 8 +- src/UI/Search/ActiveFilters.svelte | 5 +- src/UI/Search/FilterResult.svelte | 3 +- src/UI/Search/GeocodeResult.svelte | 5 +- src/UI/ThemeViewGUI.svelte | 5 +- 13 files changed, 97 insertions(+), 80 deletions(-) create mode 100644 src/UI/Map/DefaultIcon.svelte diff --git a/scripts/generateLayerOverview.ts b/scripts/generateLayerOverview.ts index b62a5e533..64c35df4c 100644 --- a/scripts/generateLayerOverview.ts +++ b/scripts/generateLayerOverview.ts @@ -119,7 +119,7 @@ class AddIconSummary extends DesugaringStep<{ raw: LayerConfigJson; parsed: Laye const pointRendering: PointRenderingConfig = layerConfig.mapRendering.find((pr) => pr.location.has("point") ) - const defaultTags = layerConfig.GetBaseTags() + const defaultTags = layerConfig.baseTags fixed["_layerIcon"] = Utils.NoNull( (pointRendering?.marker ?? []).map((i) => { const icon = i.icon?.GetRenderValue(defaultTags)?.txt diff --git a/src/Models/ThemeConfig/LayerConfig.ts b/src/Models/ThemeConfig/LayerConfig.ts index 60652478f..c5fea4525 100644 --- a/src/Models/ThemeConfig/LayerConfig.ts +++ b/src/Models/ThemeConfig/LayerConfig.ts @@ -24,6 +24,9 @@ import { QuestionableTagRenderingConfigJson } from "./Json/QuestionableTagRender import MarkdownUtils from "../../Utils/MarkdownUtils" import { And } from "../../Logic/Tags/And" import Combine from "../../UI/Base/Combine" +import SvelteUIElement from "../../UI/Base/SvelteUIElement" +import DynamicMarker from "../../UI/Map/DynamicMarker.svelte" +import { ImmutableStore } from "../../Logic/UIEventSource" export default class LayerConfig extends WithContextLoader { public static readonly syncSelectionAllowed = ["no", "local", "theme-only", "global"] as const @@ -67,6 +70,8 @@ export default class LayerConfig extends WithContextLoader { public readonly popupInFloatover: boolean | string public readonly enableMorePrivacy: boolean + public readonly baseTags: Readonly> + /** * If this layer is based on another layer, this might be indicated here * @private @@ -104,7 +109,7 @@ export default class LayerConfig extends WithContextLoader { mercatorCrs: json.source["mercatorCrs"], idKey: json.source["idKey"], }, - json.id + json.id, ) } @@ -124,7 +129,7 @@ export default class LayerConfig extends WithContextLoader { if (json.calculatedTags !== undefined) { if (!official) { console.warn( - `Unofficial theme ${this.id} with custom javascript! This is a security risk` + `Unofficial theme ${this.id} with custom javascript! This is a security risk`, ) } this.calculatedTags = [] @@ -194,7 +199,7 @@ export default class LayerConfig extends WithContextLoader { tags: pr.tags.map((t) => TagUtils.SimpleTag(t)), description: Translations.T( pr.description, - `${translationContext}.presets.${i}.description` + `${translationContext}.presets.${i}.description`, ), preciseInput: preciseInput, exampleImages: pr.exampleImages, @@ -208,7 +213,7 @@ export default class LayerConfig extends WithContextLoader { if (json.lineRendering) { this.lineRendering = Utils.NoNull(json.lineRendering).map( - (r, i) => new LineRenderingConfig(r, `${context}[${i}]`) + (r, i) => new LineRenderingConfig(r, `${context}[${i}]`), ) } else { this.lineRendering = [] @@ -216,7 +221,7 @@ export default class LayerConfig extends WithContextLoader { if (json.pointRendering) { this.mapRendering = Utils.NoNull(json.pointRendering).map( - (r, i) => new PointRenderingConfig(r, `${context}[${i}](${this.id})`) + (r, i) => new PointRenderingConfig(r, `${context}[${i}](${this.id})`), ) } else { this.mapRendering = [] @@ -228,7 +233,7 @@ export default class LayerConfig extends WithContextLoader { r.location.has("centroid") || r.location.has("projected_centerpoint") || r.location.has("start") || - r.location.has("end") + r.location.has("end"), ) if ( @@ -250,7 +255,7 @@ export default class LayerConfig extends WithContextLoader { Constants.priviliged_layers.indexOf(this.id) < 0 && this.source !== null /*library layer*/ && !this.source?.geojsonSource?.startsWith( - "https://api.openstreetmap.org/api/0.6/notes.json" + "https://api.openstreetmap.org/api/0.6/notes.json", ) ) { throw ( @@ -269,7 +274,7 @@ export default class LayerConfig extends WithContextLoader { typeof tr !== "string" && tr["builtin"] === undefined && tr["id"] === undefined && - tr["rewrite"] === undefined + tr["rewrite"] === undefined, ) ?? [] if (missingIds?.length > 0 && official) { console.error("Some tagRenderings of", this.id, "are missing an id:", missingIds) @@ -280,8 +285,8 @@ export default class LayerConfig extends WithContextLoader { (tr, i) => new TagRenderingConfig( tr, - this.id + ".tagRenderings[" + i + "]" - ) + this.id + ".tagRenderings[" + i + "]", + ), ) if (json.units !== undefined && !Array.isArray(json.units)) { throw ( @@ -291,7 +296,7 @@ export default class LayerConfig extends WithContextLoader { ) } this.units = (json.units ?? []).flatMap((unitJson, i) => - Unit.fromJson(unitJson, this.tagRenderings, `${context}.unit[${i}]`) + Unit.fromJson(unitJson, this.tagRenderings, `${context}.unit[${i}]`), ) if ( @@ -352,31 +357,18 @@ export default class LayerConfig extends WithContextLoader { ) } this.popupInFloatover = json.popupInFloatover ?? false - } - - public defaultIcon(properties?: Record): BaseUIElement | undefined { - if (this.mapRendering === undefined || this.mapRendering === null) { - return undefined - } - const mapRenderings = this.mapRendering.filter((r) => r.location.has("point")) - if (mapRenderings.length === 0) { - return undefined - } - return new Combine( - mapRenderings.map((mr) => - mr - .GetBaseIcon(properties ?? this.GetBaseTags()) - .SetClass("absolute left-0 top-0 w-full h-full") - ) - ).SetClass("relative block w-full h-full") - } - - public GetBaseTags(): Record { - return TagUtils.changeAsProperties( - this.source?.osmTags?.asChange({ id: "node/-1" }) ?? [{ k: "id", v: "node/-1" }] + this.baseTags = TagUtils.changeAsProperties( + this.source?.osmTags?.asChange({ id: "node/-1" }) ?? [{ k: "id", v: "node/-1" }], ) } + + public hasDefaultIcon() { + if (this.mapRendering === undefined || this.mapRendering === null) { + return false + } + return this.mapRendering.some((r) => r.location.has("point")) + } public GenerateDocumentation( usedInThemes: string[], layerIsNeededBy?: Map, @@ -386,7 +378,7 @@ export default class LayerConfig extends WithContextLoader { neededLayer: string }[] = [], addedByDefault = false, - canBeIncluded = true + canBeIncluded = true, ): string { const extraProps: string[] = [] extraProps.push("This layer is shown at zoomlevel **" + this.minzoom + "** and higher") @@ -394,32 +386,32 @@ export default class LayerConfig extends WithContextLoader { if (canBeIncluded) { if (addedByDefault) { extraProps.push( - "**This layer is included automatically in every theme. This layer might contain no points**" + "**This layer is included automatically in every theme. This layer might contain no points**", ) } if (this.shownByDefault === false) { extraProps.push( - "This layer is not visible by default and must be enabled in the filter by the user. " + "This layer is not visible by default and must be enabled in the filter by the user. ", ) } if (this.title === undefined) { extraProps.push( - "Elements don't have a title set and cannot be toggled nor will they show up in the dashboard. If you import this layer in your theme, override `title` to make this toggleable." + "Elements don't have a title set and cannot be toggled nor will they show up in the dashboard. If you import this layer in your theme, override `title` to make this toggleable.", ) } if (this.name === undefined && this.shownByDefault === false) { extraProps.push( - "This layer is not visible by default and the visibility cannot be toggled, effectively resulting in a fully hidden layer. This can be useful, e.g. to calculate some metatags. If you want to render this layer (e.g. for debugging), enable it by setting the URL-parameter layer-=true" + "This layer is not visible by default and the visibility cannot be toggled, effectively resulting in a fully hidden layer. This can be useful, e.g. to calculate some metatags. If you want to render this layer (e.g. for debugging), enable it by setting the URL-parameter layer-=true", ) } if (this.name === undefined) { extraProps.push( - "Not visible in the layer selection by default. If you want to make this layer toggable, override `name`" + "Not visible in the layer selection by default. If you want to make this layer toggable, override `name`", ) } if (this.mapRendering.length === 0) { extraProps.push( - "Not rendered on the map by default. If you want to rendering this on the map, override `mapRenderings`" + "Not rendered on the map by default. If you want to rendering this on the map, override `mapRenderings`", ) } @@ -429,12 +421,12 @@ export default class LayerConfig extends WithContextLoader { "", "This layer is loaded from an external source, namely ", "`" + this.source.geojsonSource + "`", - ].join("\n\n") + ].join("\n\n"), ) } } else { extraProps.push( - "This layer can **not** be included in a theme. It is solely used by [special renderings](SpecialRenderings.md) showing a minimap with custom data." + "This layer can **not** be included in a theme. It is solely used by [special renderings](SpecialRenderings.md) showing a minimap with custom data.", ) } @@ -444,7 +436,7 @@ export default class LayerConfig extends WithContextLoader { usingLayer = [ "## Themes using this layer", MarkdownUtils.list( - (usedInThemes ?? []).map((id) => `[${id}](https://mapcomplete.org/${id})`) + (usedInThemes ?? []).map((id) => `[${id}](https://mapcomplete.org/${id})`), ), ] } else if (this.source !== null) { @@ -460,7 +452,7 @@ export default class LayerConfig extends WithContextLoader { " into the layout as it depends on it: ", dep.reason, "(" + dep.context + ")", - ].join(" ") + ].join(" "), ) } @@ -487,7 +479,7 @@ export default class LayerConfig extends WithContextLoader { new And(preset.tags).asHumanString(true) + snaps ) - }) + }), ), ] } @@ -495,8 +487,8 @@ export default class LayerConfig extends WithContextLoader { for (const revDep of Utils.Dedup(layerIsNeededBy?.get(this.id) ?? [])) { extraProps.push( ["This layer is needed as dependency for layer", `[${revDep}](#${revDep})`].join( - " " - ) + " ", + ), ) } @@ -507,10 +499,10 @@ export default class LayerConfig extends WithContextLoader { .filter((values) => values.key !== "id") .map((values) => { const embedded: string[] = values.values?.map((v) => - Link.OsmWiki(values.key, v, true).SetClass("mr-2").AsMarkdown() + Link.OsmWiki(values.key, v, true).SetClass("mr-2").AsMarkdown(), ) ?? ["_no preset options defined, or no values in them_"] const statistics = `https://taghistory.raifer.tech/?#***/${encodeURIComponent( - values.key + values.key, )}/` const tagInfo = `https://taginfo.openstreetmap.org/keys/${values.key}#values` return [ @@ -525,7 +517,7 @@ export default class LayerConfig extends WithContextLoader { : `[${values.type}](../SpecialInputElements.md#${values.type})`, embedded.join(" "), ] - }) + }), ) let quickOverview: string[] = [] @@ -535,7 +527,7 @@ export default class LayerConfig extends WithContextLoader { "this quick overview is incomplete", MarkdownUtils.table( ["attribute", "type", "values which are supported by this layer"], - tableRows + tableRows, ), ] } @@ -569,19 +561,19 @@ export default class LayerConfig extends WithContextLoader { const parts = neededTags["and"] tagsDescription.push( "Elements must match **all** of the following expressions:", - parts.map((p, i) => i + ". " + p.asHumanString(true, false, {})).join("\n") + parts.map((p, i) => i + ". " + p.asHumanString(true, false, {})).join("\n"), ) } else if (neededTags["or"]) { const parts = neededTags["or"] tagsDescription.push( "Elements must match **any** of the following expressions:", - parts.map((p) => " - " + p.asHumanString(true, false, {})).join("\n") + parts.map((p) => " - " + p.asHumanString(true, false, {})).join("\n"), ) } else { tagsDescription.push( "Elements must match the expression **" + - neededTags.asHumanString(true, false, {}) + - "**" + neededTags.asHumanString(true, false, {}) + + "**", ) } diff --git a/src/Models/ThemeConfig/PointRenderingConfig.ts b/src/Models/ThemeConfig/PointRenderingConfig.ts index 8bae5f296..4c74a4843 100644 --- a/src/Models/ThemeConfig/PointRenderingConfig.ts +++ b/src/Models/ThemeConfig/PointRenderingConfig.ts @@ -143,15 +143,6 @@ export default class PointRenderingConfig extends WithContextLoader { "w-full h-full block absolute top-0 left-0" ) } - - public GetBaseIcon(tags?: Record): BaseUIElement { - return new SvelteUIElement(DynamicMarker, { - marker: this.marker, - rotation: this.rotation, - tags: new ImmutableStore(tags), - }) - } - public RenderIcon( tags: Store>, options?: { diff --git a/src/UI/BigComponents/Filterview.svelte b/src/UI/BigComponents/Filterview.svelte index a59962d5f..aa3bdec8b 100644 --- a/src/UI/BigComponents/Filterview.svelte +++ b/src/UI/BigComponents/Filterview.svelte @@ -14,6 +14,7 @@ import Translations from "../i18n/Translations" import type { SpecialVisualizationState } from "../SpecialVisualization" import Constants from "../../Models/Constants" + import DefaultIcon from "../Map/DefaultIcon.svelte" export let state: SpecialVisualizationState export let filteredLayer: FilteredLayer @@ -58,7 +59,7 @@ {#if showLayerTitle}
- layer.defaultIcon()} /> +
diff --git a/src/UI/History/History.svelte b/src/UI/History/History.svelte index 66792cefc..065822d08 100644 --- a/src/UI/History/History.svelte +++ b/src/UI/History/History.svelte @@ -10,6 +10,7 @@ import ToSvelte from "../Base/ToSvelte.svelte" import Tr from "../Base/Tr.svelte" import Translations from "../i18n/Translations" + import DefaultIcon from "../Map/DefaultIcon.svelte" export let onlyShowChangesBy: string[] export let id: OsmId @@ -58,7 +59,7 @@

- +

+ + + import LayerConfig from "../../Models/ThemeConfig/LayerConfig" + import DynamicIcon from "./DynamicIcon.svelte" + import DynamicMarker from "./DynamicMarker.svelte" + import Marker from "./Marker.svelte" + import { ImmutableStore } from "../../Logic/UIEventSource" + + /** + * The 'DefaultIcon' is the icon that a layer shows by default + * Used e.g. in the filterview + */ + export let layer: LayerConfig + export let properties: Readonly> = layer.baseTags + export let clss= "" + let tags = new ImmutableStore(properties) + let mapRenderings = layer.mapRendering?.filter((r) => r.location.has("point")) + + +{#if mapRenderings?.length > 0} +
+ {#each mapRenderings as mr} + + {/each} +
+{/if} diff --git a/src/UI/Map/DynamicMarker.svelte b/src/UI/Map/DynamicMarker.svelte index 2f6b1cb90..bc88a2291 100644 --- a/src/UI/Map/DynamicMarker.svelte +++ b/src/UI/Map/DynamicMarker.svelte @@ -24,7 +24,7 @@ } -{#if marker && marker} +{#if marker}
{#each marker as icon}
diff --git a/src/UI/Popup/DisabledQuestionsLayer.svelte b/src/UI/Popup/DisabledQuestionsLayer.svelte index 6460f2fac..8396e5843 100644 --- a/src/UI/Popup/DisabledQuestionsLayer.svelte +++ b/src/UI/Popup/DisabledQuestionsLayer.svelte @@ -8,6 +8,7 @@ import { Translation } from "../i18n/Translation" import { XMarkIcon } from "@babeard/svelte-heroicons/mini" import ToSvelte from "../Base/ToSvelte.svelte" + import DefaultIcon from "../Map/DefaultIcon.svelte" export let layer: LayerConfig export let state: ThemeViewState @@ -28,7 +29,7 @@

- layer.defaultIcon()} /> +

diff --git a/src/UI/Popup/TagRendering/TagRenderingMapping.svelte b/src/UI/Popup/TagRendering/TagRenderingMapping.svelte index 73413b0e9..f28b8633b 100644 --- a/src/UI/Popup/TagRendering/TagRenderingMapping.svelte +++ b/src/UI/Popup/TagRendering/TagRenderingMapping.svelte @@ -15,6 +15,7 @@ import SvelteUIElement from "../../Base/SvelteUIElement" import Icon from "../../Map/Icon.svelte" import { TagsFilter } from "../../../Logic/Tags/TagsFilter" + import DefaultIcon from "../../Map/DefaultIcon.svelte" export let selectedElement: Feature export let tags: UIEventSource> @@ -27,6 +28,7 @@ */ export let clss: string = "ml-2" export let mapping: { + readonly if?: TagsFilter readonly then: Translation readonly searchTerms?: Record readonly icon?: string @@ -46,13 +48,13 @@ large: "5rem", } - function getAutoIcon(mapping: { if?: TagsFilter }): BaseUIElement { + function getAutoIcon(mapping: { readonly if?: TagsFilter }): Readonly> { for (const preset of layer.presets) { if (!new And(preset.tags).shadows(mapping.if)) { continue } - return layer.defaultIcon(TagUtils.asProperties(preset.tags)) + return TagUtils.asProperties(preset.tags) } return undefined } @@ -62,7 +64,7 @@
{#if mapping.icon === "auto"}
- getAutoIcon(mapping)} /> +
{:else} enableAllLayers()}>
- +
@@ -82,7 +83,7 @@ {#each $nonactiveLayers as nonActive (nonActive.layerDef.id)} nonActive.isDisplayed.set(true)}>
- +
diff --git a/src/UI/Search/FilterResult.svelte b/src/UI/Search/FilterResult.svelte index e19a21339..0ea0e07d6 100644 --- a/src/UI/Search/FilterResult.svelte +++ b/src/UI/Search/FilterResult.svelte @@ -6,6 +6,7 @@ import type { FilterSearchResult } from "../../Logic/Search/FilterSearch" import LayerConfig from "../../Models/ThemeConfig/LayerConfig" import Loading from "../Base/Loading.svelte" + import DefaultIcon from "../Map/DefaultIcon.svelte" export let entry: FilterSearchResult[] | LayerConfig let asFilter: FilterSearchResult[] @@ -41,7 +42,7 @@
{#if asLayer}
- +
diff --git a/src/UI/Search/GeocodeResult.svelte b/src/UI/Search/GeocodeResult.svelte index 2f3f6fc6f..3562ab02a 100644 --- a/src/UI/Search/GeocodeResult.svelte +++ b/src/UI/Search/GeocodeResult.svelte @@ -12,6 +12,7 @@ import Icon from "../Map/Icon.svelte" import TagRenderingAnswer from "../Popup/TagRendering/TagRenderingAnswer.svelte" import ArrowUp from "@babeard/svelte-heroicons/mini/ArrowUp" + import DefaultIcon from "../Map/DefaultIcon.svelte" export let entry: GeocodeResult export let state: SpecialVisualizationState @@ -62,9 +63,7 @@
{#if layer}
- layer.defaultIcon(entry.feature.properties)?.SetClass("w-6 h-6")} - /> +
{:else if entry.category} - {#if currentViewLayer?.tagRenderings && currentViewLayer.defaultIcon()} + {#if currentViewLayer?.tagRenderings && currentViewLayer.hasDefaultIcon()} { state.selectCurrentView() @@ -403,7 +404,7 @@ on:keydown={forwardEventToMap} >
- currentViewLayer.defaultIcon()} /> +
{/if} From 312a32e7b5e7dc578b47bc0fbfd76083a50084ff Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 2 Jan 2025 04:30:18 +0100 Subject: [PATCH 28/31] =?UTF-8?q?Performance:=20only=20ping=20'allPreferen?= =?UTF-8?q?ces'=20when=20all=20preferences=20are=20set,=20not=20on=20every?= =?UTF-8?q?=20preference=20added=20(which=20causes=20a=20O(n=C2=B2)=20in?= =?UTF-8?q?=20some=20cases)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Logic/Osm/OsmPreferences.ts | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/Logic/Osm/OsmPreferences.ts b/src/Logic/Osm/OsmPreferences.ts index 4b30c4212..6c4a9d554 100644 --- a/src/Logic/Osm/OsmPreferences.ts +++ b/src/Logic/Osm/OsmPreferences.ts @@ -38,14 +38,23 @@ export class OsmPreferences { }) } - private setPreferencesAll(key: string, value: string) { + /** + * Sets a new preferenceValue in 'allPreferences' + * @param key + * @param value + * @param deferping: the end user will ping '_allPreferences' + * @private + */ + private setPreferencesAll(key: string, value: string, deferping = false) { if (this._allPreferences.data[key] !== value) { this._allPreferences.data[key] = value - this._allPreferences.ping() + if(!deferping){ + this._allPreferences.ping() + } } } - private initPreference(key: string, value: string = undefined): UIEventSource { + private initPreference(key: string, value: string = undefined, deferPing = false): UIEventSource { if (this.preferences[key] !== undefined) { if (value !== undefined) { this.preferences[key].set(value) @@ -54,12 +63,12 @@ export class OsmPreferences { } const pref = (this.preferences[key] = new UIEventSource(value, "preference: " + key)) if (value) { - this.setPreferencesAll(key, value) + this.setPreferencesAll(key, value, deferPing) } pref.addCallback((v) => { console.log("Got an update:", key, "--->", v) this.uploadKvSplit(key, v) - this.setPreferencesAll(key, v) + this.setPreferencesAll(key, v, deferPing) }) return pref } @@ -73,11 +82,12 @@ export class OsmPreferences { await this.removeLegacy(legacy) } for (const key in merged) { - this.initPreference(key, prefs[key]) + this.initPreference(key, prefs[key], true) } for (const key in legacy) { - this.initPreference(key, legacy[key]) + this.initPreference(key, legacy[key], true) } + this._allPreferences.ping() } public getPreference(key: string, defaultValue: string = undefined, prefix?: string) { From 1cc76e3b5da46699def9e1154e3ab2f9cc0e8bfe Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 2 Jan 2025 04:30:26 +0100 Subject: [PATCH 29/31] Chore: remove stray token --- src/Logic/Osm/OsmConnection.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Logic/Osm/OsmConnection.ts b/src/Logic/Osm/OsmConnection.ts index 97cc7a616..e59678b64 100644 --- a/src/Logic/Osm/OsmConnection.ts +++ b/src/Logic/Osm/OsmConnection.ts @@ -503,7 +503,7 @@ export class OsmConnection { (options.filename ?? "gpx_track_mapcomplete_" + new Date().toISOString()) + '"\r\nContent-Type: application/gpx+xml', } - user + const boundary = "987654" let body = "" From a089a5e1d9edcc3fe7dfed5840816863c6ed9e0d Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 2 Jan 2025 04:45:03 +0100 Subject: [PATCH 30/31] Performance: optimize asset --- assets/svg/brick_wall_round.svg | 994 +++---------------------------- assets/svg/brick_wall_square.svg | 946 ++--------------------------- 2 files changed, 136 insertions(+), 1804 deletions(-) diff --git a/assets/svg/brick_wall_round.svg b/assets/svg/brick_wall_round.svg index f4be352bb..515407bc7 100644 --- a/assets/svg/brick_wall_round.svg +++ b/assets/svg/brick_wall_round.svg @@ -7,920 +7,100 @@ viewBox="0 0 40 40" version="1.1" id="svg5" - inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" - sodipodi:docname="bick_wal_round.svg" + sodipodi:docname="brick_wall_round.svg" + inkscape:version="1.3.2 (1:1.3.2+202311252150+091e20ef0f)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"> - - - - - - - - - - + id="defs1"> + + + + - + id="mask47"> + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + diff --git a/assets/svg/brick_wall_square.svg b/assets/svg/brick_wall_square.svg index 9e9fc293f..6d99feb96 100644 --- a/assets/svg/brick_wall_square.svg +++ b/assets/svg/brick_wall_square.svg @@ -7,90 +7,36 @@ viewBox="0 0 40 40" version="1.1" id="svg5" - inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" - sodipodi:docname="bick_wal_square.svg" + sodipodi:docname="brick_wall_square.svg" + inkscape:version="1.3.2 (1:1.3.2+202311252150+091e20ef0f)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"> + - - - - - - - - - - - - - - + inkscape:current-layer="svg5" /> + style="display:inline"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + From 9c686fed89fb3ee8a3de21706aee37fa17ac3205 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 2 Jan 2025 05:34:38 +0100 Subject: [PATCH 31/31] Fix: fix special visualisations --- src/UI/Base/ToSvelte.svelte | 2 +- src/UI/SpecialVisualizations.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/UI/Base/ToSvelte.svelte b/src/UI/Base/ToSvelte.svelte index 2828ef42c..059dd97ea 100644 --- a/src/UI/Base/ToSvelte.svelte +++ b/src/UI/Base/ToSvelte.svelte @@ -43,6 +43,6 @@ {:else} {/if} -{:else if elem !== undefined} +{:else} {/if} diff --git a/src/UI/SpecialVisualizations.ts b/src/UI/SpecialVisualizations.ts index 1e3a37fa9..3a874c6bf 100644 --- a/src/UI/SpecialVisualizations.ts +++ b/src/UI/SpecialVisualizations.ts @@ -1474,8 +1474,8 @@ export default class SpecialVisualizations { state, feature, layer, - clss: classes ?? "", - }) + // clss: classes ?? "", + }) .SetClass(classes) elements.push(subsTr) } return elements
- - -
- +
+
+
Remove