2020-07-16 14:21:06 +02:00
|
|
|
|
|
|
|
export abstract class TagsFilter {
|
|
|
|
abstract matches(tags: { k: string, v: string }[]): boolean
|
|
|
|
abstract asOverpass(): string[]
|
|
|
|
abstract substituteValues(tags: any) : TagsFilter;
|
|
|
|
|
|
|
|
matchesProperties(properties: any) : boolean{
|
|
|
|
return this.matches(TagUtils.proprtiesToKV(properties));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export class Regex extends TagsFilter {
|
2020-06-24 00:35:19 +02:00
|
|
|
private _k: string;
|
|
|
|
private _r: string;
|
|
|
|
|
|
|
|
constructor(k: string, r: string) {
|
2020-07-16 14:21:06 +02:00
|
|
|
super();
|
2020-06-24 00:35:19 +02:00
|
|
|
this._k = k;
|
|
|
|
this._r = r;
|
|
|
|
}
|
|
|
|
|
|
|
|
asOverpass(): string[] {
|
|
|
|
return ["['" + this._k + "'~'" + this._r + "']"];
|
|
|
|
}
|
|
|
|
|
|
|
|
matches(tags: { k: string; v: string }[]): boolean {
|
2020-07-17 17:21:07 +02:00
|
|
|
if(!(tags instanceof Array)){
|
|
|
|
throw "You used 'matches' on something that is not a list. Did you mean to use 'matchesProperties'?"
|
|
|
|
}
|
|
|
|
|
2020-06-24 00:35:19 +02:00
|
|
|
for (const tag of tags) {
|
|
|
|
if (tag.k === this._k) {
|
|
|
|
if (tag.v === "") {
|
|
|
|
// This tag has been removed
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (this._r === "*") {
|
|
|
|
// Any is allowed
|
|
|
|
return true;
|
|
|
|
}
|
2020-07-12 23:19:05 +02:00
|
|
|
|
2020-06-24 00:35:19 +02:00
|
|
|
|
2020-07-12 23:19:05 +02:00
|
|
|
const matchCount =tag.v.match(this._r)?.length;
|
|
|
|
return (matchCount ?? 0) > 0;
|
2020-06-24 00:35:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-07-05 18:59:47 +02:00
|
|
|
substituteValues(tags: any) : TagsFilter{
|
|
|
|
throw "Substituting values is not supported on regex tags"
|
|
|
|
}
|
|
|
|
|
2020-06-24 00:35:19 +02:00
|
|
|
}
|
|
|
|
|
2020-07-16 14:21:06 +02:00
|
|
|
export class Tag extends TagsFilter {
|
2020-06-24 00:35:19 +02:00
|
|
|
public key: string;
|
|
|
|
public value: string;
|
|
|
|
|
|
|
|
constructor(key: string, value: string) {
|
2020-07-16 14:21:06 +02:00
|
|
|
super()
|
2020-06-24 00:35:19 +02:00
|
|
|
this.key = key;
|
|
|
|
this.value = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
matches(tags: { k: string; v: string }[]): boolean {
|
|
|
|
for (const tag of tags) {
|
|
|
|
if (tag.k === this.key) {
|
|
|
|
if (tag.v === "") {
|
|
|
|
// This tag has been removed
|
2020-07-05 18:59:47 +02:00
|
|
|
return this.value === "";
|
2020-06-24 00:35:19 +02:00
|
|
|
}
|
|
|
|
if (this.value === "*") {
|
|
|
|
// Any is allowed
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.value === tag.v;
|
|
|
|
}
|
|
|
|
}
|
2020-07-05 18:59:47 +02:00
|
|
|
if(this.value === ""){
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-06-24 00:35:19 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
asOverpass(): string[] {
|
|
|
|
if (this.value === "*") {
|
|
|
|
return ['["' + this.key + '"]'];
|
|
|
|
}
|
|
|
|
if (this.value === "") {
|
|
|
|
// NOT having this key
|
|
|
|
return ['[!"' + this.key + '"]'];
|
|
|
|
}
|
|
|
|
return ['["' + this.key + '"="' + this.value + '"]'];
|
|
|
|
}
|
|
|
|
|
2020-07-05 18:59:47 +02:00
|
|
|
substituteValues(tags: any) {
|
|
|
|
return new Tag(this.key, TagUtils.ApplyTemplate(this.value, tags));
|
|
|
|
}
|
|
|
|
|
2020-06-24 00:35:19 +02:00
|
|
|
}
|
|
|
|
|
2020-07-16 14:21:06 +02:00
|
|
|
export class Or extends TagsFilter {
|
2020-06-24 00:35:19 +02:00
|
|
|
|
|
|
|
public or: TagsFilter[]
|
|
|
|
|
|
|
|
constructor(or: TagsFilter[]) {
|
2020-07-16 14:21:06 +02:00
|
|
|
super();
|
2020-06-24 00:35:19 +02: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 18:59:47 +02:00
|
|
|
substituteValues(tags: any): TagsFilter {
|
|
|
|
const newChoices = [];
|
|
|
|
for (const c of this.or) {
|
|
|
|
newChoices.push(c.substituteValues(tags));
|
|
|
|
}
|
|
|
|
return new Or(newChoices);
|
|
|
|
}
|
|
|
|
|
2020-06-24 00:35:19 +02:00
|
|
|
}
|
|
|
|
|
2020-07-16 14:21:06 +02:00
|
|
|
export class And extends TagsFilter {
|
2020-06-24 00:35:19 +02:00
|
|
|
|
|
|
|
public and: TagsFilter[]
|
|
|
|
|
|
|
|
constructor(and: TagsFilter[]) {
|
2020-07-16 14:21:06 +02:00
|
|
|
super();
|
2020-06-24 00:35:19 +02: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;
|
|
|
|
}
|
|
|
|
|
|
|
|
private combine(filter: string, choices: string[]): string[] {
|
|
|
|
var values = []
|
|
|
|
for (const or of choices) {
|
|
|
|
values.push(filter + or);
|
|
|
|
}
|
|
|
|
return values;
|
|
|
|
}
|
|
|
|
|
|
|
|
asOverpass(): string[] {
|
|
|
|
|
|
|
|
var allChoices = null;
|
|
|
|
|
|
|
|
for (const andElement of this.and) {
|
|
|
|
var andElementFilter = andElement.asOverpass();
|
|
|
|
if (allChoices === null) {
|
|
|
|
allChoices = andElementFilter;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
var newChoices = []
|
|
|
|
for (var choice of allChoices) {
|
|
|
|
newChoices.push(
|
|
|
|
this.combine(choice, andElementFilter)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
allChoices = newChoices;
|
|
|
|
}
|
|
|
|
return allChoices;
|
|
|
|
}
|
2020-07-05 18:59:47 +02:00
|
|
|
|
|
|
|
substituteValues(tags: any): TagsFilter {
|
|
|
|
const newChoices = [];
|
|
|
|
for (const c of this.and) {
|
|
|
|
newChoices.push(c.substituteValues(tags));
|
|
|
|
}
|
|
|
|
return new And(newChoices);
|
|
|
|
}
|
2020-06-24 00:35:19 +02:00
|
|
|
}
|
|
|
|
|
2020-07-17 17:21:07 +02:00
|
|
|
export class Not extends TagsFilter{
|
|
|
|
private not: TagsFilter;
|
|
|
|
|
|
|
|
constructor(not: TagsFilter) {
|
|
|
|
super();
|
|
|
|
this.not = not;
|
|
|
|
}
|
|
|
|
|
|
|
|
asOverpass(): string[] {
|
|
|
|
throw "Not supported yet"
|
|
|
|
}
|
|
|
|
|
|
|
|
matches(tags: { k: string; v: string }[]): boolean {
|
|
|
|
return !this.not.matches(tags);
|
|
|
|
}
|
|
|
|
|
|
|
|
substituteValues(tags: any): TagsFilter {
|
|
|
|
return new Not(this.not.substituteValues(tags));
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-06-24 00:35:19 +02: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 18:59:47 +02:00
|
|
|
static ApplyTemplate(template: string, tags: any): string {
|
|
|
|
for (const k in tags) {
|
|
|
|
while (template.indexOf("{" + k + "}") >= 0) {
|
|
|
|
template = template.replace("{" + k + "}", tags[k]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return template;
|
|
|
|
}
|
|
|
|
|
2020-06-24 00:35:19 +02:00
|
|
|
}
|