mapcomplete/Logic/TagsFilter.ts

224 lines
5.2 KiB
TypeScript
Raw Normal View History

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;
matchesProperties(properties: any) : boolean{
return this.matches(TagUtils.proprtiesToKV(properties));
}
}
export class Regex extends TagsFilter {
2020-06-23 22:35:19 +00:00
private _k: string;
private _r: string;
constructor(k: string, r: string) {
2020-07-16 12:21:06 +00:00
super();
2020-06-23 22:35:19 +00:00
this._k = k;
this._r = r;
}
asOverpass(): string[] {
return ["['" + this._k + "'~'" + this._r + "']"];
}
matches(tags: { k: string; v: string }[]): boolean {
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 21:19:05 +00:00
2020-06-23 22:35:19 +00:00
2020-07-12 21:19:05 +00:00
const matchCount =tag.v.match(this._r)?.length;
return (matchCount ?? 0) > 0;
2020-06-23 22:35:19 +00:00
}
}
return false;
}
2020-07-05 16:59:47 +00:00
substituteValues(tags: any) : TagsFilter{
throw "Substituting values is not supported on regex tags"
}
2020-06-23 22:35:19 +00:00
}
2020-07-16 12:21:06 +00:00
export class Tag extends TagsFilter {
2020-06-23 22:35:19 +00:00
public key: string;
public value: string;
constructor(key: string, value: string) {
2020-07-16 12:21:06 +00:00
super()
2020-06-23 22:35:19 +00: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 16:59:47 +00:00
return this.value === "";
2020-06-23 22:35:19 +00:00
}
if (this.value === "*") {
// Any is allowed
return true;
}
return this.value === tag.v;
}
}
2020-07-05 16:59:47 +00:00
if(this.value === ""){
return true;
}
2020-06-23 22:35:19 +00: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 16:59:47 +00:00
substituteValues(tags: any) {
return new Tag(this.key, TagUtils.ApplyTemplate(this.value, tags));
}
2020-06-23 22:35:19 +00:00
}
2020-07-16 12:21:06 +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-06-23 22:35:19 +00:00
}
2020-07-16 12:21:06 +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;
}
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 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-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) {
template = template.replace("{" + k + "}", tags[k]);
}
}
return template;
}
2020-06-23 22:35:19 +00:00
}