2020-08-25 22:21:34 +00:00
|
|
|
import {Utils} from "../Utils";
|
2020-07-16 12:21:06 +00:00
|
|
|
|
|
|
|
export abstract class TagsFilter {
|
|
|
|
abstract matches(tags: { k: string, v: string }[]): boolean
|
|
|
|
abstract asOverpass(): string[]
|
|
|
|
abstract substituteValues(tags: any) : TagsFilter;
|
|
|
|
|
2020-08-29 23:13:18 +00:00
|
|
|
matchesProperties(properties: Map<string, string>): boolean {
|
2020-07-16 12:21:06 +00:00
|
|
|
return this.matches(TagUtils.proprtiesToKV(properties));
|
|
|
|
}
|
2020-08-22 15:33:08 +00:00
|
|
|
|
2020-08-27 16:44:16 +00:00
|
|
|
abstract asHumanString(linkToWiki: boolean, shorten: boolean);
|
2020-07-16 12:21:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-29 23:13:18 +00:00
|
|
|
export class RegexTag extends TagsFilter {
|
2020-09-03 00:05:09 +00:00
|
|
|
private readonly key: RegExp | string;
|
|
|
|
private readonly value: RegExp | string;
|
2020-08-29 23:13:18 +00:00
|
|
|
private readonly invert: boolean;
|
2020-06-23 22:35:19 +00:00
|
|
|
|
2020-09-03 00:05:09 +00:00
|
|
|
constructor(key: string | RegExp, value: RegExp | string, invert: boolean = false) {
|
2020-07-16 12:21:06 +00:00
|
|
|
super();
|
2020-09-03 00:05:09 +00:00
|
|
|
this.key = key;
|
2020-08-29 23:13:18 +00:00
|
|
|
this.value = value;
|
|
|
|
this.invert = invert;
|
2020-06-23 22:35:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
asOverpass(): string[] {
|
2020-09-03 00:05:09 +00:00
|
|
|
return [`['${RegexTag.source(this.key)}'${this.invert ? "!" : ""}~'${RegexTag.source(this.value)}']`];
|
|
|
|
}
|
|
|
|
|
|
|
|
private static doesMatch(fromTag: string, possibleRegex: string | RegExp): boolean {
|
|
|
|
if(typeof possibleRegex === "string"){
|
|
|
|
return fromTag === possibleRegex;
|
|
|
|
}
|
|
|
|
return fromTag.match(possibleRegex) !== null;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static source(r: string | RegExp) {
|
|
|
|
if (typeof (r) === "string") {
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
return r.source;
|
2020-06-23 22:35:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
matches(tags: { k: string; v: string }[]): boolean {
|
|
|
|
for (const tag of tags) {
|
2020-09-03 00:05:09 +00:00
|
|
|
if (RegexTag.doesMatch(tag.k, this.key)){
|
2020-09-03 17:05:18 +00:00
|
|
|
return RegexTag.doesMatch(tag.v, this.value) != this.invert;
|
2020-06-23 22:35:19 +00:00
|
|
|
}
|
|
|
|
}
|
2020-09-05 13:27:35 +00:00
|
|
|
// The matching key was not found
|
|
|
|
return this.invert;
|
2020-06-23 22:35:19 +00:00
|
|
|
}
|
|
|
|
|
2020-07-05 16:59:47 +00:00
|
|
|
substituteValues(tags: any) : TagsFilter{
|
2020-08-29 23:13:18 +00:00
|
|
|
return this;
|
2020-07-05 16:59:47 +00:00
|
|
|
}
|
2020-08-22 15:33:08 +00:00
|
|
|
|
|
|
|
asHumanString() {
|
2020-09-03 00:05:09 +00:00
|
|
|
return `${RegexTag.source(this.key)}${this.invert ? "!" : ""}~${RegexTag.source(this.value)}`;
|
2020-08-22 15:33:08 +00:00
|
|
|
}
|
2020-06-23 22:35:19 +00:00
|
|
|
}
|
|
|
|
|
2020-07-29 11:16:21 +00:00
|
|
|
|
2020-07-16 12:21:06 +00:00
|
|
|
export class Tag extends TagsFilter {
|
2020-07-29 22:59:08 +00:00
|
|
|
public key: string
|
2020-08-29 23:13:18 +00:00
|
|
|
public value: string
|
2020-07-29 15:16:59 +00:00
|
|
|
|
2020-08-29 23:13:18 +00:00
|
|
|
constructor(key: string, value: string) {
|
2020-07-16 12:21:06 +00:00
|
|
|
super()
|
2020-07-29 11:16:21 +00:00
|
|
|
this.key = key
|
|
|
|
this.value = value
|
2020-08-29 23:13:18 +00:00
|
|
|
if(key === undefined || key === ""){
|
|
|
|
throw "Invalid key";
|
|
|
|
}
|
|
|
|
if(value === undefined){
|
|
|
|
throw "Invalid value";
|
|
|
|
}
|
2020-08-31 00:59:47 +00:00
|
|
|
if(value === "*"){
|
2020-08-29 23:13:18 +00:00
|
|
|
console.warn(`Got suspicious tag ${key}=* ; did you mean ${key}!~*`)
|
2020-07-29 11:16:21 +00:00
|
|
|
}
|
2020-06-23 22:35:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
matches(tags: { k: string; v: string }[]): boolean {
|
2020-08-27 16:44:16 +00:00
|
|
|
if (this.value === "") {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-06-23 22:35:19 +00:00
|
|
|
for (const tag of tags) {
|
2020-08-29 23:13:18 +00:00
|
|
|
if (this.key == tag.k) {
|
2020-08-22 00:12:46 +00:00
|
|
|
|
2020-06-23 22:35:19 +00:00
|
|
|
if (tag.v === "") {
|
2020-08-22 00:12:46 +00:00
|
|
|
// This tag has been removed -> always matches false
|
|
|
|
return false;
|
2020-06-23 22:35:19 +00:00
|
|
|
}
|
2020-08-29 23:13:18 +00:00
|
|
|
|
|
|
|
if (this.value === tag.v) {
|
2020-08-22 00:12:46 +00:00
|
|
|
return true;
|
|
|
|
}
|
2020-06-23 22:35:19 +00:00
|
|
|
}
|
|
|
|
}
|
2020-08-29 23:13:18 +00:00
|
|
|
|
|
|
|
return false;
|
2020-06-23 22:35:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
asOverpass(): string[] {
|
|
|
|
if (this.value === "") {
|
|
|
|
// NOT having this key
|
2020-08-29 23:13:18 +00:00
|
|
|
return ['[!"' + this.key + '"]'];
|
2020-06-23 22:35:19 +00:00
|
|
|
}
|
2020-08-29 23:13:18 +00:00
|
|
|
return [`["${this.key}"="${this.value}"]`];
|
2020-06-23 22:35:19 +00:00
|
|
|
}
|
|
|
|
|
2020-07-05 16:59:47 +00:00
|
|
|
substituteValues(tags: any) {
|
2020-07-29 15:16:59 +00:00
|
|
|
return new Tag(this.key, TagUtils.ApplyTemplate(this.value as string, tags));
|
2020-07-05 16:59:47 +00:00
|
|
|
}
|
2020-08-22 15:33:08 +00:00
|
|
|
|
2020-08-27 16:44:16 +00:00
|
|
|
asHumanString(linkToWiki: boolean, shorten: boolean) {
|
2020-08-29 23:13:18 +00:00
|
|
|
let v = this.value;
|
2020-08-27 16:44:16 +00:00
|
|
|
if (shorten) {
|
|
|
|
v = Utils.EllipsesAfter(v, 25);
|
|
|
|
}
|
2020-08-22 16:57:27 +00:00
|
|
|
if (linkToWiki) {
|
|
|
|
return `<a href='https://wiki.openstreetmap.org/wiki/Key:${this.key}' target='_blank'>${this.key}</a>` +
|
|
|
|
`=` +
|
2020-08-25 22:21:34 +00:00
|
|
|
`<a href='https://wiki.openstreetmap.org/wiki/Tag:${this.key}%3D${this.value}' target='_blank'>${v}</a>`
|
2020-08-22 16:57:27 +00:00
|
|
|
}
|
2020-08-29 23:13:18 +00:00
|
|
|
return this.key + "=" + v;
|
2020-08-22 15:33:08 +00:00
|
|
|
}
|
2020-06-23 22:35:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-07-29 11:16:21 +00:00
|
|
|
export class Or extends TagsFilter {
|
2020-06-23 22:35:19 +00:00
|
|
|
public or: TagsFilter[]
|
|
|
|
|
|
|
|
constructor(or: TagsFilter[]) {
|
2020-07-16 12:21:06 +00:00
|
|
|
super();
|
2020-06-23 22:35:19 +00:00
|
|
|
this.or = or;
|
|
|
|
}
|
|
|
|
|
|
|
|
matches(tags: { k: string; v: string }[]): boolean {
|
|
|
|
for (const tagsFilter of this.or) {
|
|
|
|
if (tagsFilter.matches(tags)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
asOverpass(): string[] {
|
|
|
|
const choices = [];
|
|
|
|
for (const tagsFilter of this.or) {
|
|
|
|
const subChoices = tagsFilter.asOverpass();
|
|
|
|
for(const subChoice of subChoices){
|
|
|
|
choices.push(subChoice)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return choices;
|
|
|
|
}
|
|
|
|
|
2020-07-05 16:59:47 +00:00
|
|
|
substituteValues(tags: any): TagsFilter {
|
|
|
|
const newChoices = [];
|
|
|
|
for (const c of this.or) {
|
|
|
|
newChoices.push(c.substituteValues(tags));
|
|
|
|
}
|
|
|
|
return new Or(newChoices);
|
|
|
|
}
|
2020-08-22 16:57:27 +00:00
|
|
|
|
2020-08-27 16:44:16 +00:00
|
|
|
asHumanString(linkToWiki: boolean, shorten: boolean) {
|
|
|
|
return this.or.map(t => t.asHumanString(linkToWiki, shorten)).join("|");
|
2020-08-22 15:33:08 +00:00
|
|
|
}
|
2020-06-23 22:35:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-07-29 11:16:21 +00:00
|
|
|
export class And extends TagsFilter {
|
2020-06-23 22:35:19 +00:00
|
|
|
public and: TagsFilter[]
|
|
|
|
|
|
|
|
constructor(and: TagsFilter[]) {
|
2020-07-16 12:21:06 +00:00
|
|
|
super();
|
2020-06-23 22:35:19 +00:00
|
|
|
this.and = and;
|
|
|
|
}
|
|
|
|
|
|
|
|
matches(tags: { k: string; v: string }[]): boolean {
|
|
|
|
for (const tagsFilter of this.and) {
|
|
|
|
if (!tagsFilter.matches(tags)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-08-29 23:13:18 +00:00
|
|
|
private static combine(filter: string, choices: string[]): string[] {
|
|
|
|
const values = [];
|
2020-06-23 22:35:19 +00:00
|
|
|
for (const or of choices) {
|
|
|
|
values.push(filter + or);
|
|
|
|
}
|
|
|
|
return values;
|
|
|
|
}
|
|
|
|
|
|
|
|
asOverpass(): string[] {
|
2020-08-29 23:13:18 +00:00
|
|
|
let allChoices: string[] = null;
|
2020-06-23 22:35:19 +00:00
|
|
|
for (const andElement of this.and) {
|
2020-08-29 23:13:18 +00:00
|
|
|
const andElementFilter = andElement.asOverpass();
|
2020-06-23 22:35:19 +00:00
|
|
|
if (allChoices === null) {
|
|
|
|
allChoices = andElementFilter;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-08-29 23:13:18 +00:00
|
|
|
const newChoices: string[] = [];
|
|
|
|
for (const choice of allChoices) {
|
2020-06-23 22:35:19 +00:00
|
|
|
newChoices.push(
|
2020-08-29 23:13:18 +00:00
|
|
|
...And.combine(choice, andElementFilter)
|
2020-06-23 22:35:19 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
allChoices = newChoices;
|
|
|
|
}
|
|
|
|
return allChoices;
|
|
|
|
}
|
2020-07-05 16:59:47 +00:00
|
|
|
|
|
|
|
substituteValues(tags: any): TagsFilter {
|
|
|
|
const newChoices = [];
|
|
|
|
for (const c of this.and) {
|
|
|
|
newChoices.push(c.substituteValues(tags));
|
|
|
|
}
|
|
|
|
return new And(newChoices);
|
|
|
|
}
|
2020-08-22 15:33:08 +00:00
|
|
|
|
2020-08-27 16:44:16 +00:00
|
|
|
asHumanString(linkToWiki: boolean, shorten: boolean) {
|
|
|
|
return this.and.map(t => t.asHumanString(linkToWiki, shorten)).join("&");
|
2020-08-22 15:33:08 +00:00
|
|
|
}
|
2020-06-23 22:35:19 +00:00
|
|
|
}
|
|
|
|
|
2020-07-29 11:16:21 +00:00
|
|
|
|
2020-06-23 22:35:19 +00:00
|
|
|
|
|
|
|
export class TagUtils {
|
|
|
|
static proprtiesToKV(properties: any): { k: string, v: string }[] {
|
|
|
|
const result = [];
|
|
|
|
for (const k in properties) {
|
|
|
|
result.push({k: k, v: properties[k]})
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2020-07-05 16:59:47 +00:00
|
|
|
static ApplyTemplate(template: string, tags: any): string {
|
|
|
|
for (const k in tags) {
|
|
|
|
while (template.indexOf("{" + k + "}") >= 0) {
|
2020-07-29 17:43:15 +00:00
|
|
|
const escaped = tags[k].replace(/</g, '<').replace(/>/g, '>');
|
|
|
|
template = template.replace("{" + k + "}", escaped);
|
2020-07-05 16:59:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return template;
|
|
|
|
}
|
2020-08-22 00:12:46 +00:00
|
|
|
|
|
|
|
static KVtoProperties(tags: Tag[]): any {
|
|
|
|
const properties = {};
|
|
|
|
for (const tag of tags) {
|
|
|
|
properties[tag.key] = tag.value
|
|
|
|
}
|
|
|
|
return properties;
|
|
|
|
}
|
2020-07-29 11:16:21 +00:00
|
|
|
}
|