diff --git a/assets/layers/bench/bench.json b/assets/layers/bench/bench.json index 673e83ebc..8c1a138c3 100644 --- a/assets/layers/bench/bench.json +++ b/assets/layers/bench/bench.json @@ -1092,9 +1092,9 @@ "id": "bench-view", "render": { "special": { - "type": "group", "header": "bench-view-title", - "labels": "bench-view-content" + "labels": "bench-view-content", + "type": "group" } } }, @@ -1116,12 +1116,13 @@ ], "render": { "special": { - "type": "image_carousel", - "image_key": "image:view;panoramax:view;mapillary:view" + "image_key": "image:view;panoramax:view;mapillary:view", + "type": "image_carousel" } } }, - { "id": "bench-view-upload", + { + "id": "bench-view-upload", "labels": [ "hidden", "bench-view-content" diff --git a/assets/layers/icons/icons.json b/assets/layers/icons/icons.json index 93e9e91ec..54ebd45d5 100644 --- a/assets/layers/icons/icons.json +++ b/assets/layers/icons/icons.json @@ -430,10 +430,10 @@ } }, { - "condition": "_favourite=yes", + "id": "favourite_icon", "description": "Only for rendering", "icon": "circle:white;heart:red", - "id": "favourite_icon", + "condition": "_favourite=yes", "metacondition": "__showTimeSensitiveIcons!=no" }, { diff --git a/assets/layers/last_click/last_click.json b/assets/layers/last_click/last_click.json index 6aaa19115..3185c513f 100644 --- a/assets/layers/last_click/last_click.json +++ b/assets/layers/last_click/last_click.json @@ -217,8 +217,8 @@ }, { "id": "debug", - "metacondition": "__featureSwitchIsDebugging=true", - "render": "{all_tags()}" + "render": "{all_tags()}", + "metacondition": "__featureSwitchIsDebugging=true" } ], "filter": [ diff --git a/assets/layers/note/note.json b/assets/layers/note/note.json index 9207f3fe4..63117633b 100644 --- a/assets/layers/note/note.json +++ b/assets/layers/note/note.json @@ -114,9 +114,9 @@ "lineRendering": [], "tagRenderings": [ { - "classes": "p-0", "id": "conversation", - "render": "{visualize_note_comments()}" + "render": "{visualize_note_comments()}", + "classes": "p-0" }, { "id": "add_image", diff --git a/assets/layers/osm_community_index/osm_community_index.json b/assets/layers/osm_community_index/osm_community_index.json index 150fc6ecd..1c605ca38 100644 --- a/assets/layers/osm_community_index/osm_community_index.json +++ b/assets/layers/osm_community_index/osm_community_index.json @@ -66,16 +66,16 @@ ], "tagRenderings": [ { - "condition": "level=country", - "description": "The name of the country", "id": "country_name", - "render": "{nameEn} {emojiFlag}" + "description": "The name of the country", + "render": "{nameEn} {emojiFlag}", + "condition": "level=country" }, { - "condition": "_community_links~*", - "description": "Community Links (Discord, meetups, Slack groups, IRC channels, mailing lists etc...)", "id": "community_links", - "render": "{_community_links}" + "description": "Community Links (Discord, meetups, Slack groups, IRC channels, mailing lists etc...)", + "render": "{_community_links}", + "condition": "_community_links~*" } ], "filter": [ diff --git a/assets/layers/questions/questions.json b/assets/layers/questions/questions.json index fd335cb14..624cc54d4 100644 --- a/assets/layers/questions/questions.json +++ b/assets/layers/questions/questions.json @@ -3234,7 +3234,7 @@ "freeform": { "key": "maxstay", "type": "pfloat", - "unit": { + "unit": { "quantity": "duration", "denominations": [ "minutes", diff --git a/assets/themes/benches/benches.json b/assets/themes/benches/benches.json index 3567cdf2e..66026a4fd 100644 --- a/assets/themes/benches/benches.json +++ b/assets/themes/benches/benches.json @@ -93,7 +93,6 @@ "geoJsonZoomLevel": 12, "#": "The 'staging'-url is a test maproulette", "#geoJson": "https://staging.maproulette.org/api/v2/challenge/view/70?bbox={x_min},{y_max},{x_max},{y_min}", - "geoJson": "https://maproulette.org/api/v2/challenge/view/53358?bbox={x_min},{y_max},{x_max},{y_min}" }, "calculatedTags": [ @@ -140,9 +139,9 @@ "condition": "_osm_poi_with_this_ref!=[]", "render": { "special": { - "type": "multi", "key": "_osm_poi_with_this_ref", - "tagrendering": "The OSM-bench {id} is linked to this OpenBenches-datapoint" + "tagrendering": "The OSM-bench {id} is linked to this OpenBenches-datapoint", + "type": "multi" } } }, diff --git a/assets/themes/mapcomplete-changes/mapcomplete-changes.json b/assets/themes/mapcomplete-changes/mapcomplete-changes.json index 903e1b204..ca9e30cf2 100644 --- a/assets/themes/mapcomplete-changes/mapcomplete-changes.json +++ b/assets/themes/mapcomplete-changes/mapcomplete-changes.json @@ -38,9 +38,6 @@ "zh_Hant": "顯示由MapComplete進行的變動" }, "icon": "./assets/svg/logo.svg", - "startZoom": 1, - "startLat": 0, - "startLon": 0, "hideFromOverview": true, "layers": [ { @@ -429,6 +426,10 @@ "if": "theme=facadegardens", "then": "./assets/themes/facadegardens/geveltuin.svg" }, + { + "if": "theme=farming_equipment_cooperatives", + "then": "./assets/layers/farming_equipment_cooperative/tractor.svg" + }, { "if": "theme=fireplace", "then": "./assets/layers/assembly_point/fire.svg" diff --git a/langs/layers/en.json b/langs/layers/en.json index d22c4592d..0598736dc 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -1339,6 +1339,16 @@ }, "question": "When was this bench last surveyed?", "render": "This bench was last surveyed on {survey:date}" + }, + "bench-view-title": { + "render": "View from the bench" + }, + "bench-view-upload": { + "render": { + "special": { + "label": "Add a picture of the view when sitting on the bench" + } + } } }, "title": { @@ -3466,6 +3476,37 @@ }, "climbing_gym": { "description": "A climbing gym", + "filter": { + "0": { + "options": { + "0": { + "question": "climbing style" + }, + "1": { + "question": "Bouldering" + }, + "2": { + "question": "Top rope climbing" + }, + "3": { + "question": "Sport climbing" + } + } + }, + "1": { + "options": { + "0": { + "question": "auto belays" + }, + "1": { + "question": "auto belays for top roping" + }, + "2": { + "question": "auto belays for lead climbing" + } + } + } + }, "name": "Climbing gyms", "presets": { "0": { @@ -5688,6 +5729,315 @@ "render": "Extinguishers" } }, + "farming_equipment_cooperative": { + "description": "This layer is to map cooperative for the use of agricultural equipment", + "name": "Cooperatives for the use of agricultural equipment", + "presets": { + "0": { + "title": "a farming equipment cooperative" + } + }, + "tagRenderings": { + "cuma_building": { + "mappings": { + "0": { + "then": "This cooperative owns an office building" + }, + "1": { + "then": "This cooperative owns a building" + }, + "2": { + "then": "This cooperative does not own a building" + } + }, + "question": "Has this cooperative a building?" + }, + "cuma_equipment": { + "mappings": { + "0": { + "then": "Plastic basins for various uses, e.g. as containers while sorting produce, holding fluids, ..." + }, + "1": { + "then": "Cooking kettle" + }, + "10": { + "then": "Motor driven equipment" + }, + "11": { + "then": "Peelers" + }, + "12": { + "then": "Plough" + }, + "13": { + "then": "Presses" + }, + "14": { + "then": "Mobile pumps" + }, + "15": { + "then": "Rice parboiling kit" + }, + "16": { + "then": "Roller" + }, + "17": { + "then": "Seed drill" + }, + "18": { + "then": "Sewing machine " + }, + "19": { + "then": "Shea churn" + }, + "2": { + "then": "Cultivator (or rotavator)" + }, + "20": { + "then": "Shea complex" + }, + "21": { + "then": "Small, manually powered equipment" + }, + "22": { + "then": "Mobile solar panel(s) (which is moved around as necessary)" + }, + "23": { + "then": "Mower" + }, + "24": { + "then": "Soybean processing unit" + }, + "25": { + "then": "Manure spreader" + }, + "26": { + "then": "Storage store" + }, + "27": { + "then": "Tarpaulin" + }, + "28": { + "then": "Threshing machine" + }, + "29": { + "then": "Tiller" + }, + "3": { + "then": "Dethatcher" + }, + "30": { + "then": "Various pieces of tilling equipment" + }, + "31": { + "then": "Tractor" + }, + "32": { + "then": "Trailer" + }, + "33": { + "then": "Tricycle" + }, + "34": { + "then": "Manages a shared, unmovable water tank - possibly at a different location" + }, + "35": { + "then": "Manages a shared water well - possibly at a different location" + }, + "4": { + "then": "Electric rice incubator" + }, + "5": { + "then": "A horse drawn cart" + }, + "6": { + "then": "Huller machine" + }, + "7": { + "then": "Millet mill (to grind flour)" + }, + "8": { + "then": "Millet huller" + }, + "9": { + "then": "Moisture meter" + } + }, + "multiTitle": "This cooperative manages the following equipment:", + "question": "What equipment does this cooperative manage that a member of the cooperative can loan to use at their farm?", + "render": "{equipment}" + }, + "cuma_type": { + "mappings": { + "0": { + "then": "This cooperation has equipment for processing" + }, + "1": { + "then": "This cooperation has equipment for horticulture" + }, + "2": { + "then": "Thils cooperation has equipment for tillage" + }, + "3": { + "then": "This cooperation has equipment for breeding" + } + }, + "question": "What category of agricultural cooperative is this?", + "render": "has equipment for {cooperative:agricultural}" + }, + "produces": { + "mappings": { + "0": { + "then": "Amaranth" + }, + "1": { + "then": "Arachide" + }, + "10": { + "then": "Corn" + }, + "11": { + "then": "Cotton" + }, + "12": { + "then": "Cowpea" + }, + "13": { + "then": "Crincrin" + }, + "14": { + "then": "Cucumber" + }, + "15": { + "then": "Eggplant" + }, + "16": { + "then": "Fonio" + }, + "17": { + "then": "Fruits" + }, + "18": { + "then": "Gboma" + }, + "19": { + "then": "Green bean" + }, + "2": { + "then": "Banana" + }, + "20": { + "then": "Hibiscus" + }, + "21": { + "then": "Lemon" + }, + "22": { + "then": "Lemongrass" + }, + "23": { + "then": "Lettuce" + }, + "24": { + "then": "Mango" + }, + "25": { + "then": "Nightshade" + }, + "26": { + "then": "Okra" + }, + "27": { + "then": "Onion" + }, + "28": { + "then": "Orange" + }, + "29": { + "then": "Papaya" + }, + "3": { + "then": "Beans" + }, + "30": { + "then": "Palm nut" + }, + "31": { + "then": "Palm oil" + }, + "32": { + "then": "Parsley" + }, + "33": { + "then": "Peanut" + }, + "34": { + "then": "Peas" + }, + "35": { + "then": "Pawpaw" + }, + "36": { + "then": "Rice" + }, + "37": { + "then": "Sesame" + }, + "38": { + "then": "Shea" + }, + "39": { + "then": "Sorghum" + }, + "4": { + "then": "Bell peppers" + }, + "40": { + "then": "Soya" + }, + "41": { + "then": "Tomatoes" + }, + "42": { + "then": "Vegetables" + }, + "43": { + "then": "Watermelon" + }, + "44": { + "then": "Yam" + }, + "5": { + "then": "Cabbage" + }, + "6": { + "then": "Carrots" + }, + "7": { + "then": "Cassava" + }, + "8": { + "then": "Cashew nut" + }, + "9": { + "then": "Chili" + } + }, + "multiTitle": "This cooperative has equipment for crops as:", + "question": "What kinds of crops does this cooperative provide equipment for?" + }, + "short_name": { + "render": "The short name is {short_name}" + } + }, + "title": { + "mappings": { + "0": { + "then": "Cooperative for the use of agricultural equipment (CUMA)" + } + } + } + }, "filters": { "filter": { "0": { @@ -7683,6 +8033,13 @@ } } }, + "mark_not_too_hard": { + "render": { + "special": { + "message": "This challenge was marked as too hard. Try anyway?" + } + } + }, "mark_too_hard": { "render": { "special": { @@ -8573,6 +8930,29 @@ } }, "tagRenderings": { + "access": { + "mappings": { + "0": { + "then": "This parking can be used by anyone" + }, + "1": { + "then": "This parking can't be used by anyone" + }, + "2": { + "then": "This parking is private" + }, + "3": { + "then": "This parking can be used by anyone, but the owner can revoke access at any time" + }, + "4": { + "then": "This parking can only be used by customers" + }, + "5": { + "then": "This parking can only be used by authorized persons" + } + }, + "question": "Who is allowed to use this parking?" + }, "capacity": { "freeform": { "placeholder": "Amount of parking spots" @@ -8598,6 +8978,18 @@ "question": "How many disabled parking spots are there at this parking?", "render": "There are {capacity:disabled} disabled parking spots" }, + "fee": { + "mappings": { + "0": { + "then": "The parking is free of charge" + }, + "1": { + "then": "There is a fee" + } + }, + "question": "Is there a fee?", + "render": "A fee of {charge} should be paid for parking here" + }, "parking-type": { "mappings": { "0": { diff --git a/langs/layers/fr.json b/langs/layers/fr.json index 4956d0a89..20784b20b 100644 --- a/langs/layers/fr.json +++ b/langs/layers/fr.json @@ -3734,6 +3734,271 @@ "render": "Exctincteurs" } }, + "farming_equipment_cooperative": { + "description": "Cette couche est destinée à cartographier des coopératives d'utilisation de matériel agricole.", + "name": "Coopératives d'utilisation de matériel agricole (CUMA)", + "tagRenderings": { + "cuma_equipment": { + "mappings": { + "0": { + "then": "Bassines manuelles" + }, + "1": { + "then": "Chaudière de cuisson" + }, + "10": { + "then": "équipement motorisé" + }, + "11": { + "then": "éplucheuses" + }, + "12": { + "then": "Charrue" + }, + "13": { + "then": "Presses" + }, + "14": { + "then": "Pompes" + }, + "15": { + "then": "Précuisson du riz" + }, + "16": { + "then": "Rouleau" + }, + "18": { + "then": "Machine à coudre" + }, + "19": { + "then": "Baratte de karité" + }, + "2": { + "then": "Cultivateur" + }, + "20": { + "then": "Complexe karité" + }, + "21": { + "then": "manuel petit format" + }, + "22": { + "then": "Panneau solaire" + }, + "24": { + "then": "Unité de transformation de soja" + }, + "25": { + "then": "épandage" + }, + "26": { + "then": "Entrepôt de stockage" + }, + "27": { + "then": "Bâche" + }, + "28": { + "then": "Machine à battre" + }, + "29": { + "then": "Motoculteur" + }, + "3": { + "then": "Scarificateur" + }, + "30": { + "then": "Autres outils de labour" + }, + "31": { + "then": "Tracteur" + }, + "32": { + "then": "Remorque" + }, + "33": { + "then": "Tricycle" + }, + "34": { + "then": "Réservoir d'eau" + }, + "35": { + "then": "puits" + }, + "4": { + "then": "Incubateur électrique" + }, + "6": { + "then": "Décortiqueuse" + }, + "9": { + "then": "Humidimètre" + } + }, + "question": "Équipements de la CUMA" + }, + "cuma_type": { + "mappings": { + "0": { + "then": "Type de CUMA : transformation" + }, + "1": { + "then": "Type de CUMA : maraîchage" + }, + "2": { + "then": "Type de CUMA : travail du sol" + }, + "3": { + "then": " Type de CUMA : élevage " + } + }, + "question": "Type de CUMA", + "render": "Type de CUMA : {cooperative:agricultural}" + }, + "produces": { + "mappings": { + "0": { + "then": "Amarante" + }, + "1": { + "then": "Arachide" + }, + "10": { + "then": "Maïs" + }, + "11": { + "then": "Coton" + }, + "12": { + "then": "Niébé" + }, + "13": { + "then": "Crincrin" + }, + "14": { + "then": "Concombre" + }, + "15": { + "then": "Aubergine" + }, + "16": { + "then": "Fonio" + }, + "17": { + "then": "Fruits" + }, + "18": { + "then": "Gboma" + }, + "19": { + "then": "Haricot vert" + }, + "2": { + "then": "Banane" + }, + "20": { + "then": "Hibiscus" + }, + "21": { + "then": "Citron" + }, + "22": { + "then": "Citronnelle" + }, + "23": { + "then": "Laitue" + }, + "24": { + "then": "Mangue" + }, + "25": { + "then": "Morelle" + }, + "26": { + "then": "Gombo" + }, + "27": { + "then": "Oignon" + }, + "28": { + "then": "Orange" + }, + "29": { + "then": "Papaye" + }, + "3": { + "then": "Haricots" + }, + "30": { + "then": "Noix de palme" + }, + "31": { + "then": "Huile de palme" + }, + "32": { + "then": "Persil" + }, + "33": { + "then": "Arachide" + }, + "34": { + "then": "Pois" + }, + "35": { + "then": "Papaye" + }, + "36": { + "then": "Riz" + }, + "37": { + "then": "Sésame" + }, + "38": { + "then": "Karité" + }, + "39": { + "then": "Sorgho" + }, + "4": { + "then": "Poivron" + }, + "40": { + "then": "Soja" + }, + "41": { + "then": "Tomates" + }, + "42": { + "then": "Légumes" + }, + "43": { + "then": "Pastèque" + }, + "44": { + "then": "Igname" + }, + "5": { + "then": "Chou" + }, + "6": { + "then": "Carottes" + }, + "7": { + "then": "Manioc" + }, + "8": { + "then": "Noix de cajou" + }, + "9": { + "then": "Piment" + } + }, + "question": "Produits de la CUMA" + }, + "short_name": { + "render": "Le nom abrégé est {short_name}" + } + } + }, "filters": { "filter": { "0": { diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 481c42a12..eee81bb13 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -1281,6 +1281,16 @@ }, "question": "Wanneer is deze laatste bank laatst gesurveyed?", "render": "Deze bank is laatst gesurveyd op {survey:date}" + }, + "bench-view-title": { + "render": "Zicht vanop de bank" + }, + "bench-view-upload": { + "render": { + "special": { + "label": "Voeg een afbeelding van het zicht vanaf de bank toe" + } + } } }, "title": { @@ -5373,6 +5383,23 @@ "render": "Brandblussers" } }, + "farming_equipment_cooperative": { + "tagRenderings": { + "cuma_equipment": { + "mappings": { + "28": { + "then": "Dorsmachine" + }, + "3": { + "then": "Verticuleermachine" + } + } + }, + "produces": { + "question": "Voor wat soort oogstproducten heeft deze cooperatie materiaal?" + } + } + }, "filters": { "filter": { "0": { diff --git a/langs/themes/en.json b/langs/themes/en.json index e7db2e9c7..f82f0cd76 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -101,6 +101,40 @@ }, "benches": { "description": "This map shows all benches that are recorded in OpenStreetMap: Individual benches, and benches belonging to public transport stops or shelters.", + "layers": { + "3": { + "override": { + "=tagRenderings": { + "0": { + "render": "This is a bench that is known in Openbenches.org but might not exist in OpenStreetMap. If this bench still exists in the real world, you can add or link this information to OpenStreetMap with the tools below" + }, + "2": { + "render": { + "special": { + "text": "See {_idN} on openbenches.org" + } + } + }, + "3": { + "render": "OpenStreetMap knows about a bench which is {_closest_osm_poi_distance} meter away. " + }, + "5": { + "render": { + "before": "Choose below which bench you want to link." + } + }, + "6": { + "render": { + "special": { + "text": "Create a bench in OSM with the properties of openBenches.org" + } + } + } + }, + "name=": "Data from OpenBenches.org" + } + } + }, "shortDescription": "A map of benches", "title": "Benches" }, diff --git a/langs/themes/nl.json b/langs/themes/nl.json index 99e2364c3..2018c7194 100644 --- a/langs/themes/nl.json +++ b/langs/themes/nl.json @@ -93,6 +93,26 @@ }, "benches": { "description": "Deze kaart toont alle zitbanken die zijn opgenomen in OpenStreetMap: individuele banken en banken bij bushaltes.", + "layers": { + "3": { + "override": { + "=tagRenderings": { + "5": { + "render": { + "before": "Kies hieronder welke bank je wilt linken." + } + }, + "6": { + "render": { + "special": { + "text": "Maak een bank aan in OSM met de attributen van openBenches.org" + } + } + } + } + } + } + }, "shortDescription": "Een kaart van zitbanken", "title": "Zitbanken" }, diff --git a/scripts/ScriptUtils.ts b/scripts/ScriptUtils.ts index 7e54ce5f0..4adeaf6c4 100644 --- a/scripts/ScriptUtils.ts +++ b/scripts/ScriptUtils.ts @@ -285,13 +285,13 @@ export default class ScriptUtils { */ static detectVariablePaths(path: string) { const splitPath = path.match(/(.*)\{(.+)\}(.*)/) - if(!splitPath){ - return [path] + if (!splitPath) { + return [path] } const [_, head] = splitPath const headPath = head.slice(0, head.lastIndexOf("/")) const allPossibleMatchingFiles = ScriptUtils.readDirRecSync(headPath) const pathAsRegex = path.replaceAll(/\{.+}/g, ".+") - return allPossibleMatchingFiles.filter(pth => pth.match(pathAsRegex)) + return allPossibleMatchingFiles.filter((pth) => pth.match(pathAsRegex)) } } diff --git a/scripts/generateDocs.ts b/scripts/generateDocs.ts index 376dcfbe0..73b9a8b94 100644 --- a/scripts/generateDocs.ts +++ b/scripts/generateDocs.ts @@ -46,8 +46,7 @@ class ToSlideshowJson { } public convert() { - const lines = readFileSync(this._source, "utf8") - .split("\n") + const lines = readFileSync(this._source, "utf8").split("\n") const sections: string[][] = [] let currentSection: string[] = [] @@ -159,10 +158,10 @@ export class GenerateDocs extends Script { this.generateEliDocs() this.generateBuiltinUnits() this.writeMarkdownFile("./Docs/Studio/SpecialInputElements.md", Validators.HelpText(), [ - "src/UI/InputElement/Validators.ts" + "src/UI/InputElement/Validators.ts", ]) this.writeMarkdownFile("./Docs/Studio/Tags_format.md", TagUtils.generateDocs(), [ - "src/Logic/Tags/TagUtils.ts" + "src/Logic/Tags/TagUtils.ts", ]) this.writeMarkdownFile( @@ -170,7 +169,7 @@ export class GenerateDocs extends Script { SpecialVisualizations.HelpMessage(), ["src/UI/SpecialVisualizations.ts"], { - tocMaxDepth: 3 + tocMaxDepth: 3, } ) this.writeMarkdownFile( @@ -181,7 +180,6 @@ export class GenerateDocs extends Script { ) } - // For dev { this.generateQueryParameterDocs() @@ -208,13 +206,12 @@ export class GenerateDocs extends Script { ScriptUtils.erasableLog("Written docs for theme", theme.id) }) - this.generateOverviewsForAllSingleLayer("./Docs/nl/Layers", "nl") } this.writeMarkdownFile("./Docs/ChangesetMeta.md", Changes.getDocs(), [ "src/Logic/Osm/Changes.ts", - "src/Logic/Osm/ChangesetHandler.ts" + "src/Logic/Osm/ChangesetHandler.ts", ]) new WikiPageGenerator().generate() @@ -224,9 +221,11 @@ export class GenerateDocs extends Script { this.generateSidebar("nl") this.generatedPaths.push(".gitignore") - writeFileSync("./Docs/.gitignore", this.generatedPaths - .map(p => p.replace("./Docs/", "")) - .join("\n"), "utf-8") + writeFileSync( + "./Docs/.gitignore", + this.generatedPaths.map((p) => p.replace("./Docs/", "")).join("\n"), + "utf-8" + ) console.log("Generated docs") } @@ -238,7 +237,7 @@ export class GenerateDocs extends Script { options?: { noTableOfContents?: boolean tocMaxDepth?: number - lang?: string, + lang?: string noWarn?: boolean } ): void { @@ -269,25 +268,31 @@ export class GenerateDocs extends Script { md += "\n" } - const warnAutomated = options?.noWarn ? "" : - "[//]: # (WARNING: this file is automatically generated. Please find the sources at the bottom and edit those sources)\n\n" + const warnAutomated = options?.noWarn + ? "" + : "[//]: # (WARNING: this file is automatically generated. Please find the sources at the bottom and edit those sources)\n\n" + const sources = autogenSource + .map( + (s) => + `[${s}](https://source.mapcomplete.org/MapComplete/MapComplete/src/branch/develop/${s})` + ) + .join(", ") - const sources = autogenSource.map( - (s) => `[${s}](https://source.mapcomplete.org/MapComplete/MapComplete/src/branch/develop/${s})`).join(", ") + const generatedFrom = new TypedTranslation<{ sources; date }>({ + en: "This document is autogenerated from {sources} on {date}", + nl: "Dit document werd gegenereerd op basis van {sources} op {date}", + }) + .Subs({ sources, date: new Date().toDateString() }) + .textFor(lang) - const generatedFrom = - new TypedTranslation<{ sources, date }>({ - en: "This document is autogenerated from {sources} on {date}", - nl: "Dit document werd gegenereerd op basis van {sources} op {date}" - }).Subs({ sources, date: new Date().toDateString() }).textFor(lang) - - - writeFileSync(filename, warnAutomated + md + (options?.noWarn ? "" : "\n\n" + generatedFrom + "\n")) + writeFileSync( + filename, + warnAutomated + md + (options?.noWarn ? "" : "\n\n" + generatedFrom + "\n") + ) this.generatedPaths.push(filename) } - private generateEliDocs() { const eli = AvailableRasterLayers.editorLayerIndex() this.writeMarkdownFile( @@ -295,8 +300,8 @@ export class GenerateDocs extends Script { [ "# Layers in the Editor Layer Index", "This table gives a summary of ids, names and other metainformation of background imagery that is available in MapComplete and that can be used as (default) map background." + - "These are sourced from [the Editor Layer Index](https://github.com/osmlab/editor-layer-index)", + - "\n[See the online, interactive map here](https://osmlab.github.io/editor-layer-index/)", + "These are sourced from [the Editor Layer Index](https://github.com/osmlab/editor-layer-index)", + +"\n[See the online, interactive map here](https://osmlab.github.io/editor-layer-index/)", MarkdownUtils.table( ["id", "name", "category", "Best", "attribution"], eli.map((f) => [ @@ -305,10 +310,10 @@ export class GenerateDocs extends Script { f.properties.category, f.properties.best ? "⭐" : "", f.properties.attribution?.html ?? f.properties.attribution?.text, - ]), + ]) ), ].join("\n\n"), - ["./public/assets/data/editor-layer-index.json"], + ["./public/assets/data/editor-layer-index.json"] ) } @@ -367,7 +372,10 @@ export class GenerateDocs extends Script { * Generates documentation for the all the individual layers. * Inline layers are included (if the theme is public) */ - private generateOverviewsForAllSingleLayer(targetDirectory: string = "./Docs/Layers", lang: string = "en"): void { + private generateOverviewsForAllSingleLayer( + targetDirectory: string = "./Docs/Layers", + lang: string = "en" + ): void { const allLayers: LayerConfig[] = Array.from(AllSharedLayers.sharedLayers.values()).filter( (layer) => layer["source"] !== null ) @@ -434,19 +442,23 @@ export class GenerateDocs extends Script { mkdirSync(targetDirectory) } allLayers.forEach((layer) => { - const element = layer.generateDocumentation({ - usedInThemes: themesPerLayer.get(layer.id), - layerIsNeededBy: layerIsNeededBy, - dependencies: DependencyCalculator.getLayerDependencies(layer), - lang - }).replaceAll("./Docs/Layers", targetDirectory) + const element = layer + .generateDocumentation({ + usedInThemes: themesPerLayer.get(layer.id), + layerIsNeededBy: layerIsNeededBy, + dependencies: DependencyCalculator.getLayerDependencies(layer), + lang, + }) + .replaceAll("./Docs/Layers", targetDirectory) const inlineSource = inlineLayers.get(layer.id) ScriptUtils.erasableLog("Exporting layer documentation for", layer.id) let source: string = `assets/layers/${layer.id}/${layer.id}.json` if (inlineSource !== undefined) { source = `assets/themes/${inlineSource}/${inlineSource}.json` } - this.writeMarkdownFile(targetDirectory + "/" + layer.id + ".md", element, [source], { lang }) + this.writeMarkdownFile(targetDirectory + "/" + layer.id + ".md", element, [source], { + lang, + }) }) } @@ -491,17 +503,24 @@ export class GenerateDocs extends Script { } const docs: string[] = [ - "# Which tagrendering is used where?", "", - "This document details where a tagRendering from one layer is reused in another layer, either by directly using it or by using a `{\"builtin\": id, \"override\": ...}` syntax", + "# Which tagrendering is used where?", + "", + 'This document details where a tagRendering from one layer is reused in another layer, either by directly using it or by using a `{"builtin": id, "override": ...}` syntax', "Having this overview supports e.g. refactoring efforts", - "## Existing builtin tagrenderings", ""] + "## Existing builtin tagrenderings", + "", + ] for (const [builtin, usedByLayers] of Array.from(layersUsingBuiltin.entries())) { docs.push(`### ${builtin}\n`) docs.push(usedByLayers.length + " usages") - docs.push(`${usedByLayers.map((item) => ` - [${item}](./Docs/Layers/${item}.md)`).join("\n")}`) + docs.push( + `${usedByLayers.map((item) => ` - [${item}](./Docs/Layers/${item}.md)`).join("\n")}` + ) } - this.writeMarkdownFile("./Docs/Studio/TagRendering_reuse_overview.md", docs.join("\n"), ["assets/layers/*.json"]) + this.writeMarkdownFile("./Docs/Studio/TagRendering_reuse_overview.md", docs.join("\n"), [ + "assets/layers/*.json", + ]) } private generateQueryParameterDocs() { @@ -537,7 +556,7 @@ export class GenerateDocs extends Script { ]) } - private generateForTheme(theme: ThemeConfig, options?: { path?: string, lang?: string }): void { + private generateForTheme(theme: ThemeConfig, options?: { path?: string; lang?: string }): void { const allLayers = AllSharedLayers.sharedLayers const layersToShow = theme.layers.filter( (l) => l.id !== "favourite" && Constants.added_by_default.indexOf(l.id) < 0 @@ -563,38 +582,59 @@ export class GenerateDocs extends Script { if (allLayers.has(l.id)) { return `[${l.id}](../Layers/${l.id}.md)` } - return `[${l.id} (${l.name?.textFor(lang)})](#${l.id.trim().replace(/ /g, "-")})` + return `[${l.id} (${l.name?.textFor(lang)})](#${l.id + .trim() + .replace(/ /g, "-")})` }) ), - new Translation( - { - en: "This theme is available in the following languages:", - nl: "Deze kaart is beschikbaar in de volgende talen:", - }, - ).textFor(lang), - MarkdownUtils.list(theme.language.filter((ln) => ln !== "_context").map(ln => { - if (language_translations[ln]) { - return ln + " (" + new Translation(language_translations[ln]).textFor(lang) + ")" - } else { - return ln - } - }, - )), + new Translation({ + en: "This theme is available in the following languages:", + nl: "Deze kaart is beschikbaar in de volgende talen:", + }).textFor(lang), + MarkdownUtils.list( + theme.language + .filter((ln) => ln !== "_context") + .map((ln) => { + if (language_translations[ln]) { + return ( + ln + + " (" + + new Translation(language_translations[ln]).textFor(lang) + + ")" + ) + } else { + return ln + } + }) + ), ] if (layersToInline.length > 0) { - el.push(MarkdownUtils.title(1, new Translation({ - en: "Layers defined in this theme configuration file", - nl: "Lagen gedefinieerd in dit kaartthema-bestand", - })).textFor(lang)) - el.push(MarkdownUtils.list(layersToInline.map(l => `[${l.name?.textFor(lang) ?? ""} (\`${l.id}\`)](#${l.id})`))) - - el.push(new Translation({ - en: "These layers can not be reused in different themes.", - nl: "Deze lagen kunnen niet in andere kaartthemas hergebruikt worden", - }).textFor(lang)) el.push( - ...layersToInline.map((l) => l.generateDocumentation({ usedInThemes: null, lang })), + MarkdownUtils.title( + 1, + new Translation({ + en: "Layers defined in this theme configuration file", + nl: "Lagen gedefinieerd in dit kaartthema-bestand", + }) + ).textFor(lang) + ) + el.push( + MarkdownUtils.list( + layersToInline.map( + (l) => `[${l.name?.textFor(lang) ?? ""} (\`${l.id}\`)](#${l.id})` + ) + ) + ) + + el.push( + new Translation({ + en: "These layers can not be reused in different themes.", + nl: "Deze lagen kunnen niet in andere kaartthemas hergebruikt worden", + }).textFor(lang) + ) + el.push( + ...layersToInline.map((l) => l.generateDocumentation({ usedInThemes: null, lang })) ) } @@ -606,7 +646,7 @@ export class GenerateDocs extends Script { path + "/" + theme.id + ".md", el.join("\n"), [`assets/themes/${theme.id}/${theme.id}.json`], - { noTableOfContents: true, lang }, + { noTableOfContents: true, lang } ) } @@ -742,14 +782,16 @@ export class GenerateDocs extends Script { */ private generateSidebar(subdirectory = ""): string[] { const tr = Translations.t.app.back.textFor(subdirectory) - const sidebar: string[] = [ - `${tr}` - ] + const sidebar: string[] = [`${tr}`] const allFiles = ScriptUtils.readDirRecSync("./Docs/" + subdirectory) - .filter(path => path.endsWith(".md")) - .filter(path => !path.startsWith("_")) - .filter(path => !path.startsWith("./Docs/nl/") || (path.startsWith("./Docs/" + subdirectory) && subdirectory !== "")) - .map(path => path.substring("./Docs/".length + subdirectory.length + 1)) + .filter((path) => path.endsWith(".md")) + .filter((path) => !path.startsWith("_")) + .filter( + (path) => + !path.startsWith("./Docs/nl/") || + (path.startsWith("./Docs/" + subdirectory) && subdirectory !== "") + ) + .map((path) => path.substring("./Docs/".length + subdirectory.length + 1)) const perDirectory = new Map() function addFile(dir: string, path: string) { @@ -779,13 +821,15 @@ export class GenerateDocs extends Script { const directories: [string, Translation | string][] = [ ["", ""], ["Layers", new Translation({ en: "Overview of layers", nl: "Overzicht van de lagen" })], - ["Themes", new Translation({ en: "Overview of map themes", nl: "Overzicht van de themas" })], + [ + "Themes", + new Translation({ en: "Overview of map themes", nl: "Overzicht van de themas" }), + ], ["UserTests", "Usability tests with users"], ["Studio", "For theme builders"], ["Dev", "For developers"], ] - for (const [dir, title] of directories) { if (title === null) { continue @@ -830,10 +874,9 @@ export class GenerateDocs extends Script { } private generateNormalLayerOverview(type: "Layers" | "Themes", subdir = "") { - const layerinfo: [string, string, string][] = [] - const source: ReadonlyMap | AllKnownLayoutsLazy - = type === "Layers" ? AllSharedLayers.sharedLayers : AllKnownLayouts.allKnownLayouts + const source: ReadonlyMap | AllKnownLayoutsLazy = + type === "Layers" ? AllSharedLayers.sharedLayers : AllKnownLayouts.allKnownLayouts const keys = Array.from(source.keys()) keys.sort() @@ -841,7 +884,7 @@ export class GenerateDocs extends Script { const layer = source.get(id) let name: Translation if (type === "Layers") { - const layer_ = (layer) + const layer_ = layer if (!layer_.isNormal()) { continue } @@ -849,36 +892,41 @@ export class GenerateDocs extends Script { } else { name = (layer).title } - layerinfo.push([`[${id}](./${type}/${id})`, name.textFor(subdir), (layer["shortDescription"] ?? layer.description)?.textFor(subdir)]) + layerinfo.push([ + `[${id}](./${type}/${id})`, + name.textFor(subdir), + (layer["shortDescription"] ?? layer.description)?.textFor(subdir), + ]) } const titles = { - "Layers": new Translation({ en: "Layers", nl: "Lagen" }), - "Themes": new Translation({ en: "Themes", nl: "Kaartthema's" }) + Layers: new Translation({ en: "Layers", nl: "Lagen" }), + Themes: new Translation({ en: "Themes", nl: "Kaartthema's" }), } const intro: Record> = { - "Layers": new TypedTranslation<{ version }>({ + Layers: new TypedTranslation<{ version }>({ en: "The following layers are available in MapComplete {version}:", - nl: "De volgende lagen zijn beschikbaar in MapComplete {version}:" + nl: "De volgende lagen zijn beschikbaar in MapComplete {version}:", }), - "Themes": new TypedTranslation<{ version }>({ + Themes: new TypedTranslation<{ version }>({ en: "The following themes are available in MapComplete {version}:", - nl: "De volgende kaartthemas zijn beschikbaar in MapComplete {version}:" - }) + nl: "De volgende kaartthemas zijn beschikbaar in MapComplete {version}:", + }), } - const doc = ["# " + titles[type].textFor(subdir), - intro[type].Subs({ version: Constants.vNumber }).textFor(subdir) - , MarkdownUtils.table( - ["id", "name", "description"], - layerinfo) + const doc = [ + "# " + titles[type].textFor(subdir), + intro[type].Subs({ version: Constants.vNumber }).textFor(subdir), + MarkdownUtils.table(["id", "name", "description"], layerinfo), ] const path = `./Docs/${subdir}/${type}` if (!existsSync(path)) { mkdirSync(path) } - this.writeMarkdownFile(`${path}/README.md`, doc.join("\n\n"), [`./assets/${type.toLowerCase()}/*.json`]) + this.writeMarkdownFile(`${path}/README.md`, doc.join("\n\n"), [ + `./assets/${type.toLowerCase()}/*.json`, + ]) } /** @@ -893,8 +941,7 @@ export class GenerateDocs extends Script { continue } if (!AllSharedLayers.sharedLayers.has(id)) { - throw ("Privileged layer definition not found: " + id) - + throw "Privileged layer definition not found: " + id } } @@ -933,7 +980,8 @@ export class GenerateDocs extends Script { } const el = [ - "# Special and priviliged layers", "", + "# Special and priviliged layers", + "", "MapComplete has a few data layers available which have special properties through builtin-hooks.", "They perform various tasks, such as showing the GPS-location and track on the screen or help in special elements such as the 'cut way'-element.", "As a theme builder, you can influence the behaviour of those layers by overriding them in your .json file", @@ -942,14 +990,15 @@ export class GenerateDocs extends Script { ), ...Lists.noNull( Constants.priviliged_layers.map((id) => AllSharedLayers.sharedLayers.get(id)) - ).map((l) => - l.generateDocumentation({ - usedInThemes: themesPerLayer.get(l.id), - layerIsNeededBy: layerIsNeededBy, - dependencies: DependencyCalculator.getLayerDependencies(l), - addedByDefault: Constants.added_by_default.indexOf(l.id) >= 0, - canBeIncluded: Constants.no_include.indexOf(l.id) < 0, - }) + "\n" + ).map( + (l) => + l.generateDocumentation({ + usedInThemes: themesPerLayer.get(l.id), + layerIsNeededBy: layerIsNeededBy, + dependencies: DependencyCalculator.getLayerDependencies(l), + addedByDefault: Constants.added_by_default.indexOf(l.id) >= 0, + canBeIncluded: Constants.no_include.indexOf(l.id) < 0, + }) + "\n" ), ].join("\n") this.writeMarkdownFile("./Docs/Studio/SpecialLayers.md", el, [ diff --git a/scripts/generateLayerOverview.ts b/scripts/generateLayerOverview.ts index 0b4a718d0..7eac436bf 100644 --- a/scripts/generateLayerOverview.ts +++ b/scripts/generateLayerOverview.ts @@ -898,7 +898,7 @@ class LayerOverviewUtils extends Script { console.log("Creating needed_assets-file") const images = Lists.dedup( Array.from(sharedThemes.values()).flatMap((th) => th._usedImages ?? []) - ).flatMap(path => ScriptUtils.detectVariablePaths(path)) + ).flatMap((path) => ScriptUtils.detectVariablePaths(path)) writeFileSync("needed_assets.csv", images.join("\n")) console.log("Written needed_assets.csv") diff --git a/scripts/generatePmTilesExtracts.ts b/scripts/generatePmTilesExtracts.ts index 09442db4f..6cf04a3a6 100644 --- a/scripts/generatePmTilesExtracts.ts +++ b/scripts/generatePmTilesExtracts.ts @@ -13,8 +13,8 @@ class GeneratePmTilesExtracts extends Script { constructor() { super( "Generates many pmtiles-archive from planet-latest.pmtiles. Expects the `pmtiles`-executable to be at `/data/pmtiles`." + - "\n\n" + - "The first argument should be the 'targetDirectory', where many subdirectories will be made containing the subarchives. A second argument (defaulting to [targetDir]/planet-latest.pmtiles) is the source data" + "\n\n" + + "The first argument should be the 'targetDirectory', where many subdirectories will be made containing the subarchives. A second argument (defaulting to [targetDir]/planet-latest.pmtiles) is the source data" ) } @@ -31,15 +31,7 @@ class GeneratePmTilesExtracts extends Script { this.skipped += boundary return } - console.log( - "Starting column", - x, - "at zoom", - z, - "as", - lastFileForColumn, - "does not exist" - ) + console.log("Starting column", x, "at zoom", z, "as", lastFileForColumn, "does not exist") for (let y = 0; y < boundary; y++) { yield this.extractsGenerator.generateArchive(z, x, y, maxzoom) @@ -58,8 +50,6 @@ class GeneratePmTilesExtracts extends Script { } } - - private *generateAll(): Generator> { const zoomlevels: Record = OfflineBasemapManager.zoomelevels for (const key in zoomlevels) { @@ -88,7 +78,7 @@ class GeneratePmTilesExtracts extends Script { async main(args: string[]): Promise { this.targetDir = args[0] - const sourceFile = this.targetDir+"/planet-latest.pmtiles" + const sourceFile = this.targetDir + "/planet-latest.pmtiles" if (!this.targetDir) { console.log("Please specify a target directory. Did you forget '--' in vite-node?") return @@ -126,11 +116,14 @@ class GeneratePmTilesExtracts extends Script { )}` ) } while (batch.length > 0) - writeFileSync(this.targetDir+"/Last_pm_tile_extracts.txt", - [new Date().getTime() + "", + writeFileSync( + this.targetDir + "/Last_pm_tile_extracts.txt", + [ + new Date().getTime() + "", new Date().toISOString(), "# The script converting the planet-latest.pmtiles into sub-archives has been successfully executed at the stated time", - ].join("\n"), "utf-8", + ].join("\n"), + "utf-8" ) } } diff --git a/scripts/generateTaginfoProjectFiles.ts b/scripts/generateTaginfoProjectFiles.ts index fc638b278..da3ac4ddc 100644 --- a/scripts/generateTaginfoProjectFiles.ts +++ b/scripts/generateTaginfoProjectFiles.ts @@ -235,12 +235,7 @@ function generateProjectsOverview(files: string[]) { const projectList = readFileSync(tagInfoList, { encoding: "utf8" }) .split("\n") .filter((entry) => entry.indexOf("mapcomplete_") < 0) - .concat( - files.map( - (f) => - `${f} https://docs.mapcomplete.org/TagInfo/${f}.json` - ) - ) + .concat(files.map((f) => `${f} https://docs.mapcomplete.org/TagInfo/${f}.json`)) .sort() .filter((entry) => entry != "") diff --git a/scripts/pmTilesExtractGenerator.ts b/scripts/pmTilesExtractGenerator.ts index 4f8f162e7..74ec5c0b2 100644 --- a/scripts/pmTilesExtractGenerator.ts +++ b/scripts/pmTilesExtractGenerator.ts @@ -6,7 +6,11 @@ export class PmTilesExtractGenerator { private readonly _sourceFile: string private readonly _executeableLocation: string - constructor(sourceFile: string, targetDir: string, executeableLocation: string = "/data/pmtiles") { + constructor( + sourceFile: string, + targetDir: string, + executeableLocation: string = "/data/pmtiles" + ) { this._sourceFile = sourceFile this._targetDir = targetDir this._executeableLocation = executeableLocation @@ -20,7 +24,7 @@ export class PmTilesExtractGenerator { }) if (captureStdioChunks !== undefined) { - child.stdout.on("data", data => { + child.stdout.on("data", (data) => { captureStdioChunks(data) }) } @@ -50,15 +54,15 @@ export class PmTilesExtractGenerator { } const outputFileName = this.getFilename(z, x, y) await this.startProcess( - `extract ${this._sourceFile} --download-threads=1 --minzoom=${z}${maxzoomflag} --bbox=${[ + `extract ${ + this._sourceFile + } --download-threads=1 --minzoom=${z}${maxzoomflag} --bbox=${[ min_lon, min_lat + 0.0001, max_lon, max_lat, - ].join(",")} ${outputFileName}`, + ].join(",")} ${outputFileName}` ) return outputFileName } - - } diff --git a/scripts/server.ts b/scripts/server.ts index ca3c18a5e..4f5ca199b 100644 --- a/scripts/server.ts +++ b/scripts/server.ts @@ -65,9 +65,9 @@ export class Server { }, }) this.handlers = handle - http.createServer((req: http.IncomingMessage, res) => - this.answerRequest(req, res) - ).listen(port) + http.createServer((req: http.IncomingMessage, res) => this.answerRequest(req, res)).listen( + port + ) console.log( "Server is running on http://127.0.0.1:" + port, ". Supported endpoints are: " + handle.map((h) => h.mustMatch).join(", ") @@ -86,7 +86,7 @@ export class Server { "from:", req.headers.origin, new Date().toISOString(), - path, + path ) if (this.options?.ignorePathPrefix) { for (const toIgnore of this.options.ignorePathPrefix) { @@ -114,14 +114,11 @@ export class Server { res.setHeader( "Access-Control-Allow-Headers", - "Origin, X-Requested-With, Content-Type, Accept", + "Origin, X-Requested-With, Content-Type, Accept" ) res.setHeader("Access-Control-Allow-Origin", req.headers.origin ?? "*") if (req.method === "OPTIONS") { - res.setHeader( - "Access-Control-Allow-Methods", - "POST, GET, OPTIONS, DELETE, UPDATE", - ) + res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, UPDATE") res.writeHead(204, { "Content-Type": handler.mimetype }) res.end() return @@ -154,7 +151,7 @@ export class Server { "resulted in a ", typeof result, " instead of a string:", - result, + result ) } @@ -162,8 +159,6 @@ export class Server { res.writeHead(200, { "Content-Type": handler.mimetype, ...extraHeaders }) res.write("" + result) res.end() - - } catch (e) { console.error("Could not handle request:", e) res.writeHead(500) @@ -183,8 +178,7 @@ export class Server { * @param path * @param res */ - public static sendFile(path: string, res: ServerResponse){ - createReadStream(path).pipe(res); + public static sendFile(path: string, res: ServerResponse) { + createReadStream(path).pipe(res) } - } diff --git a/scripts/serverPmTileExtracts.ts b/scripts/serverPmTileExtracts.ts index 900f51ef0..14ce96539 100644 --- a/scripts/serverPmTileExtracts.ts +++ b/scripts/serverPmTileExtracts.ts @@ -11,14 +11,16 @@ class ServerPmTileExtracts extends Script { private static FILE_IS_STALE_AFTER_SEC = 30 * 24 * 60 * 60 constructor() { - super("Starts a server that serves PMtiles. Usage:\n" + - "sourceFile cachedir [portnumber??2346]") + super( + "Starts a server that serves PMtiles. Usage:\n" + + "sourceFile cachedir [portnumber??2346]" + ) } async main(args: string[]): Promise { - if(args.length < 2){ + if (args.length < 2) { this.printHelp() - throw ("!!! Please, specify a source- and cachedir !!!") + throw "!!! Please, specify a source- and cachedir !!!" } const sourcefile = args[0] const targetDir = args[1] @@ -29,52 +31,65 @@ class ServerPmTileExtracts extends Script { const creationDates = new Map() - new Server(port, {}, - [ - { - mustMatch: /\d+\/\d+\/\d+.pmtiles/, - unmanaged: true, - mimetype: "application/octet-stream", - handle: async (path: string, - queryParams: URLSearchParams, - req: http.IncomingMessage, - body: string, - res: ServerResponse) => { - const [z,x,y] = path.split(".")[0].split("/").map(x => Number(x)) - if(!(z in zoomlevels)){ - throw `Invalid zoomlevel ${z} (x: ${x}, y: ${y}, must be one of ${Array.from(Object.keys(zoomlevels)).join(", ")}` - } + new Server(port, {}, [ + { + mustMatch: /\d+\/\d+\/\d+.pmtiles/, + unmanaged: true, + mimetype: "application/octet-stream", + handle: async ( + path: string, + queryParams: URLSearchParams, + req: http.IncomingMessage, + body: string, + res: ServerResponse + ) => { + const [z, x, y] = path + .split(".")[0] + .split("/") + .map((x) => Number(x)) + if (!(z in zoomlevels)) { + throw `Invalid zoomlevel ${z} (x: ${x}, y: ${y}, must be one of ${Array.from( + Object.keys(zoomlevels) + ).join(", ")}` + } - const targetFile = generator.getFilename(z, x, y) + const targetFile = generator.getFilename(z, x, y) - let targetCreationDate = creationDates.get(targetFile) - let isStale = targetCreationDate === undefined ? false : new Date().getTime() - targetCreationDate.getTime() > 1000 * ServerPmTileExtracts.FILE_IS_STALE_AFTER_SEC + let targetCreationDate = creationDates.get(targetFile) + let isStale = + targetCreationDate === undefined + ? false + : new Date().getTime() - targetCreationDate.getTime() > + 1000 * ServerPmTileExtracts.FILE_IS_STALE_AFTER_SEC - if (isStale || !existsSync(targetFile)) { - ScriptUtils.createParentDir(targetFile) - console.log("Creating", targetFile) - const start = new Date() - await generator.generateArchive(z, x, y) - const stop = new Date() - console.log("Creating ", targetFile, "took", (stop.getTime() - start.getTime()) + "ms") - } else if (targetCreationDate === undefined) { - const stats = statSync(targetFile) - creationDates.set(targetFile, stats.mtime) - } - - if(req.destroyed){ - return null - } - res.writeHead(200, { "Content-Type": "application/octet-stream"}) - Server.sendFile(targetFile, res) + if (isStale || !existsSync(targetFile)) { + ScriptUtils.createParentDir(targetFile) + console.log("Creating", targetFile) + const start = new Date() + await generator.generateArchive(z, x, y) + const stop = new Date() + console.log( + "Creating ", + targetFile, + "took", + stop.getTime() - start.getTime() + "ms" + ) + } else if (targetCreationDate === undefined) { + const stats = statSync(targetFile) + creationDates.set(targetFile, stats.mtime) + } + if (req.destroyed) { return null - }, - }, - ], - ) - } + } + res.writeHead(200, { "Content-Type": "application/octet-stream" }) + Server.sendFile(targetFile, res) + return null + }, + }, + ]) + } } new ServerPmTileExtracts().run() diff --git a/src/Logic/Tags/RegexTag.ts b/src/Logic/Tags/RegexTag.ts index 20c47412f..2bcda3e02 100644 --- a/src/Logic/Tags/RegexTag.ts +++ b/src/Logic/Tags/RegexTag.ts @@ -255,18 +255,20 @@ export class RegexTag extends TagsFilter { asHumanString(linkToWiki: boolean) { if (typeof this.key === "string") { const oper = typeof this.value === "string" ? "=" : "~" - if(linkToWiki){ - return `[${this.key}](https://wiki.osm.org/wiki/Key:${this.key})\`${this.invert ? "!" : ""}${oper}${RegexTag.source(this.value)}\`` + if (linkToWiki) { + return `[${this.key}](https://wiki.osm.org/wiki/Key:${this.key})\`${ + this.invert ? "!" : "" + }${oper}${RegexTag.source(this.value)}\`` } - const v= `${this.key}${this.invert ? "!" : ""}${oper}${RegexTag.source(this.value)}` - if(linkToWiki){ + const v = `${this.key}${this.invert ? "!" : ""}${oper}${RegexTag.source(this.value)}` + if (linkToWiki) { return `\`${v}\`` } return v } const v = `${this.key.source}${this.invert ? "!" : ""}~~${RegexTag.source(this.value)}` - if(linkToWiki){ + if (linkToWiki) { return `\`${v}\`` } return v diff --git a/src/Models/ThemeConfig/Conversion/PrepareLayer.ts b/src/Models/ThemeConfig/Conversion/PrepareLayer.ts index 318d72e52..2279c2e41 100644 --- a/src/Models/ThemeConfig/Conversion/PrepareLayer.ts +++ b/src/Models/ThemeConfig/Conversion/PrepareLayer.ts @@ -1,6 +1,18 @@ -import { Concat, DesugaringContext, DesugaringStep, Each, FirstOf, Fuse, On, SetDefault } from "./Conversion" +import { + Concat, + DesugaringContext, + DesugaringStep, + Each, + FirstOf, + Fuse, + On, + SetDefault, +} from "./Conversion" import { LayerConfigJson } from "../Json/LayerConfigJson" -import { MinimalTagRenderingConfigJson, TagRenderingConfigJson } from "../Json/TagRenderingConfigJson" +import { + MinimalTagRenderingConfigJson, + TagRenderingConfigJson, +} from "../Json/TagRenderingConfigJson" import { Utils } from "../../../Utils" import SpecialVisualizations from "../../../UI/SpecialVisualizations" import Translations from "../../../UI/i18n/Translations" @@ -11,7 +23,10 @@ import { TagConfigJson } from "../Json/TagConfigJson" import PointRenderingConfigJson, { IconConfigJson } from "../Json/PointRenderingConfigJson" import ValidationUtils from "./ValidationUtils" import { RenderingSpecification } from "../../../UI/SpecialVisualization" -import { MappingConfigJson, QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson" +import { + MappingConfigJson, + QuestionableTagRenderingConfigJson, +} from "../Json/QuestionableTagRenderingConfigJson" import { ConfigMeta } from "../../../UI/Studio/configMeta" import { ConversionContext } from "./ConversionContext" import { ExpandRewrite } from "./ExpandRewrite" @@ -457,7 +472,10 @@ export class RewriteSpecial extends DesugaringStep { private static escapeStr(v: string, context: ConversionContext): string { if (typeof v !== "string") { - context.err("Detected a non-string value where one expected a string (while rewriting a special): " + JSON.stringify(v)) + context.err( + "Detected a non-string value where one expected a string (while rewriting a special): " + + JSON.stringify(v) + ) return RewriteSpecial.escapeStr("" + v, context) } return v @@ -593,9 +611,13 @@ export class RewriteSpecial extends DesugaringStep { argNamesList, (x) => x ) - return `Unexpected argument in special block of type ${vis.funcName} with name '${wrongArg}'. Did you mean ${ + return `Unexpected argument in special block of type ${ + vis.funcName + } with name '${wrongArg}'. Did you mean ${ byDistance[0] - }?\n\tAll known arguments are: ${vis.args.map(arg => arg.name + ": \t" + arg.type + "\t" + arg.doc).join("\n ")}` + }?\n\tAll known arguments are: ${vis.args + .map((arg) => arg.name + ": \t" + arg.type + "\t" + arg.doc) + .join("\n ")}` }) .forEach((e) => context.err(e)) @@ -756,7 +778,7 @@ class ExpandIconBadges extends DesugaringStep { for (const tr of expanded) { const condition = tr.condition - for (const trElement of tr.mappings) { + for (const trElement of tr.mappings) { const showIf = TagUtils.optimzeJson({ and: Lists.noNull([ condition, @@ -1129,10 +1151,9 @@ export class OrderLayer extends DesugaringStep { const orderTag = new OrderTagRendering() let json: LayerConfigJson = jsonOrString - json = new On( - "tagRenderings", - new Each(orderTag) - ).convert(json, context) + json = ( + new On("tagRenderings", new Each(orderTag)).convert(json, context) + ) json = new On("title", orderTag).convert(json, context) @@ -1174,29 +1195,46 @@ export class PrepareLayer extends Fuse { return Lists.flatten(list) }) ), - new On( - "pointRendering", - (layer) => - new Each(new On("marker", new Each(new ExpandMarkerRenderings(state, layer)))) + ( + new On( + "pointRendering", + (layer) => + new Each( + new On( + "marker", + new Each(new ExpandMarkerRenderings(state, layer)) + ) + ) + ) ), - new On( - "pointRendering", - (layer) => new Each(new PreparePointRendering(state, layer)) + ( + new On( + "pointRendering", + (layer) => new Each(new PreparePointRendering(state, layer)) + ) ), new SetDefault("titleIcons", ["icons.defaults"]), new AddRatingBadge(), new AddFavouriteBadges(), new AutoTitleIcon(), - new On( - "titleIcons", - (layer) => - new Concat( new ExpandTagRendering(state, layer, { noHardcodedStrings: true })) + ( + new On( + "titleIcons", + (layer) => + new Concat( + ( + new ExpandTagRendering(state, layer, { + noHardcodedStrings: true, + }) + ) + ) + ) ), new AddFiltersFromTagRenderings(), new ExpandFilter(state), new MoveUnitConfigs(), new PruneFilters(), - new OrderLayer() + new OrderLayer() ) } diff --git a/src/Models/ThemeConfig/Conversion/PrevalidateLayer.ts b/src/Models/ThemeConfig/Conversion/PrevalidateLayer.ts index 3d7d11e04..80cb30bbe 100644 --- a/src/Models/ThemeConfig/Conversion/PrevalidateLayer.ts +++ b/src/Models/ThemeConfig/Conversion/PrevalidateLayer.ts @@ -156,8 +156,10 @@ export class PrevalidateLayer extends DesugaringStep { context.err("Layer " + json.id + " does not have an explicit 'allowMove'") } } - if(json.source["geojsonZoomLevel"]){ - context.enter("source").err("Use geoJsonZoomLevel (with capital J) instead of geojsonZoomLevel") + if (json.source["geojsonZoomLevel"]) { + context + .enter("source") + .err("Use geoJsonZoomLevel (with capital J) instead of geojsonZoomLevel") } if (context.hasErrors()) { diff --git a/src/Models/ThemeConfig/FilterConfig.ts b/src/Models/ThemeConfig/FilterConfig.ts index 767f2791d..866c4d7bd 100644 --- a/src/Models/ThemeConfig/FilterConfig.ts +++ b/src/Models/ThemeConfig/FilterConfig.ts @@ -224,13 +224,17 @@ export default class FilterConfig { public generateDocs(options?: { lang?: string }): string { const lang = options?.lang ?? "en" - const header = ["id", { en: "Question", nl: "Vraag" }, { - en: "Attributes", - nl: "Attributen", - }, + const header = [ + "id", + { en: "Question", nl: "Vraag" }, + { + en: "Attributes", + nl: "Attributen", + }, { en: "Fields", nl: "Velden" }, - ].map(x => typeof x === "string" ? new Translation({ "*": x }) : new Translation(x)) - .map(tr => tr.textFor(lang)) + ] + .map((x) => (typeof x === "string" ? new Translation({ "*": x }) : new Translation(x))) + .map((tr) => tr.textFor(lang)) const content: string[][] = [] for (let i = 0; i < this.options.length; i++) { @@ -239,7 +243,10 @@ export default class FilterConfig { content.push([ this.id + "." + i, opt.question.textFor(lang), - opt.osmTags?.asHumanString(true) ?? (new Translation({en: "_None - show all_", nl: "_Geen - toon alles_"})).textFor(lang), + opt.osmTags?.asHumanString(true) ?? + new Translation({ en: "_None - show all_", nl: "_Geen - toon alles_" }).textFor( + lang + ), opt.fields?.map((f) => f.name + " (" + f.type + ")").join(" "), ]) } diff --git a/src/Models/ThemeConfig/LayerConfig.ts b/src/Models/ThemeConfig/LayerConfig.ts index f85605fac..cbf357c52 100644 --- a/src/Models/ThemeConfig/LayerConfig.ts +++ b/src/Models/ThemeConfig/LayerConfig.ts @@ -422,13 +422,11 @@ export default class LayerConfig extends WithContextLoader { reusedTagRenderings?: Map lang?: string }): string { + const paragraphs: (string | Translation)[] = ["# " + this.id, this.description] - const paragraphs: (string | Translation)[] = [ - "# " + this.id, - this.description, - ] - - function add(item: Translation | string | Record & { en: string, nl: string }) { + function add( + item: Translation | string | (Record & { en: string; nl: string }) + ) { if (item instanceof Translation || typeof item === "string") { paragraphs.push(item) } else if (item["en"] !== undefined) { @@ -437,12 +435,12 @@ export default class LayerConfig extends WithContextLoader { } if (this._basedOn) { - add(new TypedTranslation<{ basedOn }>( - { + add( + new TypedTranslation<{ basedOn }>({ en: `This layer is based on [{basedOn}](../Layers/{basedOn}.md)`, nl: `Deze laag is gebaseerd op [{basedOn}](../Layers/{basedOn}.md)`, - }, - ).Subs({ basedOn: this._basedOn })) + }).Subs({ basedOn: this._basedOn }) + ) } // Various extra properties, added in a list below the description @@ -452,18 +450,16 @@ export default class LayerConfig extends WithContextLoader { new TypedTranslation<{ minzoom }>({ en: "This layer is shown at zoomlevel **{minzoom}** and higher", nl: "Deze laag wordt getoond vanaf zoomlevel **{minzoom}**", - }).Subs(this)) + }).Subs(this) + ) if (canBeIncluded) { if (addedByDefault) { extraProps.push( - new Translation( - { - en: - "**This layer is included automatically in every theme. This layer might contain no points**", - nl: "**Deze laag wordt automatisch toegevoegd aan ieder kaartthema. Deze laag bevat mogelijks geen punten", - }, - ), + new Translation({ + en: "**This layer is included automatically in every theme. This layer might contain no points**", + nl: "**Deze laag wordt automatisch toegevoegd aan ieder kaartthema. Deze laag bevat mogelijks geen punten", + }) ) } if (this.shownByDefault === false) { @@ -471,34 +467,31 @@ export default class LayerConfig extends WithContextLoader { new Translation({ en: "This layer is not visible by default and must be enabled in the filter by the user. ", nl: "Deze laag is standaard niet zichtbaar en moet door de gebruiker aangezet worden in het 'filter'-menu ", - }), + }) ) } if (this.title === undefined) { extraProps.push( new Translation({ - en: - "This layers doesn't have a title set. As such, elements will appear on the map but cannot be clicked. If you import this layer in your theme, override `title` to make sure elements can be opened." - , + en: "This layers doesn't have a title set. As such, elements will appear on the map but cannot be clicked. If you import this layer in your theme, override `title` to make sure elements can be opened.", nl: "Deze laag heeft geen 'title' ingesteld. Elementen zullen op de kaart tonen, maar kunnen niet opengeklikt worden door de gebruiker. Hergebruik je deze laag? Voeg een `title` toe zodat element wel opengeklikt kunnen worden.", - }), + }) ) } if (this.name === undefined && this.shownByDefault === false) { if (this.shownByDefault === false) { - extraProps.push( new TypedTranslation<{ id }>({ en: "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 adding the URL-parameter `layer-{id}=true`", nl: "Deze laag is standaard niet zichtbaar én heeft geen `name`. Dit betekent dat de zichtbaarheid niet door de gebruiker ingesteld kan worden, wat in een volledig verborgen laag resulteert. Dit kan nuttig zij, bv. om metatags uit te rekenen. Wil je deze laag toch tonen (bv om te debuggen?)? Voeg de URL-parameter `layer-{id}=true` toe", - }).Subs(this), + }).Subs(this) ) } else { extraProps.push( new Translation({ en: "Not visible in the layer selection by default. If you want to make this layer toggable, override `name`", nl: "Deze laag kan niet onzichtbaar gemaakt worden in het filtermenu. Overschrijf `name` indien je dit wel wilt.", - }), + }) ) } } @@ -507,40 +500,35 @@ export default class LayerConfig extends WithContextLoader { new Translation({ en: "Not rendered on the map by default.", nl: "Deze laag wordt standaard niet weergegeven op de kaart.", - }), + }) ) } - } else { extraProps.push( new Translation({ en: "This layer can **not** be included in a theme. It is solely used by [special renderings](SpecialRenderings.md) showing a minimap with custom data.", nl: "Deze laag kan **niet** aan een kaartthema toegevoegd worden. Deze laag wordt enkel en alleen gebruikt om een [speciale rendering](SpecialRenderings.md) te ondersteunen. ", - }), + }) ) } - for (const dep of dependencies) { extraProps.push( - new TypedTranslation<{ neededLayer, reason, context? }>({ + new TypedTranslation<{ neededLayer; reason; context? }>({ en: "This layer will automatically load [`{neededLayer}`](./{neededLayer}.md) into the theme as it depends on it: {reason} ({context??no context given})", nl: "Deze laag laadt automatisch de laag [`{neededLayer}`](./{neededLayer}.md) in het kaartthema want deze laag steunt hierop: {reason} ({context??geen context gekend})", - - }).Subs(dep), + }).Subs(dep) ) } for (const revDep of Lists.dedup(layerIsNeededBy?.get(this.id) ?? [])) { extraProps.push( new TypedTranslation<{ revDep }>({ en: "This layer is needed as dependency for layer [`{revDep}`](#{revDep})", - }).Subs({ revDep }), + }).Subs({ revDep }) ) } - - add(MarkdownUtils.list(extraProps.map(tr => tr.textFor(lang)))) - + add(MarkdownUtils.list(extraProps.map((tr) => tr.textFor(lang)))) } // Overview of what themes use this layer @@ -548,16 +536,20 @@ export default class LayerConfig extends WithContextLoader { if (!addedByDefault) { if (usedInThemes?.length > 0) { add( - MarkdownUtils.title(2, + MarkdownUtils.title( + 2, new Translation({ en: "Themes using this layer", nl: "Kaartthemas die deze laag gebruiken", - })), + }) + ) ) add( MarkdownUtils.list( - (usedInThemes ?? []).map((id) => `[\`${id}\`](https://mapcomplete.org/${id})`), - ), + (usedInThemes ?? []).map( + (id) => `[\`${id}\`](https://mapcomplete.org/${id})` + ) + ) ) } else if (this.source !== null) { add({ @@ -570,85 +562,75 @@ export default class LayerConfig extends WithContextLoader { // Data source information { - paragraphs.push(MarkdownUtils.title(2, new Translation({ en: "Data source", nl: "Databron" }))) + paragraphs.push( + MarkdownUtils.title(2, new Translation({ en: "Data source", nl: "Databron" })) + ) if (this.source?.geojsonSource) { paragraphs.push( new TypedTranslation<{ geojsonSource? }>({ - en: "⚠️ This layer is loaded from an external source, namely `{geojsonSource}`", - nl: "⚠️ Deze laag wordt van een externe bron geladen, namelijk `{geojsonSource}`", - }, - ).Subs(this.source), + en: "⚠️ This layer is loaded from an external source, namely `{geojsonSource}`", + nl: "⚠️ Deze laag wordt van een externe bron geladen, namelijk `{geojsonSource}`", + }).Subs(this.source) ) } else if (!this.source) { add({ en: "This is a special layer, probably a library layer or support layer for MapComplete", nl: "Dit is een speciale laag, waarschijnlijk een bibliotheeklaag of een ondersteunende laag", }) - } else { const neededTags = this.source.osmTags.optimize() if (neededTags["and"]) { - add( - { - en: "Elements on this layer match **all** of the following expressions:", - nl: "Elementen in deze laag hebben **alle** van de volgende kenmerken:", - - }, - ) + add({ + en: "Elements on this layer match **all** of the following expressions:", + nl: "Elementen in deze laag hebben **alle** van de volgende kenmerken:", + }) const parts = neededTags["and"] - add( - parts.map((p, i) => i + ". " + p.asHumanString(true, false, {})).join("\n"), - ) + add(parts.map((p, i) => i + ". " + p.asHumanString(true, false, {})).join("\n")) } else if (neededTags["or"]) { const parts = neededTags["or"] - add( - { - en: "Elements on this layer match **any** of the following expressions:", - nl: "Elementen in deze laag hebben **minstens één** van de volgende kenmerken:", - - }) - add( - parts.map((p) => " - " + p.asHumanString(true, false, {})).join("\n"), - ) + add({ + en: "Elements on this layer match **any** of the following expressions:", + nl: "Elementen in deze laag hebben **minstens één** van de volgende kenmerken:", + }) + add(parts.map((p) => " - " + p.asHumanString(true, false, {})).join("\n")) } else { add({ en: "Elements on this layer match the following expression:", nl: "Elementen in deze laag hebben de volgende kenmerken:", - }) - add( - neededTags.asHumanString(true, false, {}), - ) + add(neededTags.asHumanString(true, false, {})) } - - const link = Overpass.AsOverpassTurboLink(this.source.osmTags.optimize()) + const link = Overpass.AsOverpassTurboLink( + this.source.osmTags.optimize() + ) .replaceAll("(", "%28") .replaceAll(")", "%29") - const txt = new TypedTranslation<{ link }>( - { - en: "Execute on overpass-turbo.eu", - nl: "Uitvoeren op overpass-turbo.eu", - }, - ).textFor(lang) + const txt = new TypedTranslation<{ link }>({ + en: "Execute on overpass-turbo.eu", + nl: "Uitvoeren op overpass-turbo.eu", + }).textFor(lang) paragraphs.push(`[🗺️ ${txt}](${link})`) } - } // Presets { if (this.presets.length > 0) { - add(new Translation({ - en: "## Presets", - nl: "## Nieuwe punten toevoegen", - })) + add( + new Translation({ + en: "## Presets", + nl: "## Nieuwe punten toevoegen", + }) + ) - add(new Translation({ - en: "The following options to create new points are included:", - nl: "De volgende opties bestaan om nieuwe punten toe te voegen:", - })) + add( + new Translation({ + en: "The following options to create new points are included:", + nl: "De volgende opties bestaan om nieuwe punten toe te voegen:", + }) + ) const hTags = new Translation({ en: "Used tags", @@ -667,28 +649,24 @@ export default class LayerConfig extends WithContextLoader { nl: "Beschrijving", }) - add(MarkdownUtils.table( - [ - hTags, - hTitle, - hSnaps, - hDescription, - ].map(t => t.textFor(lang)), - this.presets.map(preset => { - let category = preset.title.textFor(lang) - category = "**" + category + "**" - return [ - new And(preset.tags).asHumanString(true), - Translations.t.general.add.addNew.Subs({ category }).textFor(lang), - preset.preciseInput?.snapToLayers - ?.map((id) => `[\`${id}\`](./Layers/${id}.md)`) - ?.join(", "), - preset.description?.textFor(lang), - ] - }), - { dropEmptyColumns: true }, - )) - + add( + MarkdownUtils.table( + [hTags, hTitle, hSnaps, hDescription].map((t) => t.textFor(lang)), + this.presets.map((preset) => { + let category = preset.title.textFor(lang) + category = "**" + category + "**" + return [ + new And(preset.tags).asHumanString(true), + Translations.t.general.add.addNew.Subs({ category }).textFor(lang), + preset.preciseInput?.snapToLayers + ?.map((id) => `[\`${id}\`](./Layers/${id}.md)`) + ?.join(", "), + preset.description?.textFor(lang), + ] + }), + { dropEmptyColumns: true } + ) + ) } else { add({ en: "This layer does not allowing adding new points", @@ -699,26 +677,33 @@ export default class LayerConfig extends WithContextLoader { // Supported attributes table { - const keyValues = (Lists.noNull(this.tagRenderings - .map((tr) => tr.freeformValues()) - .filter((values) => values !== undefined) - .filter((values) => values.key !== "id"))) - keyValues.sort((a, b) => a.key < b.key ? -1 : 1) + const keyValues = Lists.noNull( + this.tagRenderings + .map((tr) => tr.freeformValues()) + .filter((values) => values !== undefined) + .filter((values) => values.key !== "id") + ) + keyValues.sort((a, b) => (a.key < b.key ? -1 : 1)) if (keyValues.length > 0) { - add(MarkdownUtils.title(2, new Translation({ - en: "Attribute overview", - nl: "Attributenoverzicht", - }))) + add( + MarkdownUtils.title( + 2, + new Translation({ + en: "Attribute overview", + nl: "Attributenoverzicht", + }) + ) + ) add({ en: "This table gives an overview of most OpenStreetMap [keys](https://wiki.openstreetmap.org/wiki/Tags) that this layer shows and/or edits", nl: "Deze tabel geeft een overzicht van de meeste OpenStreetMap [sleutels](https://wiki.openstreetmap.org/wiki/Tags) die deze laag toont en/of aanpast", }) const tableRows: string[][] = keyValues.map((values) => { const embedded: string[] = values.values?.map((v) => - OsmWiki.constructLinkMd(values.key, v), + OsmWiki.constructLinkMd(values.key, v) ) ?? ["_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 [ @@ -734,55 +719,71 @@ export default class LayerConfig extends WithContextLoader { ] }) - const header: { en: string, nl: string }[] = [ - { en: "Key", nl: "OSM-sleutel" }, { + const header: { en: string; nl: string }[] = [ + { en: "Key", nl: "OSM-sleutel" }, + { en: "Type (for freeform input)", nl: "Inputtype", - }, { en: "Predefined, supported options", nl: "Voorgedefinieerde, ondersteunde opties" }, + }, + { + en: "Predefined, supported options", + nl: "Voorgedefinieerde, ondersteunde opties", + }, ] add( - MarkdownUtils.table( - header.map(tr => new Translation(tr).textFor(lang)), - tableRows - ) + MarkdownUtils.table( + header.map((tr) => new Translation(tr).textFor(lang)), + tableRows + ) ) + } } - } - - // Elements in the popup { - add(MarkdownUtils.title(2, new Translation({ - en: "Overview of questions (and other elements) in the popup", - nl: "Overzicht van vragen (en andere elementen) in de popup", - }))) + add( + MarkdownUtils.title( + 2, + new Translation({ + en: "Overview of questions (and other elements) in the popup", + nl: "Overzicht van vragen (en andere elementen) in de popup", + }) + ) + ) for (const tagRendering of this.tagRenderings) { if (tagRendering.labels.indexOf("ignore_docs") >= 0) { continue } - add(tagRendering.generateDocumentation( - this.id, - lang, - reusedTagRenderings?.get(tagRendering.id)?.map((l) => l.layer), - )) + add( + tagRendering.generateDocumentation( + this.id, + lang, + reusedTagRenderings?.get(tagRendering.id)?.map((l) => l.layer) + ) + ) } } // Filters if (this.filters.length > 0) { - add(MarkdownUtils.title(2, new Translation({ - en: "Filters", - nl: "Filters" - }))) + add( + MarkdownUtils.title( + 2, + new Translation({ + en: "Filters", + nl: "Filters", + }) + ) + ) for (const filter of this.filters) { - add(filter.generateDocs({lang})) + add(filter.generateDocs({ lang })) } } - - return (Lists.noEmpty(Lists.noNull(paragraphs).map(p => typeof p === "string" ? p : p.textFor(lang)))).join("\n\n") + return Lists.noEmpty( + Lists.noNull(paragraphs).map((p) => (typeof p === "string" ? p : p.textFor(lang))) + ).join("\n\n") } public CustomCodeSnippets(): string[] { diff --git a/src/Models/ThemeConfig/TagRenderingConfig.ts b/src/Models/ThemeConfig/TagRenderingConfig.ts index 07fa5be4c..20abc5741 100644 --- a/src/Models/ThemeConfig/TagRenderingConfig.ts +++ b/src/Models/ThemeConfig/TagRenderingConfig.ts @@ -678,7 +678,7 @@ export default class TagRenderingConfig { } catch (e) { return undefined } - }) ?? [], + }) ?? [] ) if (values.length === 0) { return @@ -967,15 +967,22 @@ export default class TagRenderingConfig { } } - generateDocumentation(currentLayerId: string, lang: string = "en", usedInLayers?: string[]): string { - let paragraphs: (Translation | string) [] = ["### " + this.id, - this.description] + generateDocumentation( + currentLayerId: string, + lang: string = "en", + usedInLayers?: string[] + ): string { + let paragraphs: (Translation | string)[] = ["### " + this.id, this.description] if (this.question === undefined) { - paragraphs.push(MarkdownUtils.quote(new Translation({ - en: "_This tagrendering has no question and is thus read-only_", - nl: "_Deze tagRendering heeft geen vraag en wordt dus enkel weergegeven_", - }))) + paragraphs.push( + MarkdownUtils.quote( + new Translation({ + en: "_This tagrendering has no question and is thus read-only_", + nl: "_Deze tagRendering heeft geen vraag en wordt dus enkel weergegeven_", + }) + ) + ) } else { paragraphs.push(MarkdownUtils.quote(this.question)) } @@ -983,33 +990,39 @@ export default class TagRenderingConfig { if (this.render) { if (this.freeform?.key) { const render = "*" + this.render.textFor(lang) + "*" - paragraphs.push(new TypedTranslation<{ render, key }>({ - en: "*{render}* is shown if `{key}` is exists in the object", - nl: "*{render}* wordt getoond indien het attribuut `{key}` bestaat", - }).Subs({ render, key: this.freeform.key })) + paragraphs.push( + new TypedTranslation<{ render; key }>({ + en: "*{render}* is shown if `{key}` is exists in the object", + nl: "*{render}* wordt getoond indien het attribuut `{key}` bestaat", + }).Subs({ render, key: this.freeform.key }) + ) } else { paragraphs.push(this.render) } if (this.question && this.freeform.range) { - let paragraph: (string)[] = [ + let paragraph: string[] = [ new TypedTranslation<{ type?: string }>({ en: "The allowed input is of type {type??string}", nl: "Input moet van het type {type} zijn", - }).Subs(this.freeform).textFor(lang), + }) + .Subs(this.freeform) + .textFor(lang), ] if (this.freeform.range) { paragraph.push( - new TypedTranslation( - { - en: "Values must be between {min??negative infinity} and {max??infinity} (both inclusive)", - nl: "Waardes moeten tussen {min??min oneindig} en {max??oneindig} vallen (beiden inclusief)", - }, - ).Subs(this.freeform.range).textFor(lang), + new TypedTranslation({ + en: "Values must be between {min??negative infinity} and {max??infinity} (both inclusive)", + nl: "Waardes moeten tussen {min??min oneindig} en {max??oneindig} vallen (beiden inclusief)", + }) + .Subs(this.freeform.range) + .textFor(lang) ) const r = this.freeform.range if (r.warnAbove && r.warnBelow) { - paragraph.push(`A warning will appear if the value is outside of ${r.warnBelow} and ${r.warnAbove}.`) + paragraph.push( + `A warning will appear if the value is outside of ${r.warnBelow} and ${r.warnAbove}.` + ) } else if (r.warnBelow) { paragraph.push(`A warning will appear below ${r.warnBelow}.`) } else if (r.warnAbove) { @@ -1021,44 +1034,51 @@ export default class TagRenderingConfig { } if (this.mappings !== undefined) { - paragraphs.push(MarkdownUtils.list( - this.mappings.map((m) => { - let icon = "" - if (m.icon?.indexOf(";") < 0) { - if (Strings.isEmoji(m.icon)) { - icon = m.icon - } else { - icon = "" + paragraphs.push( + MarkdownUtils.list( + this.mappings.map((m) => { + let icon = "" + if (m.icon?.indexOf(";") < 0) { + if (Strings.isEmoji(m.icon)) { + icon = m.icon + } else { + icon = + "" + } } - } - const msgs: Translation[] = [ - new TypedTranslation<{ icon, then, cond }>({ - en: "{icon} *{then}* is shown if {cond}", - nl: "{icon} *{then}* wordt getoond als {cond}", - }).Subs({ icon, then: m.then.textFor(lang), cond: m.if.asHumanString(true) }), - ] + const msgs: Translation[] = [ + new TypedTranslation<{ icon; then; cond }>({ + en: "{icon} *{then}* is shown if {cond}", + nl: "{icon} *{then}* wordt getoond als {cond}", + }).Subs({ + icon, + then: m.then.textFor(lang), + cond: m.if.asHumanString(true), + }), + ] - if (m.hideInAnswer === true) { - msgs.push(new Translation( - { - en: "_This option cannot be chosen as answer_", - nl: "_Deze optie kan niet als antwoord gekozen worden_", - })) - } - if (m.ifnot !== undefined) { - msgs.push( - new TypedTranslation<{ ifnot }>({ - en: "If _not_ selected when answering, {ifnot} will be added", - nl: "Indien _niet_ geselecteerd bij het antwoorden, zal {ifnot} toegevoegd worden", - }).Subs({ ifnot: m.ifnot.asHumanString(true) }), - - ) - } - return msgs.map(tr => tr.textFor(lang)).join(". ") - }) - )) + if (m.hideInAnswer === true) { + msgs.push( + new Translation({ + en: "_This option cannot be chosen as answer_", + nl: "_Deze optie kan niet als antwoord gekozen worden_", + }) + ) + } + if (m.ifnot !== undefined) { + msgs.push( + new TypedTranslation<{ ifnot }>({ + en: "If _not_ selected when answering, {ifnot} will be added", + nl: "Indien _niet_ geselecteerd bij het antwoorden, zal {ifnot} toegevoegd worden", + }).Subs({ ifnot: m.ifnot.asHumanString(true) }) + ) + } + return msgs.map((tr) => tr.textFor(lang)).join(". ") + }) + ) + ) } if (this.condition !== undefined && !this.condition?.matchesProperties({})) { @@ -1067,12 +1087,12 @@ export default class TagRenderingConfig { false, {} ) - paragraphs.push(new TypedTranslation<{ conditionAsLink }>( - { + paragraphs.push( + new TypedTranslation<{ conditionAsLink }>({ en: "This tagRendering is only visible in the information panel if the condition *{conditionAsLink}* is met", nl: "Deze tagRendering is enkel zichtbaar in het informatiepaneel indien de voorwaarde *{conditionAsLink}* vervuld is", - }, - ).Subs({ conditionAsLink })) + }).Subs({ conditionAsLink }) + ) } if (this.invalidValues) { @@ -1080,18 +1100,18 @@ export default class TagRenderingConfig { new Translation({ en: "This tagRendering has some values that are not valid and cannot be entered. The following values are _not_ accepted:", nl: "Deze tagRendering heeft waardes die als ongeldig beschouwd worden en _niet_ ingevoerd kunnen worden:", - }), - ) - paragraphs.push( - "❌ " + this.invalidValues.asHumanString(true), + }) ) + paragraphs.push("❌ " + this.invalidValues.asHumanString(true)) } if (this.labels?.length > 0) { - paragraphs.push(new Translation({ - en: "This tagRendering has the following labels:", - nl: "Deze tagRendering heeft de volgende labels:", - })) + paragraphs.push( + new Translation({ + en: "This tagRendering has the following labels:", + nl: "Deze tagRendering heeft de volgende labels:", + }) + ) paragraphs.push(MarkdownUtils.list(this.labels.map((label) => "`" + label + "`"))) } if (usedInLayers?.length > 0) { @@ -1099,32 +1119,31 @@ export default class TagRenderingConfig { new TypedTranslation<{ length }>({ en: "This tagrendering is reused in {length} other layers:", nl: "Deze tagRendering wordt ook hergebruikt in {length} andere lagen:", - }).Subs(usedInLayers), + }).Subs(usedInLayers) ) paragraphs.push( - MarkdownUtils.list(usedInLayers.map((l) => `[${l}](../Docs/Layers/${l}.md)`)), + MarkdownUtils.list(usedInLayers.map((l) => `[${l}](../Docs/Layers/${l}.md)`)) ) } - if (this._definedIn) { const [layer, id] = this._definedIn if (this.id === id && currentLayerId === id) { // pass // It is defined right here } else { - - paragraphs.push(new TypedTranslation( - { + paragraphs.push( + new TypedTranslation({ en: "Originally defined in [{layer}](../Docs/Layers/{layer}.md#{id})", nl: "Oorspronkelijk gedefinieerd in [`{layer}`](../Docs/Layers/{layer}.md#{id})", - }, - ).Subs({ layer, id })) + }).Subs({ layer, id }) + ) } } - return Lists.noNull(paragraphs).map(tr => typeof tr === "string" ? tr : tr.textFor(lang)).join("\n\n") - + return Lists.noNull(paragraphs) + .map((tr) => (typeof tr === "string" ? tr : tr.textFor(lang))) + .join("\n\n") } public usedTags(): TagsFilter[] { diff --git a/src/UI/Base/SelectedElementPanel.svelte b/src/UI/Base/SelectedElementPanel.svelte index 8cfe8323c..bd587e2dc 100644 --- a/src/UI/Base/SelectedElementPanel.svelte +++ b/src/UI/Base/SelectedElementPanel.svelte @@ -21,10 +21,10 @@ let tags = state.featureProperties.getStore(selected.properties.id) export let absolute = true - let layerCorrect = tags.map(properties => state.getMatchingLayer(properties)) + let layerCorrect = tags.map((properties) => state.getMatchingLayer(properties)) let layer: LayerConfig - layerCorrect.addCallbackAndRun(l => { + layerCorrect.addCallbackAndRun((l) => { if (layer) { layer = undefined window.setTimeout(() => { @@ -33,19 +33,29 @@ } else { layer = l } - }) - + {#if !layer || $tags._deleted === "yes"} -
+
{#if $layerCorrect === undefined || $tags._deleted === "yes"} -
+
- {$tags.id} - state.selectedElement.setData(undefined)}> + + {$tags.id} + + state.selectedElement.setData(undefined)} + > {:else} @@ -55,7 +65,7 @@ {:else}
- - + +
{/if} diff --git a/src/UI/Base/SidebarUnit.svelte b/src/UI/Base/SidebarUnit.svelte index 95a1042ca..fa84a10d7 100644 --- a/src/UI/Base/SidebarUnit.svelte +++ b/src/UI/Base/SidebarUnit.svelte @@ -2,7 +2,7 @@ export let clss = "" -
+
diff --git a/src/UI/Base/TableOfContents.ts b/src/UI/Base/TableOfContents.ts index 99961bd8c..d5d560d44 100644 --- a/src/UI/Base/TableOfContents.ts +++ b/src/UI/Base/TableOfContents.ts @@ -66,9 +66,15 @@ export default class TableOfContents { } const heading = Utils.times(() => "#", firstTitle.depth) - toc = heading +" " + new Translation({ - en: "Table of contents",nl: "Inhoudsopgave" - }).textFor(lang) + "\n\n" + toc + toc = + heading + + " " + + new Translation({ + en: "Table of contents", + nl: "Inhoudsopgave", + }).textFor(lang) + + "\n\n" + + toc const firstTitleIndex = md.indexOf(firstTitle.title) diff --git a/src/UI/BigComponents/CompassWidget.svelte b/src/UI/BigComponents/CompassWidget.svelte index 6dd066ff2..ed12f4327 100644 --- a/src/UI/BigComponents/CompassWidget.svelte +++ b/src/UI/BigComponents/CompassWidget.svelte @@ -24,7 +24,7 @@ }) let mapRotation = mapProperties.rotation let showHint = new UIEventSource(false) - showHint.stabilized(4000).addCallback(isShown => { + showHint.stabilized(4000).addCallback((isShown) => { if (isShown) { showHint.set(false) } @@ -51,19 +51,23 @@ } } - let orientationText = mapProperties.rotation.mapD(r => { + let orientationText = mapProperties.rotation.mapD((r) => { const key = GeoOperations.bearingToHuman(r) return Translations.t.compass[key] }) let deviceOrientation = Orientation.singleton.alpha - {#if $allowRotation || $gotNonZero} - {/if} - {OpeningHours.hhmm(range.startHour, range.startMinutes)} + {OpeningHours.hhmm(range.startHour, range.startMinutes)} {#if range.endHour - range.startHour > 1} - {/if} @@ -76,43 +76,47 @@ - - -
+
{#if range.endHour - range.startHour > 1} - {/if} - - {OpeningHours.hhmm(range.endHour, range.endMinutes)} + {OpeningHours.hhmm(range.endHour, range.endMinutes)} {#if range.endHour != 24} - {/if}
-
- {:else} -
+
{#if range.endHour - range.startHour >= 3} {OpeningHours.hhmm(range.startHour, range.startMinutes)} {/if} @@ -121,5 +125,4 @@ {OpeningHours.hhmm(range.endHour, range.endMinutes)} {/if}
- {/if} diff --git a/src/UI/InputElement/Helpers/OpeningHours/OHTable.svelte b/src/UI/InputElement/Helpers/OpeningHours/OHTable.svelte index 8385b8bd7..6d8cbc33f 100644 --- a/src/UI/InputElement/Helpers/OpeningHours/OHTable.svelte +++ b/src/UI/InputElement/Helpers/OpeningHours/OHTable.svelte @@ -131,7 +131,6 @@ clearSelection() } - let lasttouched: [number, number] = undefined function moved(weekday: number, hour: number) { @@ -212,7 +211,7 @@ {#each range(7) as wd} - +