diff --git a/Models/ThemeConfig/Conversion/PrepareLayer.ts b/Models/ThemeConfig/Conversion/PrepareLayer.ts index e8c8b9044e..399d7892e2 100644 --- a/Models/ThemeConfig/Conversion/PrepareLayer.ts +++ b/Models/ThemeConfig/Conversion/PrepareLayer.ts @@ -1,23 +1,65 @@ -import { - Concat, - Conversion, - DesugaringContext, - DesugaringStep, - Each, - FirstOf, - Fuse, - On, - SetDefault, -} from "./Conversion" -import { LayerConfigJson } from "../Json/LayerConfigJson" -import { TagRenderingConfigJson } from "../Json/TagRenderingConfigJson" -import { Utils } from "../../../Utils" +import {Concat, Conversion, DesugaringContext, DesugaringStep, Each, FirstOf, Fuse, On, SetDefault,} from "./Conversion" +import {LayerConfigJson} from "../Json/LayerConfigJson" +import {TagRenderingConfigJson} from "../Json/TagRenderingConfigJson" +import {Utils} from "../../../Utils" import RewritableConfigJson from "../Json/RewritableConfigJson" import SpecialVisualizations from "../../../UI/SpecialVisualizations" import Translations from "../../../UI/i18n/Translations" -import { Translation } from "../../../UI/i18n/Translation" +import {Translation} from "../../../UI/i18n/Translation" import * as tagrenderingconfigmeta from "../../../assets/tagrenderingconfigmeta.json" -import { AddContextToTranslations } from "./AddContextToTranslations" +import {AddContextToTranslations} from "./AddContextToTranslations" +import FilterConfigJson from "../Json/FilterConfigJson"; +import * as predifined_filters from "../../../assets/layers/filters/filters.json" + +class ExpandFilter extends DesugaringStep{ + + + private static load_filters(): Map{ + let filters = new Map(); + for (const filter of (predifined_filters.filter)) { + filters.set(filter.id, filter) + } + return filters; + } + + private static readonly predefinedFilters = ExpandFilter.load_filters(); + + constructor() { + super("Expands filters: replaces a shorthand by the value found in 'filters.json'", ["filter"], "ExpandFilter"); + } + + convert(json: LayerConfigJson, context: string): { result: LayerConfigJson; errors?: string[]; warnings?: string[]; information?: string[] } { + if(json.filter === undefined || json.filter === null){ + return {result: json} // Nothing to change here + } + + if( json.filter["sameAs"] !== undefined){ + return {result: json} // Nothing to change here + } + + const newFilters : FilterConfigJson[] = [] + const errors :string[]= [] + for (const filter of (<(FilterConfigJson|string)[]> json.filter)) { + if (typeof filter !== "string") { + newFilters.push(filter) + continue + } + // Search for the filter: + const found = ExpandFilter.predefinedFilters.get(filter) + if(found === undefined){ + const suggestions = Utils.sortedByLevenshteinDistance(filter, Array.from(ExpandFilter.predefinedFilters.keys()), t => t) + const err = context+".filter: while searching for predifined filter "+filter+": this filter is not found. Perhaps you meant one of: "+suggestions + errors.push(err) + } + newFilters.push(found) + } + return {result: { + ...json, filter: newFilters + }, errors}; + } + + +} class ExpandTagRendering extends Conversion< string | TagRenderingConfigJson | { builtin: string | string[]; override: any }, @@ -178,7 +220,7 @@ class ExpandTagRendering extends Conversion< if (lookup === undefined) { let candidates = Array.from(state.tagRenderings.keys()) if (name.indexOf(".") > 0) { - const [layerName, search] = name.split(".") + const [layerName] = name.split(".") let layer = state.sharedLayers.get(layerName) if (layerName === this._self.id) { layer = this._self @@ -699,7 +741,8 @@ export class PrepareLayer extends Fuse { ) ), new SetDefault("titleIcons", ["defaults"]), - new On("titleIcons", (layer) => new Concat(new ExpandTagRendering(state, layer))) + new On("titleIcons", (layer) => new Concat(new ExpandTagRendering(state, layer))), + new ExpandFilter() ) } } diff --git a/Models/ThemeConfig/Json/LayerConfigJson.ts b/Models/ThemeConfig/Json/LayerConfigJson.ts index 4c6b601a26..36dc8ea3d3 100644 --- a/Models/ThemeConfig/Json/LayerConfigJson.ts +++ b/Models/ThemeConfig/Json/LayerConfigJson.ts @@ -316,9 +316,10 @@ export interface LayerConfigJson { )[] /** - * All the extra questions for filtering + * All the extra questions for filtering. + * If a string is given, mapComplete will search in 'filters.json' for the appropriate filter */ - filter?: FilterConfigJson[] | { sameAs: string } + filter?: (FilterConfigJson | string)[] | { sameAs: string } /** * This block defines under what circumstances the delete dialog is shown for objects of this layer. diff --git a/Models/ThemeConfig/TagRenderingConfig.ts b/Models/ThemeConfig/TagRenderingConfig.ts index a191e9f26a..e12fdaa1aa 100644 --- a/Models/ThemeConfig/TagRenderingConfig.ts +++ b/Models/ThemeConfig/TagRenderingConfig.ts @@ -267,6 +267,9 @@ export default class TagRenderingConfig { if (this.freeform.key === "wikidata" && txt.indexOf("{wikipedia()") >= 0) { continue } + if (this.freeform.type === "wikidata" && txt.indexOf(`{wikidata_label(${this.freeform.key})`) >= 0) { + continue + } throw `${context}: The rendering for language ${ln} does not contain the freeform key {${this.freeform.key}}. This is a bug, as this rendering should show exactly this freeform key!\nThe rendering is ${txt} ` } } diff --git a/assets/layers/artwork/artwork.json b/assets/layers/artwork/artwork.json index 91a6736f37..c9d368b5be 100644 --- a/assets/layers/artwork/artwork.json +++ b/assets/layers/artwork/artwork.json @@ -407,10 +407,38 @@ "es": "Cerámica", "da": "flisebeklædning" } + }, + { + "if": "artwork_type=woodcarving", + "then": { + "nl": "Houtsculptuur", + "en": "Woodcarving" + } } ], "id": "artwork-artwork_type" }, + { + "id": "artwork-artist-wikidata", + "render": { + "en": "This artwork was made by {wikidata_label(artist:wikidata)}" + }, + "question": { + "en": "Who made this artwork?" + }, + "freeform": { + "key": "artist:wikidata", + "type": "wikidata", + "helperArgs": [ + { + "key": "artist_name", + "instanceOf": [ + "Q5" + ] + } + ] + } + }, { "question": { "en": "Which artist created this?", @@ -449,6 +477,7 @@ "freeform": { "key": "artist_name" }, + "condition": "artist:wikidata=", "id": "artwork-artist_name" }, { @@ -491,7 +520,21 @@ }, "id": "artwork-website" }, - "wikipedia" + "wikipedia", + { + "id": "artwork_subject", + "condition": "subject:wikidata~*", + "question": { + "en": "What does this artwork depict?" + }, + "freeform": { + "key": "subject:wikidata", + "type": "wikidata" + }, + "render": { + "en": "This artwork depicts {wikidata_label(subject:wikidata)}{wikipedia(subject:wikidata)}" + } + } ], "deletion": { "softDeletionTags": { @@ -524,5 +567,6 @@ "render": "10" } } - ] + ], + "filter": ["has_image"] } diff --git a/assets/layers/cafe_pub/cafe_pub.json b/assets/layers/cafe_pub/cafe_pub.json index 647d759326..e9900221a1 100644 --- a/assets/layers/cafe_pub/cafe_pub.json +++ b/assets/layers/cafe_pub/cafe_pub.json @@ -277,24 +277,9 @@ "reviews" ], "filter": [ - { - "id": "opened-now", - "options": [ - { - "question": { - "en": "Opened now", - "nl": "Nu geopend", - "de": "Derzeit geöffnet", - "fr": "Ouvert maintenant", - "hu": "Most nyitva van", - "ca": "Obert ara", - "es": "Abiert oahora", - "da": "Åbent nu" - }, - "osmTags": "_isOpen=yes" - } - ] - } + "open_now", + "accepts_cash", + "accepts_cards" ], "deletion": { "softDeletionTags": { @@ -362,4 +347,4 @@ "da": "Et lag med caféer og pubber, hvor man kan samles omkring en drink. Laget stiller nogle relevante spørgsmål", "fr": "Une couche montrants les cafés et pubs où l’on peut prendre un verre. Cette couche pose des questions y afférentes." } -} \ No newline at end of file +} diff --git a/assets/layers/filters/filters.json b/assets/layers/filters/filters.json new file mode 100644 index 0000000000..a8ec485ddc --- /dev/null +++ b/assets/layers/filters/filters.json @@ -0,0 +1,95 @@ +{ + "id": "filters", + "description": "This layer acts as library for common filters", + "mapRendering": [ + ], + "source": { + "osmTags": "id~*" + }, + "filter": [ + { + "id":"open_now", + "options": [ + { + "question": { + "en": "Opened now", + "nl": "Nu geopened", + "de": "Aktuell geöffnet", + "ca": "Obert ara", + "es": "Abierta ahora", + "fr": "Ouvert maintenant", + "hu": "Most nyitva van", + "da": "Åbent nu" + }, + "osmTags": "_isOpen=yes" + } + ] + }, + { + "id": "accepts_cash", + "options": [ + { + "osmTags": "payment:cash=yes", + "question": { + "en": "Accepts cash", + "de": "Akzeptiert Bargeld", + "nl": "Accepteert cash", + "es": "Acepta efectivo", + "fr": "Accepte les espèces" + } + } + ] + }, + { + "id": "accepts_cards", + "options": [ + { + "osmTags": "payment:cards=yes", + "question": { + "en": "Accepts payment cards", + "de": "Akzeptiert Kartenzahlung", + "nl": "Accepteert betaalkaarten", + "es": "Acepta el pago por tarjeta", + "fr": "Accepte les cartes de paiement" + } + } + ] + }, + { + "id": "has_image", + "options": [ + { + "question": "With and without images" + }, + { + "question": { + "en": "Has at least one image" + }, + "osmTags": { + "or": [ + "image~*", + "image:0~*", + "image:1~*", + "image:2~*", + "image:3~*" + ] + } + }, + { + "question": { + "en": "Probably does not have an image" + }, + "osmTags": { + "and": [ + "image=", + "image:0=", + "image:1=", + "image:2=", + "image:3=" + ] + } + } + ] + } + ] +} diff --git a/assets/layers/food/food.json b/assets/layers/food/food.json index 8888218b7b..bacd07382b 100644 --- a/assets/layers/food/food.json +++ b/assets/layers/food/food.json @@ -770,22 +770,7 @@ "reviews" ], "filter": [ - { - "id": "opened-now", - "options": [ - { - "question": { - "en": "Opened now", - "nl": "Nu geopened", - "de": "Aktuell geöffnet", - "ca": "Obert ara", - "es": "Abierta ahora", - "fr": "Ouvert maintenant" - }, - "osmTags": "_isOpen=yes" - } - ] - }, + "open_now", { "id": "vegetarian", "options": [ @@ -849,36 +834,8 @@ } ] }, - { - "id": "accepts-cash", - "options": [ - { - "osmTags": "payment:cash=yes", - "question": { - "en": "Accepts cash", - "de": "Akzeptiert Bargeld", - "es": "Acepta efectivo", - "nl": "Accepteert cash", - "fr": "Accepte les paiements en espèces" - } - } - ] - }, - { - "id": "accepts-cards", - "options": [ - { - "osmTags": "payment:cards=yes", - "question": { - "en": "Accepts payment cards", - "de": "Akzeptiert Kartenzahlung", - "es": "Acepta tarjetas de pago", - "nl": "Accepteert betaalkaarten", - "fr": "Accepte les cartes de paiement" - } - } - ] - } +"accepts_cash", + "accepts_cards" ], "deletion": { "nonDeleteMappings": [ @@ -979,4 +936,4 @@ "es": "Una capa que muestra restaurantes y facilidades de comida rápida", "fr": "Un claque montrant les restaurants et les endroits de nourriture rapide (avec un rendu spécial pour les friteries)" } -} \ No newline at end of file +} diff --git a/assets/layers/shops/shops.json b/assets/layers/shops/shops.json index 324ee0634b..f1a00fe89f 100644 --- a/assets/layers/shops/shops.json +++ b/assets/layers/shops/shops.json @@ -295,6 +295,7 @@ } ], "filter": [ + "open_now", { "id": "shop-type", "options": [ @@ -337,35 +338,8 @@ } ] }, - { - "id": "accepts-cash", - "options": [ - { - "osmTags": "payment:cash=yes", - "question": { - "en": "Accepts cash", - "de": "Akzeptiert Bargeld", - "nl": "Accepteert cash", - "es": "Acepta efectivo", - "fr": "Accepte les espèces" - } - } - ] - }, - { - "id": "accepts-cards", - "options": [ - { - "osmTags": "payment:cards=yes", - "question": { - "en": "Accepts payment cards", - "de": "Akzeptiert Kartenzahlung", - "nl": "Accepteert betaalkaarten", - "es": "Acepta el pago por tarjeta", - "fr": "Accepte les cartes de paiement" - } - } - ] - } + "accepts_cash", + "accepts_cards" + ] -} \ No newline at end of file +}