forked from MapComplete/MapComplete
		
	Logic: better support for tag optimization and simplifying expressions
This commit is contained in:
		
							parent
							
								
									bd228a6129
								
							
						
					
					
						commit
						a3d26db84a
					
				
					 11 changed files with 430 additions and 260 deletions
				
			
		
							
								
								
									
										220
									
								
								src/Models/ThemeConfig/Conversion/ExpandFilter.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								src/Models/ThemeConfig/Conversion/ExpandFilter.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,220 @@
 | 
			
		|||
import { DesugaringContext, DesugaringStep } from "./Conversion"
 | 
			
		||||
import { LayerConfigJson } from "../Json/LayerConfigJson"
 | 
			
		||||
import FilterConfigJson, { FilterConfigOptionJson } from "../Json/FilterConfigJson"
 | 
			
		||||
import predifined_filters from "../../../../assets/layers/filters/filters.json"
 | 
			
		||||
import { TagRenderingConfigJson } from "../Json/TagRenderingConfigJson"
 | 
			
		||||
import { ConversionContext } from "./ConversionContext"
 | 
			
		||||
import { QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson"
 | 
			
		||||
import { Utils } from "../../../Utils"
 | 
			
		||||
import { TagUtils } from "../../../Logic/Tags/TagUtils"
 | 
			
		||||
import { Tag } from "../../../Logic/Tags/Tag"
 | 
			
		||||
import { RegexTag } from "../../../Logic/Tags/RegexTag"
 | 
			
		||||
import { Or } from "../../../Logic/Tags/Or"
 | 
			
		||||
import Translations from "../../../UI/i18n/Translations"
 | 
			
		||||
import { FlatTag, OptimizedTag, TagsFilterClosed } from "../../../Logic/Tags/TagTypes"
 | 
			
		||||
import { TagsFilter } from "../../../Logic/Tags/TagsFilter"
 | 
			
		||||
import { And } from "../../../Logic/Tags/And"
 | 
			
		||||
 | 
			
		||||
export class PruneFilters extends DesugaringStep<LayerConfigJson>{
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super("Removes all filters which are impossible, e.g. because they conflict with the base tags", ["filter"],"PruneFilters")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private prune(sourceTags:FlatTag, filter: FilterConfigJson, context: ConversionContext): FilterConfigJson{
 | 
			
		||||
        if(!filter.strict){
 | 
			
		||||
            return filter
 | 
			
		||||
        }
 | 
			
		||||
        const countBefore = filter.options.length
 | 
			
		||||
        const newOptions: FilterConfigOptionJson[] = filter.options.filter(option => {
 | 
			
		||||
            if(!option.osmTags){
 | 
			
		||||
                return true
 | 
			
		||||
            }
 | 
			
		||||
            const condition = <OptimizedTag & TagsFilterClosed> TagUtils.Tag(option.osmTags).optimize()
 | 
			
		||||
            return condition.shadows(sourceTags);
 | 
			
		||||
 | 
			
		||||
        }).map(option => {
 | 
			
		||||
            if(!option.osmTags){
 | 
			
		||||
                return option
 | 
			
		||||
            }
 | 
			
		||||
            let basetags: TagsFilter = <TagsFilter> And.construct([TagUtils.Tag(option.osmTags)]).optimize()
 | 
			
		||||
            if(basetags instanceof And){
 | 
			
		||||
                basetags = <TagsFilter> basetags.removePhraseConsideredKnown(sourceTags, true)
 | 
			
		||||
            }
 | 
			
		||||
            return {...option, osmTags: basetags.asJson()}
 | 
			
		||||
        })
 | 
			
		||||
        const countAfter = newOptions.length
 | 
			
		||||
        if(countAfter !== countBefore){
 | 
			
		||||
            context.enters("filter", filter.id   ).info("Pruned "+(countBefore-countAfter)+" options away from filter (out of "+countBefore+")")
 | 
			
		||||
        }
 | 
			
		||||
        return {...filter, options: newOptions, strict: undefined}
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public convert(json: LayerConfigJson, context: ConversionContext): LayerConfigJson {
 | 
			
		||||
        if(!Array.isArray(json.filter) || typeof json.source === "string"){
 | 
			
		||||
            return json
 | 
			
		||||
        }
 | 
			
		||||
        if(!json.source["osmTags"]){
 | 
			
		||||
            return json
 | 
			
		||||
        }
 | 
			
		||||
        const sourceTags = TagUtils.Tag(json.source["osmTags"])
 | 
			
		||||
        return {...json, filter: json.filter?.map(obj => this.prune(sourceTags, <FilterConfigJson> obj, context))}
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
export class ExpandFilter extends DesugaringStep<LayerConfigJson> {
 | 
			
		||||
    private static readonly predefinedFilters = ExpandFilter.load_filters()
 | 
			
		||||
    private _state: DesugaringContext
 | 
			
		||||
 | 
			
		||||
    constructor(state: DesugaringContext) {
 | 
			
		||||
        super(
 | 
			
		||||
            [
 | 
			
		||||
                "Expands filters: replaces a shorthand by the value found in 'filters.json'.",
 | 
			
		||||
                "If the string is formatted 'layername.filtername, it will be looked up into that layer instead. Note that pruning should still be done",
 | 
			
		||||
            ].join(" "),
 | 
			
		||||
            ["filter"],
 | 
			
		||||
            "ExpandFilter",
 | 
			
		||||
        )
 | 
			
		||||
        this._state = state
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static load_filters(): Map<string, FilterConfigJson> {
 | 
			
		||||
        const filters = new Map<string, FilterConfigJson>()
 | 
			
		||||
        for (const filter of <FilterConfigJson[]>predifined_filters.filter) {
 | 
			
		||||
            filters.set(filter.id, filter)
 | 
			
		||||
        }
 | 
			
		||||
        return filters
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static buildFilterFromTagRendering(
 | 
			
		||||
        tr: TagRenderingConfigJson,
 | 
			
		||||
        context: ConversionContext,
 | 
			
		||||
    ): FilterConfigJson {
 | 
			
		||||
        if (!(tr.mappings?.length >= 1)) {
 | 
			
		||||
            context.err(
 | 
			
		||||
                "Found a matching tagRendering to base a filter on, but this tagRendering does not contain any mappings",
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        const qtr = <QuestionableTagRenderingConfigJson>tr
 | 
			
		||||
        const options = qtr.mappings.map((mapping) => {
 | 
			
		||||
            let icon: string = mapping.icon?.["path"] ?? mapping.icon
 | 
			
		||||
            let emoji: string = undefined
 | 
			
		||||
            if (Utils.isEmoji(icon)) {
 | 
			
		||||
                emoji = icon
 | 
			
		||||
                icon = undefined
 | 
			
		||||
            }
 | 
			
		||||
            let osmTags = TagUtils.Tag(mapping.if)
 | 
			
		||||
            if (qtr.multiAnswer && osmTags instanceof Tag) {
 | 
			
		||||
                osmTags = new RegexTag(
 | 
			
		||||
                    osmTags.key,
 | 
			
		||||
                    new RegExp("^(.+;)?" + osmTags.value + "(;.+)$", "is"),
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            if (mapping.alsoShowIf) {
 | 
			
		||||
                osmTags = new Or([osmTags, TagUtils.Tag(mapping.alsoShowIf)])
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return <FilterConfigOptionJson>{
 | 
			
		||||
                question: mapping.then,
 | 
			
		||||
                osmTags: osmTags.asJson(),
 | 
			
		||||
                searchTerms: mapping.searchTerms,
 | 
			
		||||
                icon,
 | 
			
		||||
                emoji,
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        // Add default option
 | 
			
		||||
        options.unshift({
 | 
			
		||||
            question: tr["question"] ?? Translations.t.general.filterPanel.allTypes,
 | 
			
		||||
            osmTags: undefined,
 | 
			
		||||
            searchTerms: undefined,
 | 
			
		||||
        })
 | 
			
		||||
        return {
 | 
			
		||||
            id: tr["id"],
 | 
			
		||||
            options,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    convert(json: LayerConfigJson, context: ConversionContext): LayerConfigJson {
 | 
			
		||||
        if (json?.filter === undefined || json?.filter === null) {
 | 
			
		||||
            return json // Nothing to change here
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (json.filter["sameAs"] !== undefined) {
 | 
			
		||||
            return json // Nothing to change here
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const newFilters: FilterConfigJson[] = []
 | 
			
		||||
        const filters = <(FilterConfigJson | string)[]>json.filter
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Create filters based on builtin filters or create them based on the tagRendering
 | 
			
		||||
         */
 | 
			
		||||
        for (let i = 0; i < filters.length; i++) {
 | 
			
		||||
            const filter = filters[i]
 | 
			
		||||
            if (filter === undefined) {
 | 
			
		||||
                continue
 | 
			
		||||
            }
 | 
			
		||||
            if (typeof filter !== "string") {
 | 
			
		||||
                newFilters.push(filter)
 | 
			
		||||
                continue
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const matchingTr = <TagRenderingConfigJson>(
 | 
			
		||||
                json.tagRenderings.find((tr) => !!tr && tr["id"] === filter)
 | 
			
		||||
            )
 | 
			
		||||
            if (matchingTr) {
 | 
			
		||||
                const filter = ExpandFilter.buildFilterFromTagRendering(
 | 
			
		||||
                    matchingTr,
 | 
			
		||||
                    context.enters("filter", i),
 | 
			
		||||
                )
 | 
			
		||||
                newFilters.push(filter)
 | 
			
		||||
                continue
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (filter.indexOf(".") > 0) {
 | 
			
		||||
                if (!(this._state.sharedLayers?.size > 0)) {
 | 
			
		||||
                    // This is a bootstrapping-run, we can safely ignore this
 | 
			
		||||
                    continue
 | 
			
		||||
                }
 | 
			
		||||
                const split = filter.split(".")
 | 
			
		||||
                if (split.length > 2) {
 | 
			
		||||
                    context.err(
 | 
			
		||||
                        "invalid filter name: " + filter + ", expected `layername.filterid`",
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
                const layer = this._state.sharedLayers.get(split[0])
 | 
			
		||||
                if (layer === undefined) {
 | 
			
		||||
                    context.err("Layer '" + split[0] + "' not found")
 | 
			
		||||
                }
 | 
			
		||||
                const expectedId = split[1]
 | 
			
		||||
                const expandedFilter = (<(FilterConfigJson | string)[]>layer.filter).find(
 | 
			
		||||
                    (f) => typeof f !== "string" && f.id === expectedId,
 | 
			
		||||
                )
 | 
			
		||||
                if (expandedFilter === undefined) {
 | 
			
		||||
                    context.err("Did not find filter with name " + filter)
 | 
			
		||||
                } else {
 | 
			
		||||
                    newFilters.push(<FilterConfigJson>expandedFilter)
 | 
			
		||||
                }
 | 
			
		||||
                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,
 | 
			
		||||
                )
 | 
			
		||||
                context
 | 
			
		||||
                    .enter(filter)
 | 
			
		||||
                    .err(
 | 
			
		||||
                        "While searching for predefined filter " +
 | 
			
		||||
                        filter +
 | 
			
		||||
                        ": this filter is not found. Perhaps you meant one of: " +
 | 
			
		||||
                        suggestions,
 | 
			
		||||
                    )
 | 
			
		||||
            }
 | 
			
		||||
            newFilters.push(found)
 | 
			
		||||
        }
 | 
			
		||||
        return { ...json, filter: newFilters }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -10,10 +10,7 @@ import {
 | 
			
		|||
    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 RewritableConfigJson from "../Json/RewritableConfigJson"
 | 
			
		||||
import SpecialVisualizations from "../../../UI/SpecialVisualizations"
 | 
			
		||||
| 
						 | 
				
			
			@ -21,8 +18,7 @@ import Translations from "../../../UI/i18n/Translations"
 | 
			
		|||
import { Translation } from "../../../UI/i18n/Translation"
 | 
			
		||||
import tagrenderingconfigmeta from "../../../../src/assets/schemas/tagrenderingconfigmeta.json"
 | 
			
		||||
import { AddContextToTranslations } from "./AddContextToTranslations"
 | 
			
		||||
import FilterConfigJson, { FilterConfigOptionJson } from "../Json/FilterConfigJson"
 | 
			
		||||
import predifined_filters from "../../../../assets/layers/filters/filters.json"
 | 
			
		||||
import FilterConfigJson from "../Json/FilterConfigJson"
 | 
			
		||||
import { TagConfigJson } from "../Json/TagConfigJson"
 | 
			
		||||
import PointRenderingConfigJson, { IconConfigJson } from "../Json/PointRenderingConfigJson"
 | 
			
		||||
import ValidationUtils from "./ValidationUtils"
 | 
			
		||||
| 
						 | 
				
			
			@ -33,9 +29,7 @@ import LineRenderingConfigJson from "../Json/LineRenderingConfigJson"
 | 
			
		|||
import { ConversionContext } from "./ConversionContext"
 | 
			
		||||
import { ExpandRewrite } from "./ExpandRewrite"
 | 
			
		||||
import { TagUtils } from "../../../Logic/Tags/TagUtils"
 | 
			
		||||
import { Tag } from "../../../Logic/Tags/Tag"
 | 
			
		||||
import { RegexTag } from "../../../Logic/Tags/RegexTag"
 | 
			
		||||
import { Or } from "../../../Logic/Tags/Or"
 | 
			
		||||
import { ExpandFilter, PruneFilters } from "./ExpandFilter"
 | 
			
		||||
 | 
			
		||||
class AddFiltersFromTagRenderings extends DesugaringStep<LayerConfigJson> {
 | 
			
		||||
    constructor() {
 | 
			
		||||
| 
						 | 
				
			
			@ -108,163 +102,6 @@ class AddFiltersFromTagRenderings extends DesugaringStep<LayerConfigJson> {
 | 
			
		|||
        return { ...json, filter: filters }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
class ExpandFilter extends DesugaringStep<LayerConfigJson> {
 | 
			
		||||
    private static readonly predefinedFilters = ExpandFilter.load_filters()
 | 
			
		||||
    private _state: DesugaringContext
 | 
			
		||||
 | 
			
		||||
    constructor(state: DesugaringContext) {
 | 
			
		||||
        super(
 | 
			
		||||
            [
 | 
			
		||||
                "Expands filters: replaces a shorthand by the value found in 'filters.json'.",
 | 
			
		||||
                "If the string is formatted 'layername.filtername, it will be looked up into that layer instead.",
 | 
			
		||||
            ].join(" "),
 | 
			
		||||
            ["filter"],
 | 
			
		||||
            "ExpandFilter"
 | 
			
		||||
        )
 | 
			
		||||
        this._state = state
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static load_filters(): Map<string, FilterConfigJson> {
 | 
			
		||||
        const filters = new Map<string, FilterConfigJson>()
 | 
			
		||||
        for (const filter of <FilterConfigJson[]>predifined_filters.filter) {
 | 
			
		||||
            filters.set(filter.id, filter)
 | 
			
		||||
        }
 | 
			
		||||
        return filters
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static buildFilterFromTagRendering(
 | 
			
		||||
        tr: TagRenderingConfigJson,
 | 
			
		||||
        context: ConversionContext
 | 
			
		||||
    ): FilterConfigJson {
 | 
			
		||||
        if (!(tr.mappings?.length >= 1)) {
 | 
			
		||||
            context.err(
 | 
			
		||||
                "Found a matching tagRendering to base a filter on, but this tagRendering does not contain any mappings"
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        const qtr = <QuestionableTagRenderingConfigJson>tr
 | 
			
		||||
        const options = qtr.mappings.map((mapping) => {
 | 
			
		||||
            let icon: string = mapping.icon?.["path"] ?? mapping.icon
 | 
			
		||||
            let emoji: string = undefined
 | 
			
		||||
            if (Utils.isEmoji(icon)) {
 | 
			
		||||
                emoji = icon
 | 
			
		||||
                icon = undefined
 | 
			
		||||
            }
 | 
			
		||||
            let osmTags = TagUtils.Tag(mapping.if)
 | 
			
		||||
            if (qtr.multiAnswer && osmTags instanceof Tag) {
 | 
			
		||||
                osmTags = new RegexTag(
 | 
			
		||||
                    osmTags.key,
 | 
			
		||||
                    new RegExp("^(.+;)?" + osmTags.value + "(;.+)$", "is")
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            if (mapping.alsoShowIf) {
 | 
			
		||||
                osmTags = new Or([osmTags, TagUtils.Tag(mapping.alsoShowIf)])
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return <FilterConfigOptionJson>{
 | 
			
		||||
                question: mapping.then,
 | 
			
		||||
                osmTags: osmTags.asJson(),
 | 
			
		||||
                searchTerms: mapping.searchTerms,
 | 
			
		||||
                icon,
 | 
			
		||||
                emoji,
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        // Add default option
 | 
			
		||||
        options.unshift({
 | 
			
		||||
            question: tr["question"] ?? Translations.t.general.filterPanel.allTypes,
 | 
			
		||||
            osmTags: undefined,
 | 
			
		||||
            searchTerms: undefined,
 | 
			
		||||
        })
 | 
			
		||||
        return {
 | 
			
		||||
            id: tr["id"],
 | 
			
		||||
            options,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    convert(json: LayerConfigJson, context: ConversionContext): LayerConfigJson {
 | 
			
		||||
        if (json?.filter === undefined || json?.filter === null) {
 | 
			
		||||
            return json // Nothing to change here
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (json.filter["sameAs"] !== undefined) {
 | 
			
		||||
            return json // Nothing to change here
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const newFilters: FilterConfigJson[] = []
 | 
			
		||||
        const filters = <(FilterConfigJson | string)[]>json.filter
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Create filters based on builtin filters or create them based on the tagRendering
 | 
			
		||||
         */
 | 
			
		||||
        for (let i = 0; i < filters.length; i++) {
 | 
			
		||||
            const filter = filters[i]
 | 
			
		||||
            if (filter === undefined) {
 | 
			
		||||
                continue
 | 
			
		||||
            }
 | 
			
		||||
            if (typeof filter !== "string") {
 | 
			
		||||
                newFilters.push(filter)
 | 
			
		||||
                continue
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const matchingTr = <TagRenderingConfigJson>(
 | 
			
		||||
                json.tagRenderings.find((tr) => !!tr && tr["id"] === filter)
 | 
			
		||||
            )
 | 
			
		||||
            if (matchingTr) {
 | 
			
		||||
                const filter = ExpandFilter.buildFilterFromTagRendering(
 | 
			
		||||
                    matchingTr,
 | 
			
		||||
                    context.enters("filter", i)
 | 
			
		||||
                )
 | 
			
		||||
                newFilters.push(filter)
 | 
			
		||||
                continue
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (filter.indexOf(".") > 0) {
 | 
			
		||||
                if (!(this._state.sharedLayers?.size > 0)) {
 | 
			
		||||
                    // This is a bootstrapping-run, we can safely ignore this
 | 
			
		||||
                    continue
 | 
			
		||||
                }
 | 
			
		||||
                const split = filter.split(".")
 | 
			
		||||
                if (split.length > 2) {
 | 
			
		||||
                    context.err(
 | 
			
		||||
                        "invalid filter name: " + filter + ", expected `layername.filterid`"
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
                const layer = this._state.sharedLayers.get(split[0])
 | 
			
		||||
                if (layer === undefined) {
 | 
			
		||||
                    context.err("Layer '" + split[0] + "' not found")
 | 
			
		||||
                }
 | 
			
		||||
                const expectedId = split[1]
 | 
			
		||||
                const expandedFilter = (<(FilterConfigJson | string)[]>layer.filter).find(
 | 
			
		||||
                    (f) => typeof f !== "string" && f.id === expectedId
 | 
			
		||||
                )
 | 
			
		||||
                if (expandedFilter === undefined) {
 | 
			
		||||
                    context.err("Did not find filter with name " + filter)
 | 
			
		||||
                } else {
 | 
			
		||||
                    newFilters.push(<FilterConfigJson>expandedFilter)
 | 
			
		||||
                }
 | 
			
		||||
                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
 | 
			
		||||
                )
 | 
			
		||||
                context
 | 
			
		||||
                    .enter(filter)
 | 
			
		||||
                    .err(
 | 
			
		||||
                        "While searching for predefined filter " +
 | 
			
		||||
                            filter +
 | 
			
		||||
                            ": this filter is not found. Perhaps you meant one of: " +
 | 
			
		||||
                            suggestions
 | 
			
		||||
                    )
 | 
			
		||||
            }
 | 
			
		||||
            newFilters.push(found)
 | 
			
		||||
        }
 | 
			
		||||
        return { ...json, filter: newFilters }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class ExpandTagRendering extends Conversion<
 | 
			
		||||
    | string
 | 
			
		||||
| 
						 | 
				
			
			@ -1481,7 +1318,8 @@ export class PrepareLayer extends Fuse<LayerConfigJson> {
 | 
			
		|||
                    new Concat(new ExpandTagRendering(state, layer, { noHardcodedStrings: true }))
 | 
			
		||||
            ),
 | 
			
		||||
            new AddFiltersFromTagRenderings(),
 | 
			
		||||
            new ExpandFilter(state)
 | 
			
		||||
            new ExpandFilter(state),
 | 
			
		||||
            new PruneFilters()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,6 +20,15 @@ export default interface FilterConfigJson {
 | 
			
		|||
     * An id/name for this filter, used to set the URL parameters
 | 
			
		||||
     */
 | 
			
		||||
    id: string
 | 
			
		||||
    /**
 | 
			
		||||
     * If set, the options will be pruned. Only items for which the filter match the layer source will be kept.
 | 
			
		||||
     *
 | 
			
		||||
     * For example, we import types of brands from the nsi. This contains a ton of items, e.g.
 | 
			
		||||
     * [{question: "Brand X", osmTags: {"and": ["shop=clothes", "brand=Brand X]}, {osmTags: {"and": "shop=convenience", ...} ...} ]
 | 
			
		||||
     * Of course, when making a layer about `shop=clothes`, we'll only want to keep the clothes shops.
 | 
			
		||||
     * If set to strict and the source is `shop=clothes`, only those options which have shop=clothes will be returned
 | 
			
		||||
     */
 | 
			
		||||
    strict?: boolean
 | 
			
		||||
    /**
 | 
			
		||||
     * The options for a filter
 | 
			
		||||
     * If there are multiple options these will be a list of radio buttons
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -601,4 +601,9 @@ export interface LayerConfigJson {
 | 
			
		|||
     * group: hidden
 | 
			
		||||
     */
 | 
			
		||||
    snapName?: Translatable
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * group: hidden
 | 
			
		||||
     */
 | 
			
		||||
    "#dont-translate": "*"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue