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 { Tag } from "./Tag"
|
||||
import { RegexTag } from "./RegexTag"
|
||||
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
|
||||
|
||||
export class And extends TagsFilter {
|
||||
public and: TagsFilter[]
|
||||
|
@ -72,6 +73,10 @@ export class And extends TagsFilter {
|
|||
return allChoices
|
||||
}
|
||||
|
||||
asJson(): TagConfigJson {
|
||||
return { and: this.and.map((a) => a.asJson()) }
|
||||
}
|
||||
|
||||
asHumanString(linkToWiki: boolean, shorten: boolean, properties: Record<string, string>) {
|
||||
return this.and
|
||||
.map((t) => {
|
||||
|
|
|
@ -1,18 +1,23 @@
|
|||
import { TagsFilter } from "./TagsFilter"
|
||||
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
|
||||
import { Tag } from "./Tag"
|
||||
|
||||
export default class ComparingTag implements TagsFilter {
|
||||
private readonly _key: string
|
||||
private readonly _predicate: (value: string) => boolean
|
||||
private readonly _representation: string
|
||||
private readonly _representation: "<" | ">" | "<=" | ">="
|
||||
private readonly _boundary: string
|
||||
|
||||
constructor(
|
||||
key: string,
|
||||
predicate: (value: string | undefined) => boolean,
|
||||
representation: string = ""
|
||||
representation: "<" | ">" | "<=" | ">=",
|
||||
boundary: string
|
||||
) {
|
||||
this._key = key
|
||||
this._predicate = predicate
|
||||
this._representation = representation
|
||||
this._boundary = boundary
|
||||
}
|
||||
|
||||
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>) {
|
||||
return this._key + this._representation
|
||||
return this._key + this._representation + this._boundary
|
||||
}
|
||||
|
||||
asOverpass(): string[] {
|
||||
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 {
|
||||
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 {
|
||||
|
@ -38,7 +92,7 @@ export default class ComparingTag implements TagsFilter {
|
|||
/**
|
||||
* 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: 41}) // => true
|
||||
* t.matchesProperties({key: 0}) // => true
|
||||
|
@ -56,6 +110,10 @@ export default class ComparingTag implements TagsFilter {
|
|||
return []
|
||||
}
|
||||
|
||||
asJson(): TagConfigJson {
|
||||
return this._key + this._representation
|
||||
}
|
||||
|
||||
optimize(): TagsFilter | boolean {
|
||||
return this
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { TagsFilter } from "./TagsFilter"
|
||||
import { TagUtils } from "./TagUtils"
|
||||
import { And } from "./And"
|
||||
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
|
||||
|
||||
export class Or extends TagsFilter {
|
||||
public or: TagsFilter[]
|
||||
|
@ -27,6 +28,10 @@ export class Or extends TagsFilter {
|
|||
return false
|
||||
}
|
||||
|
||||
asJson(): TagConfigJson {
|
||||
return { or: this.or.map((o) => o.asJson()) }
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* import {Tag} from "./Tag";
|
||||
|
@ -174,9 +179,9 @@ export class Or extends TagsFilter {
|
|||
const newOrs: TagsFilter[] = []
|
||||
let containedAnds: And[] = []
|
||||
for (const tf of optimized) {
|
||||
if (tf instanceof Or) {
|
||||
if (tf["or"]) {
|
||||
// expand all the nested ors...
|
||||
newOrs.push(...tf.or)
|
||||
newOrs.push(...tf["or"])
|
||||
} else if (tf instanceof And) {
|
||||
// partition of all the ands
|
||||
containedAnds.push(tf)
|
||||
|
@ -191,7 +196,7 @@ export class Or extends TagsFilter {
|
|||
const cleanedContainedANds: And[] = []
|
||||
outer: for (let containedAnd of containedAnds) {
|
||||
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)
|
||||
// newOrs (and thus known): (K=V) --> false
|
||||
const cleaned = containedAnd.removePhraseConsideredKnown(known, false)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Tag } from "./Tag"
|
||||
import { TagsFilter } from "./TagsFilter"
|
||||
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
|
||||
|
||||
export class RegexTag extends TagsFilter {
|
||||
public readonly key: RegExp | string
|
||||
|
@ -82,6 +83,10 @@ export class RegexTag extends TagsFilter {
|
|||
}
|
||||
}
|
||||
|
||||
asJson(): TagConfigJson {
|
||||
return this.asHumanString()
|
||||
}
|
||||
|
||||
isUsableAsAnswer(): boolean {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { TagsFilter } from "./TagsFilter"
|
||||
import { Tag } from "./Tag"
|
||||
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.
|
||||
|
@ -45,6 +46,10 @@ export default class SubstitutingTag implements TagsFilter {
|
|||
)
|
||||
}
|
||||
|
||||
asJson(): TagConfigJson {
|
||||
return this._key + (this._invert ? "!" : "") + ":=" + this._value
|
||||
}
|
||||
|
||||
asOverpass(): string[] {
|
||||
throw "A variable with substitution can not be used to query overpass"
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Utils } from "../../Utils"
|
||||
import { TagsFilter } from "./TagsFilter"
|
||||
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
|
||||
|
||||
export class Tag extends TagsFilter {
|
||||
public key: string
|
||||
|
@ -67,6 +68,10 @@ export class Tag extends TagsFilter {
|
|||
return [`["${this.key}"="${this.value}"]`]
|
||||
}
|
||||
|
||||
asJson(): TagConfigJson {
|
||||
return this.key + "=" + this.value
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
const t = new Tag("key", "value")
|
||||
|
|
|
@ -15,13 +15,14 @@ type Tags = Record<string, string>
|
|||
export type UploadableTag = Tag | SubstitutingTag | And
|
||||
|
||||
export class TagUtils {
|
||||
public static readonly comparators: ReadonlyArray<[string, (a: number, b: number) => boolean]> =
|
||||
[
|
||||
["<=", (a, b) => a <= b],
|
||||
[">=", (a, b) => a >= b],
|
||||
["<", (a, b) => a < b],
|
||||
[">", (a, b) => a > b],
|
||||
]
|
||||
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],
|
||||
]
|
||||
public static modeDocumentation: Record<
|
||||
string,
|
||||
{ name: string; docs: string; uploadable?: boolean; overpassSupport: boolean }
|
||||
|
@ -735,11 +736,10 @@ export class TagUtils {
|
|||
const tag = json as string
|
||||
for (const [operator, comparator] of TagUtils.comparators) {
|
||||
if (tag.indexOf(operator) >= 0) {
|
||||
const split = Utils.SplitFirst(tag, operator)
|
||||
|
||||
let val = Number(split[1].trim())
|
||||
const split = Utils.SplitFirst(tag, operator).map((v) => v.trim())
|
||||
let val = Number(split[1])
|
||||
if (isNaN(val)) {
|
||||
val = new Date(split[1].trim()).getTime()
|
||||
val = new Date(split[1]).getTime()
|
||||
}
|
||||
|
||||
const f = (value: string | number | undefined) => {
|
||||
|
@ -762,7 +762,7 @@ export class TagUtils {
|
|||
}
|
||||
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 {
|
||||
abstract asOverpass(): string[]
|
||||
|
||||
|
@ -17,6 +19,8 @@ export abstract class TagsFilter {
|
|||
properties: Record<string, string>
|
||||
): string
|
||||
|
||||
abstract asJson(): TagConfigJson
|
||||
|
||||
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…
Reference in a new issue