forked from MapComplete/MapComplete
		
	This commit is contained in:
		
							parent
							
								
									52d4adee84
								
							
						
					
					
						commit
						4e73d9b5f9
					
				
					 1 changed files with 104 additions and 30 deletions
				
			
		|  | @ -5,7 +5,7 @@ import LayerConfig from "../Models/ThemeConfig/LayerConfig" | |||
| import { CountryCoder } from "latlon2country" | ||||
| import Constants from "../Models/Constants" | ||||
| import { TagUtils } from "./Tags/TagUtils" | ||||
| import { Feature, LineString } from "geojson" | ||||
| import { Feature, LineString, MultiPolygon, Polygon } from "geojson" | ||||
| import { OsmTags } from "../Models/OsmFeature" | ||||
| import { UIEventSource } from "./UIEventSource" | ||||
| import ThemeConfig from "../Models/ThemeConfig/ThemeConfig" | ||||
|  | @ -80,7 +80,7 @@ export class ReferencingWaysMetaTagger extends SimpleMetaTagger { | |||
|         super({ | ||||
|             keys: ["_referencing_ways"], | ||||
|             isLazy: true, | ||||
|             doc: "_referencing_ways contains - for a node - which ways use this node as point in their geometry. ", | ||||
|             doc: "_referencing_ways contains - for a node - which ways use this node as point in their geometry. " | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|  | @ -116,7 +116,7 @@ class CountryTagger extends SimpleMetaTagger { | |||
|         super({ | ||||
|             keys: ["_country"], | ||||
|             doc: "The country codes of the of the country/countries that the feature is located in (with latlon2country). Might contain _multiple_ countries, separated by a `;`", | ||||
|             includesDates: false, | ||||
|             includesDates: false | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|  | @ -213,9 +213,9 @@ class RewriteMetaInfoTags extends SimpleMetaTagger { | |||
|                 "_last_edit:changeset", | ||||
|                 "_last_edit:timestamp", | ||||
|                 "_version_number", | ||||
|                 "_backend", | ||||
|                 "_backend" | ||||
|             ], | ||||
|             doc: "Information about the last edit of this object. This object will actually _rewrite_ some tags for features coming from overpass", | ||||
|             doc: "Information about the last edit of this object. This object will actually _rewrite_ some tags for features coming from overpass" | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|  | @ -244,6 +244,69 @@ class RewriteMetaInfoTags extends SimpleMetaTagger { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| class NormalizePanoramax extends SimpleMetaTagger { | ||||
|     constructor() { | ||||
|         super( | ||||
|             { | ||||
|                 keys: ["panoramax"], | ||||
|                 doc: "Converts a `panoramax=hash1;hash2;hash3;...` into `panoramax=hash1`,`panoramax:0=hash1`...", | ||||
|                 isLazy: false, | ||||
|                 cleanupRetagger: true | ||||
|             }) | ||||
|     } | ||||
| 
 | ||||
|     private addValue(comesFromKey: string, tags: Record<string, string>, hashesToAdd: string[], postfix?: string) { | ||||
|         let basekey = "panoramax" | ||||
|         if (postfix) { | ||||
|             basekey = "panoramax:" + postfix | ||||
|         } | ||||
|         let index = -1 | ||||
|         for (let i = 0; i < hashesToAdd.length; i++) { | ||||
|             let k = basekey | ||||
|             do { | ||||
|                 if (index >= 0) { | ||||
|                     k = `${basekey}:${index}` | ||||
|                 } | ||||
|                 index++ | ||||
|             } while (k !== comesFromKey && tags[k]) | ||||
|             tags[k] = hashesToAdd[i] | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * const tags = new UIEventSource({panoramax: "abc;def;ghi", "panoramax:2": "xyz;uvw", "panoramax:streetsign":"a;b;c"}) | ||||
|      * const _ = undefined | ||||
|      * new NormalizePanoramax().applyMetaTagsOnFeature(_, _, tags, _) | ||||
|      * tags.data // => {"panoramax": "abc", "panoramax:0" : "def", "panoramax:1": "ghi", "panoramax:2":"xyz", "panoramax:3":"uvw", "panoramax:streetsign":"a", "panoramax:streetsign:0":"b","panoramax:streetsign:1": "c"}
 | ||||
|      */ | ||||
|     applyMetaTagsOnFeature(feature: Feature, layer: LayerConfig, tags: UIEventSource<Record<string, string>>): boolean { | ||||
|         const tgs = tags.data | ||||
|         let somethingChanged = false | ||||
|         for (const key in tgs) { | ||||
|             if (!(key === "panoramax" || key.startsWith("panoramax:"))) { | ||||
|                 continue | ||||
|             } | ||||
|             const v = tgs[key] | ||||
|             if (v.indexOf(";") < 0) { | ||||
|                 continue | ||||
|             } | ||||
|             const parts = v.split(";") | ||||
|             if (key === "panoramax" || key.match("panoramax:[0-9]+")) { | ||||
|                 this.addValue(key, tgs, parts) | ||||
|                 somethingChanged = true | ||||
|             } else { | ||||
| 
 | ||||
|                 const postfix = key.match(/panoramax:([^:]+)(:[0-9]+)?/)?.[1] | ||||
|                 if (postfix) { | ||||
|                     this.addValue(key, tgs, parts, postfix) | ||||
|                     somethingChanged = true | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return somethingChanged | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export default class SimpleMetaTaggers { | ||||
|     /** | ||||
|      * A simple metatagger which rewrites various metatags as needed | ||||
|  | @ -253,7 +316,7 @@ export default class SimpleMetaTaggers { | |||
|     public static geometryType = new InlineMetaTagger( | ||||
|         { | ||||
|             keys: ["_geometry:type"], | ||||
|             doc: "Adds the geometry type as property. This is identical to the GoeJson geometry type and is one of `Point`,`LineString`, `Polygon` and exceptionally `MultiPolygon` or `MultiLineString`", | ||||
|             doc: "Adds the geometry type as property. This is identical to the GoeJson geometry type and is one of `Point`,`LineString`, `Polygon` and exceptionally `MultiPolygon` or `MultiLineString`" | ||||
|         }, | ||||
|         (feature) => { | ||||
|             const changed = feature.properties["_geometry:type"] === feature.geometry.type | ||||
|  | @ -262,6 +325,7 @@ export default class SimpleMetaTaggers { | |||
|         } | ||||
|     ) | ||||
|     public static referencingWays = new ReferencingWaysMetaTagger() | ||||
|     private static normalizePanoramax = new NormalizePanoramax() | ||||
|     private static readonly cardinalDirections = { | ||||
|         N: 0, | ||||
|         NNE: 22.5, | ||||
|  | @ -278,12 +342,12 @@ export default class SimpleMetaTaggers { | |||
|         W: 270, | ||||
|         WNW: 292.5, | ||||
|         NW: 315, | ||||
|         NNW: 337.5, | ||||
|         NNW: 337.5 | ||||
|     } | ||||
|     private static latlon = new InlineMetaTagger( | ||||
|         { | ||||
|             keys: ["_lat", "_lon"], | ||||
|             doc: "The latitude and longitude of the point (or centerpoint in the case of a way/area)", | ||||
|             doc: "The latitude and longitude of the point (or centerpoint in the case of a way/area)" | ||||
|         }, | ||||
|         (feature) => { | ||||
|             const centerPoint = GeoOperations.centerpoint(feature) | ||||
|  | @ -298,7 +362,7 @@ export default class SimpleMetaTaggers { | |||
|         { | ||||
|             doc: "The layer-id to which this feature belongs. Note that this might be return any applicable if `passAllFeatures` is defined.", | ||||
|             keys: ["_layer"], | ||||
|             includesDates: false, | ||||
|             includesDates: false | ||||
|         }, | ||||
|         (feature, layer) => { | ||||
|             if (feature.properties._layer === layer.id) { | ||||
|  | @ -314,11 +378,11 @@ export default class SimpleMetaTaggers { | |||
|                 "sidewalk:left", | ||||
|                 "sidewalk:right", | ||||
|                 "generic_key:left:property", | ||||
|                 "generic_key:right:property", | ||||
|                 "generic_key:right:property" | ||||
|             ], | ||||
|             doc: "Rewrites tags from 'generic_key:both:property' as 'generic_key:left:property' and 'generic_key:right:property' (and similar for sidewalk tagging). Note that this rewritten tags _will be reuploaded on a change_. To prevent to much unrelated retagging, this is only enabled if the layer has at least some lineRenderings with offset defined", | ||||
|             includesDates: false, | ||||
|             cleanupRetagger: true, | ||||
|             cleanupRetagger: true | ||||
|         }, | ||||
|         (feature, layer) => { | ||||
|             if (!layer.lineRendering.some((lr) => lr.leftRightSensitive)) { | ||||
|  | @ -332,11 +396,15 @@ export default class SimpleMetaTaggers { | |||
|         { | ||||
|             keys: ["_surface"], | ||||
|             doc: "The surface area of the feature in square meters. Not set on points and ways", | ||||
|             isLazy: true, | ||||
|             isLazy: true | ||||
|         }, | ||||
|         (feature) => { | ||||
|             if (feature.geometry.type !== "Polygon" && feature.geometry.type !== "MultiPolygon") { | ||||
|                 return | ||||
|             } | ||||
|             const f = <Feature<Polygon | MultiPolygon>>feature | ||||
|             Utils.AddLazyProperty(feature.properties, "_surface", () => { | ||||
|                 return "" + GeoOperations.surfaceAreaInSqMeters(feature) | ||||
|                 return "" + GeoOperations.surfaceAreaInSqMeters(f) | ||||
|             }) | ||||
| 
 | ||||
|             return true | ||||
|  | @ -346,11 +414,15 @@ export default class SimpleMetaTaggers { | |||
|         { | ||||
|             keys: ["_surface:ha"], | ||||
|             doc: "The surface area of the feature in hectare. Not set on points and ways", | ||||
|             isLazy: true, | ||||
|             isLazy: true | ||||
|         }, | ||||
|         (feature) => { | ||||
|             if (feature.geometry.type !== "Polygon" && feature.geometry.type !== "MultiPolygon") { | ||||
|                 return | ||||
|             } | ||||
|             const f = <Feature<Polygon | MultiPolygon>>feature | ||||
|             Utils.AddLazyProperty(feature.properties, "_surface:ha", () => { | ||||
|                 const sqMeters = GeoOperations.surfaceAreaInSqMeters(feature) | ||||
|                 const sqMeters = GeoOperations.surfaceAreaInSqMeters(f) | ||||
|                 return "" + Math.floor(sqMeters / 1000) / 10 | ||||
|             }) | ||||
| 
 | ||||
|  | @ -360,7 +432,7 @@ export default class SimpleMetaTaggers { | |||
|     private static levels = new InlineMetaTagger( | ||||
|         { | ||||
|             doc: "Extract the 'level'-tag into a normalized, ';'-separated value called '_level' (which also includes 'repeat_on'). The `level` tag (without underscore) will be normalized with only the value of `level`.", | ||||
|             keys: ["_level"], | ||||
|             keys: ["_level"] | ||||
|         }, | ||||
|         (feature) => { | ||||
|             let somethingChanged = false | ||||
|  | @ -395,7 +467,7 @@ export default class SimpleMetaTaggers { | |||
|     private static canonicalize = new InlineMetaTagger( | ||||
|         { | ||||
|             doc: "If 'units' is defined in the layoutConfig, then this metatagger will rewrite the specified keys to have the canonical form (e.g. `1meter` will be rewritten to `1m`; `1` will be rewritten to `1m` as well)", | ||||
|             keys: ["Theme-defined keys"], | ||||
|             keys: ["Theme-defined keys"] | ||||
|         }, | ||||
|         (feature, _, __, state) => { | ||||
|             const units = Utils.NoNull( | ||||
|  | @ -452,7 +524,7 @@ export default class SimpleMetaTaggers { | |||
|     private static lngth = new InlineMetaTagger( | ||||
|         { | ||||
|             keys: ["_length", "_length:km"], | ||||
|             doc: "The total length of a feature in meters (and in kilometers, rounded to one decimal for '_length:km'). For a surface, the length of the perimeter", | ||||
|             doc: "The total length of a feature in meters (and in kilometers, rounded to one decimal for '_length:km'). For a surface, the length of the perimeter" | ||||
|         }, | ||||
|         (feature) => { | ||||
|             const l = GeoOperations.lengthInMeters(feature) | ||||
|  | @ -468,7 +540,7 @@ export default class SimpleMetaTaggers { | |||
|             keys: ["_isOpen"], | ||||
|             doc: "If 'opening_hours' is present, it will add the current state of the feature (being 'yes' or 'no')", | ||||
|             includesDates: true, | ||||
|             isLazy: true, | ||||
|             isLazy: true | ||||
|         }, | ||||
|         (feature) => { | ||||
|             if (Utils.runningFromConsole) { | ||||
|  | @ -507,8 +579,8 @@ export default class SimpleMetaTaggers { | |||
|                                 lon: lon, | ||||
|                                 address: { | ||||
|                                     country_code: tags._country.toLowerCase(), | ||||
|                                     state: undefined, | ||||
|                                 }, | ||||
|                                     state: undefined | ||||
|                                 } | ||||
|                             }, | ||||
|                             <any>{ tag_key: "opening_hours" } | ||||
|                         ) | ||||
|  | @ -520,14 +592,14 @@ export default class SimpleMetaTaggers { | |||
|                         delete tags._isOpen | ||||
|                         tags["_isOpen"] = "parse_error" | ||||
|                     } | ||||
|                 }, | ||||
|                 } | ||||
|             }) | ||||
|         } | ||||
|     ) | ||||
|     private static directionSimplified = new InlineMetaTagger( | ||||
|         { | ||||
|             keys: ["_direction:numerical", "_direction:leftright"], | ||||
|             doc: "_direction:numerical is a normalized, numerical direction based on 'camera:direction' or on 'direction'; it is only present if a valid direction is found (e.g. 38.5 or NE). _direction:leftright is either 'left' or 'right', which is left-looking on the map or 'right-looking' on the map", | ||||
|             doc: "_direction:numerical is a normalized, numerical direction based on 'camera:direction' or on 'direction'; it is only present if a valid direction is found (e.g. 38.5 or NE). _direction:leftright is either 'left' or 'right', which is left-looking on the map or 'right-looking' on the map" | ||||
|         }, | ||||
|         (feature) => { | ||||
|             const tags = feature.properties | ||||
|  | @ -552,7 +624,7 @@ export default class SimpleMetaTaggers { | |||
|         { | ||||
|             keys: ["_direction:centerpoint"], | ||||
|             isLazy: true, | ||||
|             doc: "_direction:centerpoint is the direction of the linestring (in degrees) if one were standing at the projected centerpoint.", | ||||
|             doc: "_direction:centerpoint is the direction of the linestring (in degrees) if one were standing at the projected centerpoint." | ||||
|         }, | ||||
|         (feature: Feature) => { | ||||
|             if (feature.geometry.type !== "LineString") { | ||||
|  | @ -575,7 +647,7 @@ export default class SimpleMetaTaggers { | |||
|                     delete feature.properties["_direction:centerpoint"] | ||||
|                     feature.properties["_direction:centerpoint"] = bearing | ||||
|                     return bearing | ||||
|                 }, | ||||
|                 } | ||||
|             }) | ||||
| 
 | ||||
|             return true | ||||
|  | @ -585,7 +657,7 @@ export default class SimpleMetaTaggers { | |||
|         { | ||||
|             keys: ["_now:date", "_now:datetime"], | ||||
|             doc: "Adds the time that the data got loaded - pretty much the time of downloading from overpass. The format is YYYY-MM-DD hh:mm, aka 'sortable' aka ISO-8601-but-not-entirely", | ||||
|             includesDates: true, | ||||
|             includesDates: true | ||||
|         }, | ||||
|         (feature) => { | ||||
|             const now = new Date() | ||||
|  | @ -609,7 +681,7 @@ export default class SimpleMetaTaggers { | |||
|             keys: ["_last_edit:passed_time"], | ||||
|             doc: "Gives the number of seconds since the last edit. Note that this will _not_ update, but rather be the number of seconds elapsed at the moment this tag is read first", | ||||
|             isLazy: true, | ||||
|             includesDates: true, | ||||
|             includesDates: true | ||||
|         }, | ||||
|         (feature) => { | ||||
|             Utils.AddLazyProperty(feature.properties, "_last_edit:passed_time", () => { | ||||
|  | @ -628,7 +700,7 @@ export default class SimpleMetaTaggers { | |||
|         { | ||||
|             keys: ["_currency"], | ||||
|             doc: "Adds the currency valid for the object, based on country or explicit tagging. Can be a single currency or a semicolon-separated list of currencies. Empty if no currency is found.", | ||||
|             isLazy: true, | ||||
|             isLazy: true | ||||
|         }, | ||||
|         (feature: Feature, layer: LayerConfig, tagsStore: UIEventSource<OsmTags>) => { | ||||
|             if (tagsStore === undefined) { | ||||
|  | @ -670,6 +742,7 @@ export default class SimpleMetaTaggers { | |||
|         } | ||||
|     ) | ||||
| 
 | ||||
| 
 | ||||
|     public static metatags: SimpleMetaTagger[] = [ | ||||
|         SimpleMetaTaggers.latlon, | ||||
|         SimpleMetaTaggers.layerInfo, | ||||
|  | @ -689,6 +762,7 @@ export default class SimpleMetaTaggers { | |||
|         SimpleMetaTaggers.referencingWays, | ||||
|         SimpleMetaTaggers.timeSinceLastEdit, | ||||
|         SimpleMetaTaggers.currency, | ||||
|         SimpleMetaTaggers.normalizePanoramax | ||||
|     ] | ||||
| 
 | ||||
|     /** | ||||
|  | @ -770,8 +844,8 @@ export default class SimpleMetaTaggers { | |||
|             [ | ||||
|                 "Metatags are extra tags available, in order to display more data or to give better questions.", | ||||
|                 "They are calculated automatically on every feature when the data arrives in the webbrowser. This document gives an overview of the available metatags.", | ||||
|                 "**Hint:** when using metatags, add the [query parameter](URL_Parameters.md) `debug=true` to the URL. This will include a box in the popup for features which shows all the properties of the object", | ||||
|             ].join("\n"), | ||||
|                 "**Hint:** when using metatags, add the [query parameter](URL_Parameters.md) `debug=true` to the URL. This will include a box in the popup for features which shows all the properties of the object" | ||||
|             ].join("\n") | ||||
|         ] | ||||
| 
 | ||||
|         subElements.push("## Metatags calculated by MapComplete") | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue