forked from MapComplete/MapComplete
Logic: enable to re-export tagsFilter as TagsConfig; improve optimization for comparingTag
This commit is contained in:
parent
3ce21f61cb
commit
78238dccc7
12 changed files with 140 additions and 20 deletions
25
scripts/generateFavouritesLayer.ts
Normal file
25
scripts/generateFavouritesLayer.ts
Normal 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()
|
|
@ -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) => {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -15,8 +15,9 @@ 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],
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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[]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
0
src/UI/Favourites/FavouriteSummary.svelte
Normal file
0
src/UI/Favourites/FavouriteSummary.svelte
Normal file
8
src/UI/Favourites/Favourites.svelte
Normal file
8
src/UI/Favourites/Favourites.svelte
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { SpecialVisualization } from "../SpecialVisualization";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A panel showing all your favourites
|
||||||
|
*/
|
||||||
|
export let state: SpecialVisualizationState
|
||||||
|
</script>
|
Loading…
Add table
Add a link
Reference in a new issue