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
|
@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue