diff --git a/assets/layers/diets/diets.json b/assets/layers/diets/diets.json index 0bc3795600..cac1388a31 100644 --- a/assets/layers/diets/diets.json +++ b/assets/layers/diets/diets.json @@ -12,9 +12,16 @@ "hidden" ], "render": { - "en": "Dietary options", - "cs": "Dietní možnosti", - "nl": "Dieetopties" + "special": { + "type": "show_icons", + "labels": "diets_content", + "class": "inline-flex float-right" + }, + "before": { + "en": "Dietary options", + "cs": "Dietní možnosti", + "nl": "Dieetopties" + } } }, { diff --git a/assets/layers/historic_rolling_stock/historic_rolling_stock.json b/assets/layers/historic_rolling_stock/historic_rolling_stock.json index ffeea6dfbc..21eb1633f3 100644 --- a/assets/layers/historic_rolling_stock/historic_rolling_stock.json +++ b/assets/layers/historic_rolling_stock/historic_rolling_stock.json @@ -131,7 +131,8 @@ }, "tags": [ "historic=locomotive" - ] + ], + "snapToLayer": ["railway"] }, { "title": { @@ -144,7 +145,9 @@ }, "tags": [ "historic=railway_car" - ] + ], + "snapToLayer": ["railway"] + }, { "title": { @@ -157,7 +160,9 @@ }, "tags": [ "historic=minecart" - ] + ], + "snapToLayer": ["railway"] + } ], "tagRenderings": [ diff --git a/assets/layers/railway/railway.json b/assets/layers/railway/railway.json new file mode 100644 index 0000000000..50ddecf0ba --- /dev/null +++ b/assets/layers/railway/railway.json @@ -0,0 +1,66 @@ +{ + "id": "railway", + "name": { + "en": "Railway" + }, + "description": { + "en": "Railways and disused railways" + }, + "source": { + "osmTags": { + "or": [ + "railway=rail", + "railway=tram", + "railway=subway", + "railway=light_rail", + + "railway=disused", + + "disused:railway=rail", + "disused:railway=tram", + "disused:railway=subway", + "disused:railway=light_rail", + + "abandoned:railway=rail", + "abandoned:railway=tram", + "abandoned:railway=subway", + "abandoned:railway=light_rail" + ] + } + }, + "minzoom": 14, + "title": { + "render": { + "en": "Railway" + }, + "icon": "./assets/svg/train.svg" + }, + "pointRendering": [ + { + "location": [ + "point" + ], + "marker": [ + { + "icon": "circle" + } + ] + } + ], + "lineRendering": [ + { + "width": 1, + "color": "black" + } + ], + "tagRenderings": [ + "images" + ], + "allowMove": false, + "#": "In first instance to snap historic rolling stock", + "snapName": { + "en": "railway track" + }, + "credits": "mnalis ALTernative", + "credits:uid": 172435 +} diff --git a/assets/themes/historic_rolling_stock/historic_rolling_stock.json b/assets/themes/historic_rolling_stock/historic_rolling_stock.json index 0b22c6b2b9..077324f278 100644 --- a/assets/themes/historic_rolling_stock/historic_rolling_stock.json +++ b/assets/themes/historic_rolling_stock/historic_rolling_stock.json @@ -18,6 +18,14 @@ }, "icon": "./assets/layers/historic_rolling_stock/steam_locomotive.svg", "layers": [ - "historic_rolling_stock" + "historic_rolling_stock", + { + "builtin": "railway", + "override": { + "name": null, + "presets": null, + "shownByDefault": false + } + } ] } diff --git a/langs/layers/cs.json b/langs/layers/cs.json index 067e08c2a8..2b661de30c 100644 --- a/langs/layers/cs.json +++ b/langs/layers/cs.json @@ -4751,7 +4751,9 @@ "diets": { "tagRenderings": { "diets_title": { - "render": "Dietní možnosti" + "render": { + "before": "Dietní možnosti" + } }, "gluten_free": { "mappings": { diff --git a/langs/layers/en.json b/langs/layers/en.json index f6476e968c..6365bf5d67 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -4768,7 +4768,9 @@ "diets": { "tagRenderings": { "diets_title": { - "render": "Dietary options" + "render": { + "before": "Dietary options" + } }, "gluten_free": { "mappings": { @@ -10092,6 +10094,14 @@ } } }, + "railway": { + "description": "Railways and disused railways", + "name": "Railway", + "snapName": "railway track", + "title": { + "render": "Railway" + } + }, "railway_platforms": { "description": "Find every platform in the station, and the train routes that use them.", "name": "Railway Platforms", diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 03ff99a8e4..6e47acc32c 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -4561,7 +4561,9 @@ "diets": { "tagRenderings": { "diets_title": { - "render": "Dieetopties" + "render": { + "before": "Dieetopties" + } }, "gluten_free": { "mappings": { diff --git a/langs/themes/en.json b/langs/themes/en.json index a23dea4728..c2dd87052c 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -646,6 +646,13 @@ "grb-reference": { "render": "Has been imported from GRB, reference number is {source:geometry:ref}" } + }, + "title": { + "mappings": { + "0": { + "then": "Building part" + } + } } }, "1": { diff --git a/scripts/generateLayerOverview.ts b/scripts/generateLayerOverview.ts index ffd9e78cca..6414ed5f6c 100644 --- a/scripts/generateLayerOverview.ts +++ b/scripts/generateLayerOverview.ts @@ -37,15 +37,8 @@ import { Translatable } from "../src/Models/ThemeConfig/Json/Translatable" import { ValidateThemeAndLayers } from "../src/Models/ThemeConfig/Conversion/ValidateThemeAndLayers" import { ExtractImages } from "../src/Models/ThemeConfig/Conversion/FixImages" import { TagRenderingConfigJson } from "../src/Models/ThemeConfig/Json/TagRenderingConfigJson" -import { - LayerConfigDependencyGraph, - LevelInfo, -} from "../src/Models/ThemeConfig/LayerConfigDependencyGraph" import { Lists } from "../src/Utils/Lists" -import { - LayerConfigDependencyGraph, - LevelInfo, -} from "../src/Models/ThemeConfig/LayerConfigDependencyGraph" +import { LayerConfigDependencyGraph, LevelInfo } from "../src/Models/ThemeConfig/LayerConfigDependencyGraph" import { AddContextToTranslations } from "../src/Models/ThemeConfig/Conversion/AddContextToTranslations" // This scripts scans 'src/assets/layers/*.json' for layer definition files and 'src/assets/themes/*.json' for theme definition files. @@ -121,7 +114,7 @@ class AddIconSummary extends DesugaringStep<{ raw: LayerConfigJson; parsed: Laye static singleton = new AddIconSummary() constructor() { - super("AddIconSummary", "Adds an icon summary for quick reference") + super("AddIconSummary", "Adds an icon summary ('_layerIcon') for quick reference. This previews how the layer should be shown in e.g. the filter menu") } convert(json: { raw: LayerConfigJson; parsed: LayerConfig }) { @@ -706,8 +699,8 @@ class LayerOverviewUtils extends Script { ) const path = "assets/layers/questions/questions.json" - const sharedQuestionsRaw = this.parseLayer(doesImageExist, prepareLayer, path).raw - const sharedQuestions = new AddContextToTranslations("").convertStrict( + const sharedQuestionsRaw: LayerConfigJson = this.parseLayer(doesImageExist, prepareLayer, path).raw + const sharedQuestions: LayerConfigJson = new AddContextToTranslations("").convertStrict( sharedQuestionsRaw, ConversionContext.construct(["layers:questions"], []) ) diff --git a/src/Models/ThemeConfig/Json/LayerConfigJson.ts b/src/Models/ThemeConfig/Json/LayerConfigJson.ts index a541856b8b..1602f7a5a5 100644 --- a/src/Models/ThemeConfig/Json/LayerConfigJson.ts +++ b/src/Models/ThemeConfig/Json/LayerConfigJson.ts @@ -299,7 +299,7 @@ export interface LayerConfigJson { /** * Advanced option - might be set by the theme compiler * - * If true, this data will _always_ be loaded, even if the theme is disabled by a filter or hidden. + * If true, this data will _always_ be loaded, even if the layer is disabled by a filter or hidden. * The opposite of `doNotDownload` * * question: Should this layer be forcibly loaded? diff --git a/src/Models/ThemeConfig/TagRenderingConfig.ts b/src/Models/ThemeConfig/TagRenderingConfig.ts index 5f915cd424..da027a3499 100644 --- a/src/Models/ThemeConfig/TagRenderingConfig.ts +++ b/src/Models/ThemeConfig/TagRenderingConfig.ts @@ -523,7 +523,9 @@ export default class TagRenderingConfig { /** * Gets all the render values. Will return multiple render values if 'multianswer' is enabled. - * The result will equal [GetRenderValue] if not 'multiAnswer' + * The result will equal [GetRenderValue] if not 'multiAnswer'. + * + * If an iconsource is given, will return an icon too (but not necessarly an iconSize) * @param tags * @constructor */ diff --git a/src/UI/Popup/DataVisualisations.ts b/src/UI/Popup/DataVisualisations.ts index d85ef9e71d..97fd3c10d3 100644 --- a/src/UI/Popup/DataVisualisations.ts +++ b/src/UI/Popup/DataVisualisations.ts @@ -1,4 +1,9 @@ -import { SpecialVisualisationParams, SpecialVisualization, SpecialVisualizationSvelte } from "../SpecialVisualization" +import { + SpecialVisualisationArg, + SpecialVisualisationParams, + SpecialVisualization, + SpecialVisualizationSvelte, +} from "../SpecialVisualization" import { HistogramViz } from "./HistogramViz" import { Store } from "../../Logic/UIEventSource" import BaseUIElement from "../BaseUIElement" @@ -22,6 +27,9 @@ import TagRenderingEditable from "./TagRendering/TagRenderingEditable.svelte" import AllTagsPanel from "./AllTagsPanel/AllTagsPanel.svelte" import CollectionTimes from "../CollectionTimes/CollectionTimes.svelte" import Tr from "../Base/Tr.svelte" +import Combine from "../Base/Combine" +import Marker from "../Map/Marker.svelte" +import { twJoin } from "tailwind-merge" class DirectionIndicatorVis extends SpecialVisualizationSvelte { funcName = "direction_indicator" @@ -209,6 +217,7 @@ class PresetDescription extends SpecialVisualizationSvelte { docs = "Shows the extra description from the presets of the layer, if one matches. It will pick the most specific one (e.g. if preset `A` implies `B`, but `B` does not imply `A`, it'll pick B) or the first one if no ordering can be made. Might be empty" args = [] + group = "UI" constr({ state, tags }: SpecialVisualisationParams): SvelteUIElement { const translation = tags.map((tags) => { @@ -223,6 +232,7 @@ class PresetTypeSelect extends SpecialVisualizationSvelte { funcName = "preset_type_select" docs = "An editable tag rendering which allows to change the type" args = [] + group = "ui" constr({ state, tags, feature, layer }: SpecialVisualisationParams,): SvelteUIElement { const t = Translations.t.preset_type @@ -281,7 +291,7 @@ class PointsInTimeVis extends SpecialVisualization { }, ] - constr( {tags, args}: SpecialVisualisationParams): BaseUIElement { + constr({ tags, args }: SpecialVisualisationParams): BaseUIElement { const key = args[0] const points_in_time = tags.map((tags) => tags[key]) const times = points_in_time.map( @@ -294,6 +304,60 @@ class PointsInTimeVis extends SpecialVisualization { } } +class KnownIcons extends SpecialVisualization { + docs = "Displays all icons from the specified tagRenderings (if they are known and have an icon) together, e.g. to give a summary of the dietary options" + needsUrls = [] + group = "UI" + funcName = "show_icons" + args: SpecialVisualisationArg[] = [{ + name: "labels", + doc: "A ';'-separated list of labels and/or ids of tagRenderings", + type: "key", + required: true, + }, { + name: "class", + doc: "CSS-classes of the container, space-separated", + type: "css", + required: false, + defaultValue: "inline-flex mx-4", + }] + + private static readonly emojiHeights = { + small: "2rem", + medium: "3rem", + large: "5rem", + } + + constr(options: SpecialVisualisationParams): BaseUIElement { + const labels = new Set(options.args[0].split(";").map(s => s.trim())) + const matchingTrs = options.layer.tagRenderings.filter( + tr => labels.has(tr.id) || tr.labels.some(l => labels.has(l)), + ) + return new VariableUiElement(options.tags.map(tags => + new Combine(matchingTrs.map(tr => { + const mapping = tr.GetRenderValueWithImage(tags) + if (!mapping?.icon) { + return undefined + } + + return new SvelteUIElement(Marker, { + emojiHeight: KnownIcons.emojiHeights[mapping.iconClass] ?? "2rem", + clss: `mapping-icon-${mapping.iconClass ?? "small"}`, + icons: mapping.icon, + size: twJoin( + "shrink-0", + `mapping-icon-${mapping.iconClass ?? "small"}-height mapping-icon-${ + mapping.iconClass ?? "small" + }-width`), + + + }) + }) + ).SetClass(options.args[1] ?? "inline-flex mx-4") + )) + } +} + export class DataVisualisations { public static initList(): SpecialVisualization[] { return [ @@ -309,6 +373,7 @@ export class DataVisualisations { new PresetDescription(), new PresetTypeSelect(), new AllTagsVis(), + new KnownIcons(), ] } } diff --git a/src/UI/SpecialVisualisations/TagrenderingManipulationSpecialVisualisations.ts b/src/UI/SpecialVisualisations/TagrenderingManipulationSpecialVisualisations.ts index b997417f77..bf082f3df3 100644 --- a/src/UI/SpecialVisualisations/TagrenderingManipulationSpecialVisualisations.ts +++ b/src/UI/SpecialVisualisations/TagrenderingManipulationSpecialVisualisations.ts @@ -213,7 +213,7 @@ class OpenInId extends SpecialVisualizationSvelte { funcName = "open_in_iD" docs = "Opens the current view in the iD-editor" args = [] - group = "tagrendering_manipulation" + group = "web_and_communication" constr({state, feature}: SpecialVisualisationParams): SvelteUIElement { return new SvelteUIElement(OpenIdEditor, { @@ -225,7 +225,7 @@ class OpenInId extends SpecialVisualizationSvelte { class OpenInJosm extends SpecialVisualizationSvelte { funcName = "open_in_josm" - group = "tagrendering_manipulation" + group = "web_and_communication" docs = "Opens the current view in the JOSM-editor" args = [] needsUrls = [