forked from MapComplete/MapComplete
		
	Add substituting tag, remove some old code
This commit is contained in:
		
							parent
							
								
									120832f241
								
							
						
					
					
						commit
						cd1171e678
					
				
					 8 changed files with 112 additions and 31 deletions
				
			
		|  | @ -5,6 +5,7 @@ import {Or} from "../../Logic/Tags/Or"; | ||||||
| import {And} from "../../Logic/Tags/And"; | import {And} from "../../Logic/Tags/And"; | ||||||
| import {Tag} from "../../Logic/Tags/Tag"; | import {Tag} from "../../Logic/Tags/Tag"; | ||||||
| import {TagsFilter} from "../../Logic/Tags/TagsFilter"; | import {TagsFilter} from "../../Logic/Tags/TagsFilter"; | ||||||
|  | import SubstitutingTag from "../../Logic/Tags/SubstitutingTag"; | ||||||
| 
 | 
 | ||||||
| export class FromJSON { | export class FromJSON { | ||||||
| 
 | 
 | ||||||
|  | @ -12,6 +13,7 @@ export class FromJSON { | ||||||
|         const tag = Utils.SplitFirst(json, "="); |         const tag = Utils.SplitFirst(json, "="); | ||||||
|         return new Tag(tag[0], tag[1]); |         return new Tag(tag[0], tag[1]); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     public static Tag(json: AndOrTagConfigJson | string, context: string = ""): TagsFilter { |     public static Tag(json: AndOrTagConfigJson | string, context: string = ""): TagsFilter { | ||||||
|         try { |         try { | ||||||
|             return this.TagUnsafe(json, context); |             return this.TagUnsafe(json, context); | ||||||
|  | @ -49,6 +51,11 @@ export class FromJSON { | ||||||
|                     new RegExp("^" + split[1] + "$") |                     new RegExp("^" + split[1] + "$") | ||||||
|                 ); |                 ); | ||||||
|             } |             } | ||||||
|  |             if(tag.indexOf(":=") >= 0){ | ||||||
|  |                 const split = Utils.SplitFirst(tag, ":="); | ||||||
|  |                 return new SubstitutingTag(split[0], split[1]); | ||||||
|  |             } | ||||||
|  |              | ||||||
|             if (tag.indexOf("!=") >= 0) { |             if (tag.indexOf("!=") >= 0) { | ||||||
|                 const split = Utils.SplitFirst(tag, "!="); |                 const split = Utils.SplitFirst(tag, "!="); | ||||||
|                 if (split[1] === "*") { |                 if (split[1] === "*") { | ||||||
|  |  | ||||||
|  | @ -35,3 +35,32 @@ Regex equals | ||||||
| A tag can also be tested against a regex with `key~regex`. Note that this regex __must match__ the entire value. If the value is allowed to appear anywhere as substring, use `key~.*regex.*` | A tag can also be tested against a regex with `key~regex`. Note that this regex __must match__ the entire value. If the value is allowed to appear anywhere as substring, use `key~.*regex.*` | ||||||
| 
 | 
 | ||||||
| Equivalently, `key!~regex` can be used if you _don't_ want to match the regex in order to appear. | Equivalently, `key!~regex` can be used if you _don't_ want to match the regex in order to appear. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | Using other tags as variables | ||||||
|  | ----------------------------- | ||||||
|  | 
 | ||||||
|  | **This is an advanced feature - use with caution** | ||||||
|  | 
 | ||||||
|  | Some tags are automatically set or calculated - see [CalculatedTags](CalculatedTags.md) for an entire overview. | ||||||
|  | If one wants to apply such a value as tag, use a substituting-tag such, for example`survey:date:={_date:now}`. Note that the separator between key and value here is `:=`. | ||||||
|  | The text between `{` and `}` is interpreted as a key, and the respective value is substituted into the string. | ||||||
|  | 
 | ||||||
|  | One can also append, e.g. `key:={some_key} fixed text {some_other_key}`. | ||||||
|  | 
 | ||||||
|  | An assigning tag _cannot_ be used to query OpenStreetMap/Overpass. | ||||||
|  | 
 | ||||||
|  | If using a key or variable which might not be defined, add a condition in the mapping to hide the option. | ||||||
|  | This is because, if `some_other_key` is not defined, one might actually upload the literal text `key={some_other_key}` to OSM - which we do not want. | ||||||
|  | 
 | ||||||
|  | To mitigate this, use: | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | "mappings": [ | ||||||
|  | { | ||||||
|  |     "if":"key:={some_other_key}" | ||||||
|  |     "then": "...", | ||||||
|  |     "hideInAnswer": "some_other_key=" | ||||||
|  | } | ||||||
|  | ] | ||||||
|  | ``` | ||||||
|  | @ -1,2 +1,5 @@ | ||||||
| <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> | <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> | ||||||
| 	<s:String x:Key="/Default/CodeInspection/Highlighting/SweaWarningsMode/@EntryValue">ShowAndRun</s:String></wpf:ResourceDictionary> | 	<s:String x:Key="/Default/CodeInspection/Highlighting/SweaWarningsMode/@EntryValue">ShowAndRun</s:String> | ||||||
|  | 	<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=d2f95dca_002Defa2_002D40b6_002D8190_002D724496f13a75/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" Name="Session" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> | ||||||
|  |   <Nothing /> | ||||||
|  | </SessionState></s:String></wpf:ResourceDictionary> | ||||||
|  | @ -46,14 +46,6 @@ export class And extends TagsFilter { | ||||||
|         return allChoices; |         return allChoices; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     substituteValues(tags: any): TagsFilter { |  | ||||||
|         const newChoices = []; |  | ||||||
|         for (const c of this.and) { |  | ||||||
|             newChoices.push(c.substituteValues(tags)); |  | ||||||
|         } |  | ||||||
|         return new And(newChoices); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     asHumanString(linkToWiki: boolean, shorten: boolean) { |     asHumanString(linkToWiki: boolean, shorten: boolean) { | ||||||
|         return this.and.map(t => t.asHumanString(linkToWiki, shorten)).join("&"); |         return this.and.map(t => t.asHumanString(linkToWiki, shorten)).join("&"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -30,14 +30,6 @@ export class Or extends TagsFilter { | ||||||
|         return choices; |         return choices; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     substituteValues(tags: any): TagsFilter { |  | ||||||
|         const newChoices = []; |  | ||||||
|         for (const c of this.or) { |  | ||||||
|             newChoices.push(c.substituteValues(tags)); |  | ||||||
|         } |  | ||||||
|         return new Or(newChoices); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     asHumanString(linkToWiki: boolean, shorten: boolean) { |     asHumanString(linkToWiki: boolean, shorten: boolean) { | ||||||
|         return this.or.map(t => t.asHumanString(linkToWiki, shorten)).join("|"); |         return this.or.map(t => t.asHumanString(linkToWiki, shorten)).join("|"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
							
								
								
									
										59
									
								
								Logic/Tags/SubstitutingTag.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								Logic/Tags/SubstitutingTag.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,59 @@ | ||||||
|  | import {TagsFilter} from "./TagsFilter"; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * The substituting-tag uses the tags of a feature a variables and replaces them. | ||||||
|  |  * | ||||||
|  |  * e.g. key:={other_key}_{ref} will match an object that has at least 'key'. | ||||||
|  |  * If {other_key} is _not_ defined, it will not be substituted. | ||||||
|  |  * | ||||||
|  |  * The 'key' is always fixed and should not contain substitutions. | ||||||
|  |  * This cannot be used to query features | ||||||
|  |  */ | ||||||
|  | export default class SubstitutingTag implements TagsFilter { | ||||||
|  |     private readonly _key: string; | ||||||
|  |     private readonly _value: string; | ||||||
|  | 
 | ||||||
|  |     constructor(key: string, value: string) { | ||||||
|  |         this._key = key; | ||||||
|  |         this._value = value; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private static substituteString(template: string, dict: any) { | ||||||
|  |         for (const k in dict) { | ||||||
|  |             template = template.replace(new RegExp("\\{" + k + "\\}", 'g'), dict[k]) | ||||||
|  |         } | ||||||
|  |         return template; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     asHumanString(linkToWiki: boolean, shorten: boolean) { | ||||||
|  |         return this._key + ":=" + this._value; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     asOverpass(): string[] { | ||||||
|  |         throw "A variable with substitution can not be used to query overpass" | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     isEquivalent(other: TagsFilter): boolean { | ||||||
|  |         if (!(other instanceof SubstitutingTag)) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         return other._key === this._key && other._value === this._value; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     isUsableAsAnswer(): boolean { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     matchesProperties(properties: any): boolean { | ||||||
|  |         const value = properties[this._key]; | ||||||
|  |         if (value === undefined || value === "") { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         const expectedValue = SubstitutingTag.substituteString(this._value, properties); | ||||||
|  |         return value === expectedValue; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     usedKeys(): string[] { | ||||||
|  |         return [this._key]; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -2,8 +2,6 @@ export abstract class TagsFilter { | ||||||
| 
 | 
 | ||||||
|     abstract asOverpass(): string[] |     abstract asOverpass(): string[] | ||||||
| 
 | 
 | ||||||
|     abstract substituteValues(tags: any): TagsFilter; |  | ||||||
| 
 |  | ||||||
|     abstract isUsableAsAnswer(): boolean; |     abstract isUsableAsAnswer(): boolean; | ||||||
| 
 | 
 | ||||||
|     abstract isEquivalent(other: TagsFilter): boolean; |     abstract isEquivalent(other: TagsFilter): boolean; | ||||||
|  | @ -14,11 +12,4 @@ export abstract class TagsFilter { | ||||||
| 
 | 
 | ||||||
|     abstract usedKeys(): string[]; |     abstract usedKeys(): string[]; | ||||||
|      |      | ||||||
|     public matches(tags: { k: string, v: string }[]) { |  | ||||||
|         const properties = {}; |  | ||||||
|         for (const kv of tags) { |  | ||||||
|             properties[kv.k] = kv.v; |  | ||||||
|         } |  | ||||||
|         return this.matchesProperties(properties); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  | @ -79,6 +79,14 @@ new T("Tags", [ | ||||||
|         equal(nameStartsWith.matchesProperties({"name": "Sint-Anna"}), false) |         equal(nameStartsWith.matchesProperties({"name": "Sint-Anna"}), false) | ||||||
|         equal(nameStartsWith.matchesProperties({"name": ""}), false) |         equal(nameStartsWith.matchesProperties({"name": ""}), false) | ||||||
|          |          | ||||||
|  |          | ||||||
|  |         const assign = FromJSON.Tag("survey:date:={_date:now}") | ||||||
|  |         equal(assign.matchesProperties({"survey:date":"2021-03-29", "_date:now":"2021-03-29"}), true); | ||||||
|  |         equal(assign.matchesProperties({"survey:date":"2021-03-29", "_date:now":"2021-01-01"}), false); | ||||||
|  |         equal(assign.matchesProperties({"survey:date":"2021-03-29"}), false); | ||||||
|  |         equal(assign.matchesProperties({"_date:now":"2021-03-29"}), false); | ||||||
|  |         equal(assign.matchesProperties({"some_key":"2021-03-29"}), false); | ||||||
|  | 
 | ||||||
|     })], |     })], | ||||||
|     ["Is equivalent test", (() => { |     ["Is equivalent test", (() => { | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue