forked from MapComplete/MapComplete
Feature: allow a fallback value in substituting tags
This commit is contained in:
parent
35c4222466
commit
4d4a7e9d84
3 changed files with 32 additions and 32 deletions
|
@ -126,23 +126,7 @@ export class TagUtils {
|
||||||
"\n" +
|
"\n" +
|
||||||
"An assigning tag _cannot_ be used to query OpenStreetMap/Overpass.\n" +
|
"An assigning tag _cannot_ be used to query OpenStreetMap/Overpass.\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"If using a key or variable which might not be defined, add a condition in the mapping to hide the option. This is\n" +
|
"It is possible to assign a default value with `key_to_assing:={some_other_key??fallback_value}`. If `some_other_key` is not present, then the literal value `fallback_value` will be used instead." +
|
||||||
"because, if `some_other_key` is not defined, one might actually upload the literal text `key={some_other_key}` to OSM -\n" +
|
|
||||||
"which we do not want.\n" +
|
|
||||||
"\n" +
|
|
||||||
"To mitigate this, use:\n" +
|
|
||||||
"\n" +
|
|
||||||
"```json\n" +
|
|
||||||
"{\n" +
|
|
||||||
' "mappings": [\n' +
|
|
||||||
" {\n" +
|
|
||||||
' "if":"key:={some_other_key}",\n' +
|
|
||||||
' "then": "...",\n' +
|
|
||||||
' "hideInAnswer": "some_other_key="\n' +
|
|
||||||
" }\n" +
|
|
||||||
" ]\n" +
|
|
||||||
"}\n" +
|
|
||||||
"```\n" +
|
|
||||||
"\n" +
|
"\n" +
|
||||||
"One can use `key!:=prefix-{other_key}-postfix` as well, to match if `key` is _not_ the same\n" +
|
"One can use `key!:=prefix-{other_key}-postfix` as well, to match if `key` is _not_ the same\n" +
|
||||||
"as `prefix-{other_key}-postfix` (with `other_key` substituted by the value)",
|
"as `prefix-{other_key}-postfix` (with `other_key` substituted by the value)",
|
||||||
|
@ -263,8 +247,8 @@ export class TagUtils {
|
||||||
return tags
|
return tags
|
||||||
}
|
}
|
||||||
|
|
||||||
static SplitKeys(tagsFilters: UploadableTag[]): Record<string, string[]> {
|
static SplitKeys(tagsFilters: UploadableTag[], currentProperties: Tags): Record<string, string[]> {
|
||||||
return this.SplitKeysRegex(tagsFilters, false)
|
return this.SplitKeysRegex(tagsFilters, false, currentProperties)
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
|
@ -272,24 +256,31 @@ export class TagUtils {
|
||||||
*
|
*
|
||||||
* TagUtils.SplitKeysRegex([new Tag("isced:level", "bachelor; master")], true) // => {"isced:level": ["bachelor","master"]}
|
* TagUtils.SplitKeysRegex([new Tag("isced:level", "bachelor; master")], true) // => {"isced:level": ["bachelor","master"]}
|
||||||
*/
|
*/
|
||||||
static SplitKeysRegex(tagsFilters: UploadableTag[], allowRegex: false): Record<string, string[]>
|
static SplitKeysRegex(tagsFilters: ReadonlyArray<UploadableTag>, allowRegex: false,
|
||||||
|
currentProperties: Tags): Record<string, string[]>
|
||||||
static SplitKeysRegex(
|
static SplitKeysRegex(
|
||||||
tagsFilters: UploadableTag[],
|
tagsFilters: ReadonlyArray<UploadableTag>,
|
||||||
allowRegex: boolean
|
allowRegex: boolean,
|
||||||
|
currentProperties: Tags
|
||||||
): Record<string, (string | RegexTag)[]>
|
): Record<string, (string | RegexTag)[]>
|
||||||
static SplitKeysRegex(
|
static SplitKeysRegex(
|
||||||
tagsFilters: UploadableTag[],
|
tagsFiltersIn: ReadonlyArray<UploadableTag>,
|
||||||
allowRegex: boolean
|
allowRegex: boolean,
|
||||||
|
currentProperties: Tags
|
||||||
): Record<string, (string | RegexTag)[]> {
|
): Record<string, (string | RegexTag)[]> {
|
||||||
const keyValues: Record<string, (string | RegexTag)[]> = {}
|
const keyValues: Record<string, (string | RegexTag)[]> = {}
|
||||||
tagsFilters = [...tagsFilters] // copy all, use as queue
|
const tagsFilters = [...tagsFiltersIn] // copy all, use as queue
|
||||||
while (tagsFilters.length > 0) {
|
while (tagsFilters.length > 0) {
|
||||||
const tagsFilter = tagsFilters.shift()
|
let tagsFilter = tagsFilters.shift()
|
||||||
|
|
||||||
if (tagsFilter === undefined) {
|
if (tagsFilter === undefined) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tagsFilter instanceof SubstitutingTag) {
|
||||||
|
tagsFilter = (<SubstitutingTag>tagsFilter).asTag(currentProperties)
|
||||||
|
}
|
||||||
|
|
||||||
if (tagsFilter instanceof And) {
|
if (tagsFilter instanceof And) {
|
||||||
tagsFilters.push(...(<UploadableTag[]>tagsFilter.and))
|
tagsFilters.push(...(<UploadableTag[]>tagsFilter.and))
|
||||||
continue
|
continue
|
||||||
|
@ -360,12 +351,12 @@ export class TagUtils {
|
||||||
* TagUtils.FlattenMultiAnswer(([new Tag("x","y"), new Tag("a","b")])) // => [new Tag("x","y"), new Tag("a","b")]
|
* TagUtils.FlattenMultiAnswer(([new Tag("x","y"), new Tag("a","b")])) // => [new Tag("x","y"), new Tag("a","b")]
|
||||||
* TagUtils.FlattenMultiAnswer(([new Tag("x","")])) // => [new Tag("x","")]
|
* TagUtils.FlattenMultiAnswer(([new Tag("x","")])) // => [new Tag("x","")]
|
||||||
*/
|
*/
|
||||||
static FlattenMultiAnswer(tagsFilters: UploadableTag[]): UploadableTag[] {
|
static FlattenMultiAnswer(tagsFilters: UploadableTag[], currentProperties: Tags): UploadableTag[] {
|
||||||
if (tagsFilters === undefined) {
|
if (tagsFilters === undefined) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
const keyValues = TagUtils.SplitKeys(tagsFilters)
|
const keyValues = TagUtils.SplitKeys(tagsFilters, currentProperties)
|
||||||
const and: UploadableTag[] = []
|
const and: UploadableTag[] = []
|
||||||
for (const key in keyValues) {
|
for (const key in keyValues) {
|
||||||
const values = Lists.dedup(keyValues[key]).filter((v) => v !== "")
|
const values = Lists.dedup(keyValues[key]).filter((v) => v !== "")
|
||||||
|
@ -387,7 +378,7 @@ export class TagUtils {
|
||||||
* TagUtils.MatchesMultiAnswer(new Tag("isced:level","master"), {"isced:level":"bachelor; master"}) // => true
|
* TagUtils.MatchesMultiAnswer(new Tag("isced:level","master"), {"isced:level":"bachelor; master"}) // => true
|
||||||
*/
|
*/
|
||||||
static MatchesMultiAnswer(tag: UploadableTag, properties: Tags): boolean {
|
static MatchesMultiAnswer(tag: UploadableTag, properties: Tags): boolean {
|
||||||
const splitted = TagUtils.SplitKeysRegex([tag], true)
|
const splitted = TagUtils.SplitKeysRegex([tag], true, properties)
|
||||||
for (const splitKey in splitted) {
|
for (const splitKey in splitted) {
|
||||||
const neededValues = splitted[splitKey]
|
const neededValues = splitted[splitKey]
|
||||||
if (properties[splitKey] === undefined) {
|
if (properties[splitKey] === undefined) {
|
||||||
|
|
|
@ -897,7 +897,7 @@ export default class TagRenderingConfig {
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const and = TagUtils.FlattenMultiAnswer([...selectedMappings, ...unselectedMappings])
|
const and = TagUtils.FlattenMultiAnswer([...selectedMappings, ...unselectedMappings], currentProperties)
|
||||||
if (and.length === 0) {
|
if (and.length === 0) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
13
src/Utils.ts
13
src/Utils.ts
|
@ -300,6 +300,10 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
||||||
* Utils.SubstituteKeys("abc{def}ghi", {def: '{XYZ}'}) // => "abc{XYZ}ghi"
|
* Utils.SubstituteKeys("abc{def}ghi", {def: '{XYZ}'}) // => "abc{XYZ}ghi"
|
||||||
* Utils.SubstituteKeys("abc\n\n{def}ghi", {def: '{XYZ}'}) // => "abc\n\n{XYZ}ghi"
|
* Utils.SubstituteKeys("abc\n\n{def}ghi", {def: '{XYZ}'}) // => "abc\n\n{XYZ}ghi"
|
||||||
*
|
*
|
||||||
|
* // Should support a default value
|
||||||
|
* Utils.SubstituteKeys("abc{def??XXX}ghi", {def: 'XYZ'}) // => "abcXYZghi"
|
||||||
|
* Utils.SubstituteKeys("abc{def??XXX}ghi", {randomKey: 'XYZ'}) // => "abcXXXghi"
|
||||||
|
*
|
||||||
* @param txt
|
* @param txt
|
||||||
* @param tags
|
* @param tags
|
||||||
* @param useLang
|
* @param useLang
|
||||||
|
@ -322,8 +326,13 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
||||||
}
|
}
|
||||||
let result = ""
|
let result = ""
|
||||||
while (match) {
|
while (match) {
|
||||||
const [_, normal, key, leftover] = match
|
const [_, normal, keyFallback, leftover] = match
|
||||||
let v = tags?.[key]
|
let key = keyFallback
|
||||||
|
let fallback = ""
|
||||||
|
if (keyFallback.indexOf("??") >= 0) {
|
||||||
|
[key, fallback] = keyFallback.split("??")
|
||||||
|
}
|
||||||
|
let v = tags?.[key] ?? fallback
|
||||||
if (v !== undefined && v !== null) {
|
if (v !== undefined && v !== null) {
|
||||||
if (v["toISOString"] != undefined) {
|
if (v["toISOString"] != undefined) {
|
||||||
// This is a date, probably the timestamp of the object
|
// This is a date, probably the timestamp of the object
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue