forked from MapComplete/MapComplete
		
	Refactoring: move documentation about tagsFilters into TagUtils
This commit is contained in:
		
							parent
							
								
									00776c1e6b
								
							
						
					
					
						commit
						48e976e6b7
					
				
					 1 changed files with 209 additions and 6 deletions
				
			
		|  | @ -13,13 +13,201 @@ 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 modeDocumentation: Record< | ||||
|         string, | ||||
|         { name: string; docs: string; uploadable?: boolean; overpassSupport: boolean } | ||||
|     > = { | ||||
|         "=": { | ||||
|             name: "strict equality", | ||||
|             uploadable: true, | ||||
|             overpassSupport: true, | ||||
|             docs: | ||||
|                 "Strict equality is denoted by `key=value`. This key matches __only if__ the keypair is present exactly as stated.\n" + | ||||
|                 "\n" + | ||||
|                 "**Only normal tags (eventually in an `and`) can be used in places where they are uploaded**. Normal tags are used in " + | ||||
|                 "the `mappings` of a [TagRendering] (unless `hideInAnswer` is specified), they are used in `addExtraTags` of [Freeform] " + | ||||
|                 "and are used in the `tags`-list of a preset.\n" + | ||||
|                 "\n" + | ||||
|                 "If a different kind of tag specification is given, your theme will fail to parse.\n" + | ||||
|                 "\n" + | ||||
|                 "### If key is not present\n" + | ||||
|                 "\n" + | ||||
|                 "If you want to check if a key is not present, use `key=` (pronounce as *key is empty*). A tag collection will match this\n" + | ||||
|                 "if `key` is missing or if `key` is a literal empty value.\n" + | ||||
|                 "\n" + | ||||
|                 "### Removing a key\n" + | ||||
|                 "\n" + | ||||
|                 "If a key should be deleted in the OpenStreetMap-database, specify `key=` as well. This can be used e.g. to remove a\n" + | ||||
|                 "fixme or value from another mapping if another field is filled out.", | ||||
|         }, | ||||
|         "!=": { | ||||
|             name: "strict not equals", | ||||
|             overpassSupport: true, | ||||
|             docs: | ||||
|                 "To check if a key does _not_ equal a certain value, use `key!=value`. This is converted behind the scenes\n" + | ||||
|                 "to `key!~^value$`\n" + | ||||
|                 "\n" + | ||||
|                 "If `key` is not present or empty, this will match too.\n" + | ||||
|                 "\n" + | ||||
|                 "### If key is present\n" + | ||||
|                 "\n" + | ||||
|                 "This implies that, to check if a key is present, `key!=` can be used. This will only match if the key is present and not\n" + | ||||
|                 "empty.", | ||||
|         }, | ||||
|         "~": { | ||||
|             name: "Value matches regex", | ||||
|             overpassSupport: true, | ||||
|             docs: | ||||
|                 "A tag can also be tested against a regex with `key~regex`. Note that this regex __must match__ the entire value. If the\n" + | ||||
|                 "value is allowed to appear anywhere as substring, use `key~.*regex.*`.\n" + | ||||
|                 "The regex is put within braces as to prevent runaway values.\n" + | ||||
|                 "\nUse `key~*` to indicate that any value is allowed. This is effectively the check that the attribute is present (defined _and_ not empty)." + | ||||
|                 "\n" + | ||||
|                 "Regexes will match the newline character with `.` too - the `s`-flag is enabled by default.", | ||||
|         }, | ||||
|         "~i~": { | ||||
|             name: "Value matches case-invariant regex", | ||||
|             overpassSupport: true, | ||||
|             docs: "A tag can also be tested against a regex with `key~i~regex`, where the case of the value will be ignored. The regex is still matched against the _entire_ value", | ||||
|         }, | ||||
|         "!~": { | ||||
|             name: "Value should _not_ match regex", | ||||
|             overpassSupport: true, | ||||
|             docs: | ||||
|                 "A tag can also be tested against a regex with `key!~regex`. This filter will match if the value does *not* match the regex. " + | ||||
|                 "\n If the\n" + | ||||
|                 "value is allowed to appear anywhere as substring, use `key~.*regex.*`.\n" + | ||||
|                 "The regex is put within braces as to prevent runaway values.\n", | ||||
|         }, | ||||
|         "!~i~": { | ||||
|             name: "Value does *not* match case-invariant regex", | ||||
|             overpassSupport: true, | ||||
|             docs: "A tag can also be tested against a regex with `key~i~regex`, where the case of the value will be ignored. The regex is still matched against the _entire_ value. This filter returns true if the value does *not* match", | ||||
|         }, | ||||
|         "~~": { | ||||
|             name: "Key and value should match given regex", | ||||
|             overpassSupport: true, | ||||
|             docs: "Both the `key` and `value` part of this specification are interpreted as regexes, both the key and value musth completely match their respective regexes", | ||||
|         }, | ||||
|         ":=": { | ||||
|             name: "Substitute `{some_key}` should match `key`", | ||||
|             overpassSupport: false, | ||||
|             docs: | ||||
|                 "**This is an advanced feature - use with caution**\n" + | ||||
|                 "\n" + | ||||
|                 "Some tags are automatically set or calculated - see [CalculatedTags](CalculatedTags.md) for an entire overview. If one\n" + | ||||
|                 "wants to apply such a value as tag, use a substituting-tag such, for example`survey:date:={_date:now}`. Note that the\n" + | ||||
|                 "separator between key and value here is `:=`. The text between `{` and `}` is interpreted as a key, and the respective\n" + | ||||
|                 "value is substituted into the string.\n" + | ||||
|                 "\n" + | ||||
|                 "One can also append, e.g. `key:={some_key} fixed text {some_other_key}`.\n" + | ||||
|                 "\n" + | ||||
|                 "An assigning tag _cannot_ be used to query OpenStreetMap/Overpass.\n" + | ||||
|                 "\n" + | ||||
|                 "If using a key or variable which might not be defined, add a condition in the mapping to hide the option. This is\n" + | ||||
|                 "because, if `some_other_key` is not defined, one might actually upload the literal text `key={some_other_key}` to OSM -\n" + | ||||
|                 "which we do not want.\n" + | ||||
|                 "\n" + | ||||
|                 "To mitigate this, use:\n" + | ||||
|                 "\n" + | ||||
|                 "```json\n" + | ||||
|                 "{\n" + | ||||
|                 '    "mappings": [\n' + | ||||
|                 "        {\n" + | ||||
|                 '            "if":"key:={some_other_key}",\n' + | ||||
|                 '            "then": "...",\n' + | ||||
|                 '            "hideInAnswer": "some_other_key="\n' + | ||||
|                 "        }\n" + | ||||
|                 "    ]\n" + | ||||
|                 "}\n" + | ||||
|                 "```\n" + | ||||
|                 "\n" + | ||||
|                 "One can use `key!:=prefix-{other_key}-postfix` as well, to match if `key` is _not_ the same\n" + | ||||
|                 "as `prefix-{other_key}-postfix` (with `other_key` substituted by the value)", | ||||
|         }, | ||||
|         "!:=": { | ||||
|             name: "Substitute `{some_key}` should not match `key`", | ||||
|             overpassSupport: false, | ||||
|             docs: "See `:=`, except that this filter is inverted", | ||||
|         }, | ||||
|     } | ||||
|     private static keyCounts: { keys: any; tags: any } = key_counts | ||||
|     private static comparators: [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 numberAndDateComparisonDocs = | ||||
|         "If the value of a tag is a number (e.g. `key=42`), one can use a filter `key<=42`, `key>=35`, `key>40` or `key<50` to\n" + | ||||
|         "match this, e.g. in conditions for renderings. These tags cannot be used to generate an answer nor can they be used to\n" + | ||||
|         "request data upstream from overpass.\n" + | ||||
|         "\n" + | ||||
|         "Note that the value coming from OSM will first be stripped by removing all non-numeric characters. For\n" + | ||||
|         "example, `length=42 meter` will be interpreted as `length=42` and will thus match `length<=42` and `length>=42`. In\n" + | ||||
|         "special circumstances (e.g. `surface_area=42 m2` or `length=100 feet`), this will result in erronous\n" + | ||||
|         "values (`surface=422` or if a length in meters is compared to). However, this can be partially alleviated by using '\n" + | ||||
|         "Units' to rewrite to a default format.\n" + | ||||
|         "\n" + | ||||
|         "Dates can be compared with the same expression: `somekey<2022-06-22` will match if `somekey` is a date and is smaller\n" + | ||||
|         "then 22nd june '22." | ||||
| 
 | ||||
|     public static readonly logicalOperator = | ||||
|         "\n" + | ||||
|         "## Logical operators\n" + | ||||
|         "\n" + | ||||
|         "One can combine multiple tags by using `and` or `or`, e.g.:\n" + | ||||
|         "\n" + | ||||
|         "```json\n" + | ||||
|         "{\n" + | ||||
|         '  "osmTags": {\n' + | ||||
|         '    "or": [\n' + | ||||
|         '      "amenity=school",\n' + | ||||
|         '      "amenity=kindergarten"\n' + | ||||
|         "    ]\n" + | ||||
|         "  }\n" + | ||||
|         "}\n" + | ||||
|         "```\n" | ||||
| 
 | ||||
|     public static readonly intro = | ||||
|         "Tags format\n" + | ||||
|         "=============\n" + | ||||
|         "\n" + | ||||
|         "When creating the `json` file describing your layer or theme, you'll have to add a few tags to describe what you want.\n" + | ||||
|         "This document gives an overview of what every expression means and how it behaves in edge cases.\n" + | ||||
|         "\n" + | ||||
|         "If the schema-files note a type [`TagConfigJson`](https://github.com/pietervdvn/MapComplete/blob/develop/Models/ThemeConfig/Json/TagConfigJson.ts), you can use one of these values.\n" + | ||||
|         "\n" + | ||||
|         "In some cases, not every type of tags-filter can be used. For example,  _rendering_ an option with a regex is\n" + | ||||
|         'fine (`"if": "brand~[Bb]randname", "then":" The brand is Brandname"`); but this regex can not be used to write a value\n' + | ||||
|         "into the database. The theme loader will however refuse to work with such inconsistencies and notify you of this while\n" + | ||||
|         "you are building your theme.\n" + | ||||
|         "\n" + | ||||
|         "Example\n" + | ||||
|         "-------\n" + | ||||
|         "\n" + | ||||
|         "This example shows the most common options on how to specify tags:\n" + | ||||
|         "\n" + | ||||
|         "```json\n" + | ||||
|         "{\n" + | ||||
|         '  "and": [\n' + | ||||
|         '    "key=value",\n' + | ||||
|         "    {\n" + | ||||
|         '      "or": [\n' + | ||||
|         '        "other_key=value",\n' + | ||||
|         '        "other_key=some_other_value"\n' + | ||||
|         "      ]\n" + | ||||
|         "    },\n" + | ||||
|         '    "key_which_should_be_missing=",\n' + | ||||
|         '    "key_which_should_have_a_value~*",\n' + | ||||
|         '    "key~.*some_regex_a*_b+_[a-z]?",\n' + | ||||
|         '    "height<1"\n' + | ||||
|         "  ]\n" + | ||||
|         "}\n" + | ||||
|         "```\n" + | ||||
|         "\n" + | ||||
|         "\n" | ||||
| 
 | ||||
|     static KVtoProperties(tags: Tag[]): Record<string, string> { | ||||
|         const properties: Record<string, string> = {} | ||||
|  | @ -688,4 +876,19 @@ export class TagUtils { | |||
|         } | ||||
|         return " (" + joined + ") " | ||||
|     } | ||||
| 
 | ||||
|     public static generateDocs(): string { | ||||
|         return [ | ||||
|             TagUtils.intro, | ||||
|             ...Object.keys(TagUtils.modeDocumentation).map((mode) => { | ||||
|                 const doc = TagUtils.modeDocumentation[mode] | ||||
|                 return ["", "## `" + mode + "` " + doc.name, "", doc.docs, "", ""].join("\n") | ||||
|             }), | ||||
|             "## " + | ||||
|                 TagUtils.comparators.map((comparator) => "`" + comparator[0] + "`").join(" ") + | ||||
|                 " Logical comparators", | ||||
|             TagUtils.numberAndDateComparisonDocs, | ||||
|             TagUtils.logicalOperator, | ||||
|         ].join("\n") | ||||
|     } | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue