Logic: enable to re-export tagsFilter as TagsConfig; improve optimization for comparingTag

This commit is contained in:
Pieter Vander Vennet 2023-11-29 14:29:11 +01:00
parent 3ce21f61cb
commit 78238dccc7
12 changed files with 140 additions and 20 deletions

View file

@ -0,0 +1,25 @@
import Script from "./Script"
import { LayerConfigJson } from "../src/Models/ThemeConfig/Json/LayerConfigJson"
import { readFileSync, writeFileSync } from "fs"
import { AllSharedLayers } from "../src/Customizations/AllSharedLayers"
class PrepareFavouritesLayerJson extends Script {
constructor() {
super("Prepares the 'favourites'-layer")
}
async main(args: string[]): Promise<void> {
const allConfigs = AllSharedLayers.getSharedLayersConfigs()
const proto = this.readLayer("favourite/favourite.proto.json")
const questions = allConfigs.get("questions")
proto.tagRenderings.push(...questions.tagRenderings)
writeFileSync("./assets/layers/favourite/favourite.json", JSON.stringify(proto, null, " "))
}
private readLayer(path: string): LayerConfigJson {
return JSON.parse(readFileSync("./assets/layers/" + path, "utf8"))
}
}
new PrepareFavouritesLayerJson().run()

View file

@ -3,6 +3,7 @@ import { Or } from "./Or"
import { TagUtils } from "./TagUtils" import { TagUtils } from "./TagUtils"
import { Tag } from "./Tag" import { Tag } from "./Tag"
import { RegexTag } from "./RegexTag" import { RegexTag } from "./RegexTag"
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
export class And extends TagsFilter { export class And extends TagsFilter {
public and: TagsFilter[] public and: TagsFilter[]
@ -72,6 +73,10 @@ export class And extends TagsFilter {
return allChoices return allChoices
} }
asJson(): TagConfigJson {
return { and: this.and.map((a) => a.asJson()) }
}
asHumanString(linkToWiki: boolean, shorten: boolean, properties: Record<string, string>) { asHumanString(linkToWiki: boolean, shorten: boolean, properties: Record<string, string>) {
return this.and return this.and
.map((t) => { .map((t) => {

View file

@ -1,18 +1,23 @@
import { TagsFilter } from "./TagsFilter" import { TagsFilter } from "./TagsFilter"
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
import { Tag } from "./Tag"
export default class ComparingTag implements TagsFilter { export default class ComparingTag implements TagsFilter {
private readonly _key: string private readonly _key: string
private readonly _predicate: (value: string) => boolean private readonly _predicate: (value: string) => boolean
private readonly _representation: string private readonly _representation: "<" | ">" | "<=" | ">="
private readonly _boundary: string
constructor( constructor(
key: string, key: string,
predicate: (value: string | undefined) => boolean, predicate: (value: string | undefined) => boolean,
representation: string = "" representation: "<" | ">" | "<=" | ">=",
boundary: string
) { ) {
this._key = key this._key = key
this._predicate = predicate this._predicate = predicate
this._representation = representation this._representation = representation
this._boundary = boundary
} }
asChange(properties: Record<string, string>): { k: string; v: string }[] { asChange(properties: Record<string, string>): { k: string; v: string }[] {
@ -20,15 +25,64 @@ export default class ComparingTag implements TagsFilter {
} }
asHumanString(linkToWiki: boolean, shorten: boolean, properties: Record<string, string>) { asHumanString(linkToWiki: boolean, shorten: boolean, properties: Record<string, string>) {
return this._key + this._representation return this._key + this._representation + this._boundary
} }
asOverpass(): string[] { asOverpass(): string[] {
throw "A comparable tag can not be used as overpass filter" throw "A comparable tag can not be used as overpass filter"
} }
/**
* const tg = new ComparingTag("key", value => (Number(value) < 42), "<", "42")
* const tg0 = new ComparingTag("key", value => (Number(value) < 42), "<", "42")
* const tg1 = new ComparingTag("key", value => (Number(value) <= 42), "<=", "42")
* const against = new ComparingTag("key", value => (Number(value) > 0), ">", "0")
* tg.shadows(new Tag("key", "41")) // => true
* tg.shadows(new Tag("key", "0")) // => true
* tg.shadows(new Tag("key", "43")) // => false
* tg.shadows(new Tag("key", "0")) // => true
* tg.shadows(tg) // => true
* tg.shadows(tg0) // => true
* tg.shadows(against) // => false
* tg1.shadows(tg0) // => true
* tg0.shadows(tg1) // => false
*
*/
shadows(other: TagsFilter): boolean { shadows(other: TagsFilter): boolean {
return other === this if (other === this) {
return true
}
if (other instanceof ComparingTag) {
if (other._key !== this._key) {
return false
}
const selfDesc = this._representation === "<" || this._representation === "<="
const otherDesc = other._representation === "<" || other._representation === "<="
if (selfDesc !== otherDesc) {
return false
}
if (
this._boundary === other._boundary &&
this._representation === other._representation
) {
return true
}
if (this._predicate(other._boundary)) {
return true
}
return false
}
if (other instanceof Tag) {
if (other.key !== this._key) {
return false
}
if (this.matchesProperties({ [other.key]: other.value })) {
return true
}
}
return false
} }
isUsableAsAnswer(): boolean { isUsableAsAnswer(): boolean {
@ -38,7 +92,7 @@ export default class ComparingTag implements TagsFilter {
/** /**
* Checks if the properties match * Checks if the properties match
* *
* const t = new ComparingTag("key", (x => Number(x) < 42)) * const t = new ComparingTag("key", (x => Number(x) < 42), "<", "42")
* t.matchesProperties({key: 42}) // => false * t.matchesProperties({key: 42}) // => false
* t.matchesProperties({key: 41}) // => true * t.matchesProperties({key: 41}) // => true
* t.matchesProperties({key: 0}) // => true * t.matchesProperties({key: 0}) // => true
@ -56,6 +110,10 @@ export default class ComparingTag implements TagsFilter {
return [] return []
} }
asJson(): TagConfigJson {
return this._key + this._representation
}
optimize(): TagsFilter | boolean { optimize(): TagsFilter | boolean {
return this return this
} }

View file

@ -1,6 +1,7 @@
import { TagsFilter } from "./TagsFilter" import { TagsFilter } from "./TagsFilter"
import { TagUtils } from "./TagUtils" import { TagUtils } from "./TagUtils"
import { And } from "./And" import { And } from "./And"
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
export class Or extends TagsFilter { export class Or extends TagsFilter {
public or: TagsFilter[] public or: TagsFilter[]
@ -27,6 +28,10 @@ export class Or extends TagsFilter {
return false return false
} }
asJson(): TagConfigJson {
return { or: this.or.map((o) => o.asJson()) }
}
/** /**
* *
* import {Tag} from "./Tag"; * import {Tag} from "./Tag";
@ -174,9 +179,9 @@ export class Or extends TagsFilter {
const newOrs: TagsFilter[] = [] const newOrs: TagsFilter[] = []
let containedAnds: And[] = [] let containedAnds: And[] = []
for (const tf of optimized) { for (const tf of optimized) {
if (tf instanceof Or) { if (tf["or"]) {
// expand all the nested ors... // expand all the nested ors...
newOrs.push(...tf.or) newOrs.push(...tf["or"])
} else if (tf instanceof And) { } else if (tf instanceof And) {
// partition of all the ands // partition of all the ands
containedAnds.push(tf) containedAnds.push(tf)
@ -191,7 +196,7 @@ export class Or extends TagsFilter {
const cleanedContainedANds: And[] = [] const cleanedContainedANds: And[] = []
outer: for (let containedAnd of containedAnds) { outer: for (let containedAnd of containedAnds) {
for (const known of newOrs) { for (const known of newOrs) {
// input for optimazation: (K=V | (X=Y & K=V)) // input for optimization: (K=V | (X=Y & K=V))
// containedAnd: (X=Y & K=V) // containedAnd: (X=Y & K=V)
// newOrs (and thus known): (K=V) --> false // newOrs (and thus known): (K=V) --> false
const cleaned = containedAnd.removePhraseConsideredKnown(known, false) const cleaned = containedAnd.removePhraseConsideredKnown(known, false)

View file

@ -1,5 +1,6 @@
import { Tag } from "./Tag" import { Tag } from "./Tag"
import { TagsFilter } from "./TagsFilter" import { TagsFilter } from "./TagsFilter"
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
export class RegexTag extends TagsFilter { export class RegexTag extends TagsFilter {
public readonly key: RegExp | string public readonly key: RegExp | string
@ -82,6 +83,10 @@ export class RegexTag extends TagsFilter {
} }
} }
asJson(): TagConfigJson {
return this.asHumanString()
}
isUsableAsAnswer(): boolean { isUsableAsAnswer(): boolean {
return false return false
} }

View file

@ -1,6 +1,7 @@
import { TagsFilter } from "./TagsFilter" import { TagsFilter } from "./TagsFilter"
import { Tag } from "./Tag" import { Tag } from "./Tag"
import { Utils } from "../../Utils" import { Utils } from "../../Utils"
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
/** /**
* The substituting-tag uses the tags of a feature a variables and replaces them. * The substituting-tag uses the tags of a feature a variables and replaces them.
@ -45,6 +46,10 @@ export default class SubstitutingTag implements TagsFilter {
) )
} }
asJson(): TagConfigJson {
return this._key + (this._invert ? "!" : "") + ":=" + this._value
}
asOverpass(): string[] { asOverpass(): string[] {
throw "A variable with substitution can not be used to query overpass" throw "A variable with substitution can not be used to query overpass"
} }

View file

@ -1,5 +1,6 @@
import { Utils } from "../../Utils" import { Utils } from "../../Utils"
import { TagsFilter } from "./TagsFilter" import { TagsFilter } from "./TagsFilter"
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
export class Tag extends TagsFilter { export class Tag extends TagsFilter {
public key: string public key: string
@ -67,6 +68,10 @@ export class Tag extends TagsFilter {
return [`["${this.key}"="${this.value}"]`] return [`["${this.key}"="${this.value}"]`]
} }
asJson(): TagConfigJson {
return this.key + "=" + this.value
}
/** /**
const t = new Tag("key", "value") const t = new Tag("key", "value")

View file

@ -15,13 +15,14 @@ type Tags = Record<string, string>
export type UploadableTag = Tag | SubstitutingTag | And export type UploadableTag = Tag | SubstitutingTag | And
export class TagUtils { export class TagUtils {
public static readonly comparators: ReadonlyArray<[string, (a: number, b: number) => boolean]> = public static readonly comparators: ReadonlyArray<
[ ["<" | ">" | "<=" | ">=", (a: number, b: number) => boolean]
["<=", (a, b) => a <= b], > = [
[">=", (a, b) => a >= b], ["<=", (a, b) => a <= b],
["<", (a, b) => a < b], [">=", (a, b) => a >= b],
[">", (a, b) => a > b], ["<", (a, b) => a < b],
] [">", (a, b) => a > b],
]
public static modeDocumentation: Record< public static modeDocumentation: Record<
string, string,
{ name: string; docs: string; uploadable?: boolean; overpassSupport: boolean } { name: string; docs: string; uploadable?: boolean; overpassSupport: boolean }
@ -735,11 +736,10 @@ export class TagUtils {
const tag = json as string const tag = json as string
for (const [operator, comparator] of TagUtils.comparators) { for (const [operator, comparator] of TagUtils.comparators) {
if (tag.indexOf(operator) >= 0) { if (tag.indexOf(operator) >= 0) {
const split = Utils.SplitFirst(tag, operator) const split = Utils.SplitFirst(tag, operator).map((v) => v.trim())
let val = Number(split[1])
let val = Number(split[1].trim())
if (isNaN(val)) { if (isNaN(val)) {
val = new Date(split[1].trim()).getTime() val = new Date(split[1]).getTime()
} }
const f = (value: string | number | undefined) => { const f = (value: string | number | undefined) => {
@ -762,7 +762,7 @@ export class TagUtils {
} }
return comparator(b, val) return comparator(b, val)
} }
return new ComparingTag(split[0], f, operator + val) return new ComparingTag(split[0], f, operator, "" + val)
} }
} }

View file

@ -1,3 +1,5 @@
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
export abstract class TagsFilter { export abstract class TagsFilter {
abstract asOverpass(): string[] abstract asOverpass(): string[]
@ -17,6 +19,8 @@ export abstract class TagsFilter {
properties: Record<string, string> properties: Record<string, string>
): string ): string
abstract asJson(): TagConfigJson
abstract usedKeys(): string[] abstract usedKeys(): string[]
/** /**

View file

@ -0,0 +1,8 @@
<script lang="ts">
import { SpecialVisualization } from "../SpecialVisualization";
/**
* A panel showing all your favourites
*/
export let state: SpecialVisualizationState
</script>