forked from MapComplete/MapComplete
		
	Fix metatagging and calculated tags in heterogenous data settings
This commit is contained in:
		
							parent
							
								
									93296d5378
								
							
						
					
					
						commit
						d547b9f968
					
				
					 14 changed files with 374 additions and 246 deletions
				
			
		|  | @ -81,6 +81,9 @@ export default class LayerConfig { | |||
|                 osmTags = FromJSON.Tag(json.source["osmTags"], context + "source.osmTags"); | ||||
|             } | ||||
| 
 | ||||
|             if(json.source["geoJsonSource"] !== undefined){ | ||||
|                 throw context + "Use 'geoJson' instead of 'geoJsonSource'" | ||||
|             } | ||||
|              | ||||
|             this.source = new SourceConfig({ | ||||
|                 osmTags: osmTags, | ||||
|  | @ -172,7 +175,10 @@ export default class LayerConfig { | |||
|                         if (shared !== undefined) { | ||||
|                             return shared; | ||||
|                         } | ||||
|                         throw `Predefined tagRendering ${renderingJson} not found in ${context}`; | ||||
|                          | ||||
|                         const keys = Array.from(SharedTagRenderings.SharedTagRendering.keys()) | ||||
|                          | ||||
|                         throw `Predefined tagRendering ${renderingJson} not found in ${context}.\n      Try one of ${(keys.join(", "))}`; | ||||
|                     } | ||||
|                     return new TagRenderingConfig(renderingJson, self.source.osmTags, `${context}.tagrendering[${i}]`); | ||||
|                 }); | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ import {UIEventSource} from "./UIEventSource"; | |||
| export class ElementStorage { | ||||
| 
 | ||||
|     private _elements = new Map<string, UIEventSource<any>>(); | ||||
|     public ContainingFeatures = new Map<string, any>(); | ||||
| 
 | ||||
|     constructor() { | ||||
| 
 | ||||
|  | @ -29,6 +30,11 @@ export class ElementStorage { | |||
| 
 | ||||
|         // At last, we overwrite the tag of the new feature to use the tags in the already existing event source
 | ||||
|         feature.properties = es.data | ||||
|          | ||||
|         if(!this.ContainingFeatures.has(elementId)){ | ||||
|             this.ContainingFeatures.set(elementId, feature); | ||||
|         } | ||||
|          | ||||
|         return es; | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,6 +2,8 @@ import {GeoOperations} from "./GeoOperations"; | |||
| import {UIElement} from "../UI/UIElement"; | ||||
| import Combine from "../UI/Base/Combine"; | ||||
| import {Relation} from "./Osm/ExtractRelations"; | ||||
| import State from "../State"; | ||||
| import {Utils} from "../Utils"; | ||||
| 
 | ||||
| export class ExtraFunction { | ||||
| 
 | ||||
|  | @ -59,17 +61,25 @@ Some advanced functions are available on <b>feat</b> as well: | |||
|     ) | ||||
|     private static readonly DistanceToFunc = new ExtraFunction( | ||||
|         "distanceTo", | ||||
|         "Calculates the distance between the feature and a specified point", | ||||
|         "Calculates the distance between the feature and a specified point in kilometer. The input should either be a pair of coordinates, a geojson feature or the ID of an object", | ||||
|         ["longitude", "latitude"], | ||||
|         (featuresPerLayer, feature) => { | ||||
|             return (arg0, lat) => { | ||||
|                 if (typeof arg0 === "number") { | ||||
|                     // Feature._lon and ._lat is conveniently place by one of the other metatags
 | ||||
|                     return GeoOperations.distanceBetween([arg0, lat], [feature._lon, feature._lat]); | ||||
|                 } else { | ||||
|                     // arg0 is probably a feature
 | ||||
|                     return GeoOperations.distanceBetween(GeoOperations.centerpointCoordinates(arg0), [feature._lon, feature._lat]) | ||||
|                 } | ||||
|                 if (typeof arg0 === "string") { | ||||
|                     // This is an identifier
 | ||||
|                     const feature = State.state.allElements.ContainingFeatures.get(arg0); | ||||
|                     if(feature === undefined){ | ||||
|                         return undefined; | ||||
|                     } | ||||
|                     arg0 = feature; | ||||
|                 } | ||||
| 
 | ||||
|                 // arg0 is probably a feature
 | ||||
|                 return GeoOperations.distanceBetween(GeoOperations.centerpointCoordinates(arg0), [feature._lon, feature._lat]) | ||||
| 
 | ||||
|             } | ||||
|         } | ||||
|  | @ -82,8 +92,21 @@ Some advanced functions are available on <b>feat</b> as well: | |||
|         (params, feature) => { | ||||
|             return (features) => { | ||||
|                 if (typeof features === "string") { | ||||
|                     const name = features | ||||
|                     features = params.featuresPerLayer.get(features) | ||||
|                     if (features === undefined) { | ||||
|                         var keys = Utils.NoNull(Array.from(params.featuresPerLayer.keys())); | ||||
|                         if (keys.length > 0) { | ||||
|                             throw `No features defined for ${name}. Defined layers are ${keys.join(", ")}`; | ||||
|                         } else { | ||||
|                             // This is the first pass over an external dataset
 | ||||
|                             // Other data probably still has to load!
 | ||||
|                             return undefined; | ||||
|                         } | ||||
| 
 | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 let closestFeature = undefined; | ||||
|                 let closestDistance = undefined; | ||||
|                 for (const otherFeature of features) { | ||||
|  | @ -120,7 +143,7 @@ Some advanced functions are available on <b>feat</b> as well: | |||
|         "For example: <code>_part_of_walking_routes=feat.memberships().map(r => r.relation.tags.name).join(';')</code>", | ||||
|         [], | ||||
|         (params, _) => { | ||||
|             return () =>   params.relations ?? []; | ||||
|             return () => params.relations ?? []; | ||||
|         } | ||||
|     ) | ||||
| 
 | ||||
|  | @ -128,9 +151,9 @@ Some advanced functions are available on <b>feat</b> as well: | |||
|     private readonly _name: string; | ||||
|     private readonly _args: string[]; | ||||
|     private readonly _doc: string; | ||||
|     private readonly _f: (params: {featuresPerLayer: Map<string, any[]>, relations: {role: string, relation: Relation}[]}, feat: any) => any; | ||||
|     private readonly _f: (params: { featuresPerLayer: Map<string, any[]>, relations: { role: string, relation: Relation }[] }, feat: any) => any; | ||||
| 
 | ||||
|     constructor(name: string, doc: string, args: string[], f: ((params: {featuresPerLayer: Map<string, any[]>, relations: {role: string, relation: Relation}[]}, feat: any) => any)) { | ||||
|     constructor(name: string, doc: string, args: string[], f: ((params: { featuresPerLayer: Map<string, any[]>, relations: { role: string, relation: Relation }[] }, feat: any) => any)) { | ||||
|         this._name = name; | ||||
|         this._doc = doc; | ||||
|         this._args = args; | ||||
|  | @ -138,7 +161,7 @@ Some advanced functions are available on <b>feat</b> as well: | |||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public static FullPatchFeature(featuresPerLayer: Map<string, any[]>,relations: {role: string, relation: Relation}[], feature) { | ||||
|     public static FullPatchFeature(featuresPerLayer: Map<string, any[]>, relations: { role: string, relation: Relation }[], feature) { | ||||
|         for (const func of ExtraFunction.allFuncs) { | ||||
|             func.PatchFeature(featuresPerLayer, relations, feature); | ||||
|         } | ||||
|  | @ -166,7 +189,7 @@ Some advanced functions are available on <b>feat</b> as well: | |||
|         ]); | ||||
|     } | ||||
| 
 | ||||
|     public PatchFeature(featuresPerLayer: Map<string, any[]>, relations: {role: string, relation: Relation}[], feature: any) { | ||||
|     public PatchFeature(featuresPerLayer: Map<string, any[]>, relations: { role: string, relation: Relation }[], feature: any) { | ||||
| 
 | ||||
|         feature[this._name] = this._f({featuresPerLayer: featuresPerLayer, relations: relations}, feature); | ||||
|     } | ||||
|  |  | |||
							
								
								
									
										12
									
								
								Logic/FeatureSource/DummyFeatureSource.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								Logic/FeatureSource/DummyFeatureSource.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | |||
| import FeatureSource from "./FeatureSource"; | ||||
| import {UIEventSource} from "../UIEventSource"; | ||||
| 
 | ||||
| export default class DummyFeatureSource implements FeatureSource{ | ||||
|   public readonly  features: UIEventSource<{ feature: any; freshness: Date }[]>; | ||||
|   public readonly  name: string = "Dummy (static) feature source"; | ||||
|      | ||||
|   constructor(features: UIEventSource<{ feature: any; freshness: Date }[]>) { | ||||
|       this.features = features; | ||||
|   } | ||||
|    | ||||
| } | ||||
|  | @ -35,7 +35,7 @@ export default class FeaturePipeline implements FeatureSource { | |||
|         const amendedOverpassSource = | ||||
|             new RememberingSource( | ||||
|                 new LocalStorageSaver( | ||||
|                     new MetaTaggingFeatureSource( | ||||
|                     new MetaTaggingFeatureSource(this, | ||||
|                         new FeatureDuplicatorPerLayer(flayers, | ||||
|                             new RegisteringFeatureSource( | ||||
|                                 updater) | ||||
|  | @ -43,18 +43,24 @@ export default class FeaturePipeline implements FeatureSource { | |||
| 
 | ||||
|         const geojsonSources: FeatureSource [] = GeoJsonSource | ||||
|             .ConstructMultiSource(flayers.data, locationControl) | ||||
|             .map(geojsonSource => new RegisteringFeatureSource(new FeatureDuplicatorPerLayer(flayers, geojsonSource))); | ||||
|             .map(geojsonSource => { | ||||
|                 let source = new RegisteringFeatureSource(new FeatureDuplicatorPerLayer(flayers, geojsonSource)); | ||||
|                 if(!geojsonSource.isOsmCache){ | ||||
|                     source = new MetaTaggingFeatureSource(this, source, updater.features); | ||||
|                 } | ||||
|                 return source | ||||
|             }); | ||||
| 
 | ||||
|         const amendedLocalStorageSource = | ||||
|             new RememberingSource(new RegisteringFeatureSource(new FeatureDuplicatorPerLayer(flayers, new LocalStorageSource(layout)) | ||||
|             )); | ||||
| 
 | ||||
|         newPoints = new MetaTaggingFeatureSource( | ||||
|         newPoints = new MetaTaggingFeatureSource(this, | ||||
|             new FeatureDuplicatorPerLayer(flayers, | ||||
|                 new RegisteringFeatureSource(newPoints))); | ||||
| 
 | ||||
|         const amendedOsmApiSource = new RememberingSource( | ||||
|             new MetaTaggingFeatureSource( | ||||
|             new MetaTaggingFeatureSource(this, | ||||
|                 new FeatureDuplicatorPerLayer(flayers, | ||||
| 
 | ||||
|                     new RegisteringFeatureSource(fromOsmApi)))); | ||||
|  |  | |||
|  | @ -19,6 +19,7 @@ export default class GeoJsonSource implements FeatureSource { | |||
|     private onFail: ((errorMsg: any, url: string) => void) = undefined; | ||||
|     private readonly layerId: string; | ||||
|     private readonly seenids: Set<string> = new Set<string>() | ||||
|     public readonly isOsmCache: boolean | ||||
| 
 | ||||
|     private constructor(locationControl: UIEventSource<Loc>, | ||||
|                         flayer: { isDisplayed: UIEventSource<boolean>, layerDef: LayerConfig }, | ||||
|  | @ -28,6 +29,8 @@ export default class GeoJsonSource implements FeatureSource { | |||
|         this.name = "GeoJsonSource of " + url; | ||||
|         const zoomLevel = flayer.layerDef.source.geojsonZoomLevel; | ||||
|          | ||||
|         this.isOsmCache = flayer.layerDef.source.isOsmCacheLayer; | ||||
| 
 | ||||
|         this.features = new UIEventSource<{ feature: any; freshness: Date }[]>([]) | ||||
| 
 | ||||
|         if (zoomLevel === undefined) { | ||||
|  |  | |||
|  | @ -6,28 +6,38 @@ import MetaTagging from "../MetaTagging"; | |||
| import ExtractRelations from "../Osm/ExtractRelations"; | ||||
| 
 | ||||
| export default class MetaTaggingFeatureSource implements FeatureSource { | ||||
|     public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{feature: any; freshness: Date}[]>(undefined); | ||||
|     public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>(undefined); | ||||
| 
 | ||||
|     public readonly name; | ||||
| 
 | ||||
|     constructor(source: FeatureSource) { | ||||
|     constructor(allFeaturesSource: FeatureSource, source: FeatureSource, updateTrigger?: UIEventSource<any>) { | ||||
|         const self = this; | ||||
|         this.name = "MetaTagging of "+source.name | ||||
|         source.features.addCallbackAndRun((featuresFreshness: { feature: any, freshness: Date }[]) => { | ||||
|                 if (featuresFreshness === undefined) { | ||||
|                     return; | ||||
|         this.name = "MetaTagging of " + source.name | ||||
| 
 | ||||
|         function update() { | ||||
|             const featuresFreshness = source.features.data | ||||
|             if (featuresFreshness === undefined) { | ||||
|                 return; | ||||
|             } | ||||
|             featuresFreshness.forEach(featureFresh => { | ||||
|                 const feature = featureFresh.feature; | ||||
| 
 | ||||
|                 if (Hash.hash.data === feature.properties.id) { | ||||
|                     State.state.selectedElement.setData(feature); | ||||
|                 } | ||||
|                 featuresFreshness.forEach(featureFresh => { | ||||
|                     const feature = featureFresh.feature; | ||||
|             }) | ||||
| 
 | ||||
|                     if (Hash.hash.data === feature.properties.id) { | ||||
|                         State.state.selectedElement.setData(feature); | ||||
|                     } | ||||
|                 }) | ||||
|             MetaTagging.addMetatags(featuresFreshness, | ||||
|                 allFeaturesSource, | ||||
|                 State.state.knownRelations.data, State.state.layoutToUse.data.layers); | ||||
|             self.features.setData(featuresFreshness); | ||||
|         } | ||||
| 
 | ||||
|                 MetaTagging.addMetatags(featuresFreshness, State.state.knownRelations.data, State.state.layoutToUse.data.layers); | ||||
|                 self.features.setData(featuresFreshness); | ||||
|         }); | ||||
|         source.features.addCallbackAndRun(_ => update()); | ||||
|         updateTrigger?.addCallback(_ => { | ||||
|             console.debug("Updating because of external call") | ||||
|             update(); | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -2,6 +2,7 @@ import LayerConfig from "../Customizations/JSON/LayerConfig"; | |||
| import SimpleMetaTagger from "./SimpleMetaTagger"; | ||||
| import {ExtraFunction} from "./ExtraFunction"; | ||||
| import {Relation} from "./Osm/ExtractRelations"; | ||||
| import FeatureSource from "./FeatureSource/FeatureSource"; | ||||
| 
 | ||||
| 
 | ||||
| interface Params { | ||||
|  | @ -22,6 +23,7 @@ export default class MetaTagging { | |||
|      * The features are a list of geojson-features, with a "properties"-field and geometry | ||||
|      */ | ||||
|     static addMetatags(features: { feature: any; freshness: Date }[], | ||||
|                        allKnownFeatures: FeatureSource, | ||||
|                        relations: Map<string, { role: string, relation: Relation }[]>, | ||||
|                        layers: LayerConfig[], | ||||
|                        includeDates = true) { | ||||
|  | @ -55,7 +57,7 @@ export default class MetaTagging { | |||
|             featuresPerLayer.get(key).push(feature.feature) | ||||
|         } | ||||
| 
 | ||||
|         for (const feature of features) { | ||||
|         for (const feature of (allKnownFeatures.features?.data ?? features ?? [])) { | ||||
|             // @ts-ignore
 | ||||
|             const key = feature.feature._matching_layer_id; | ||||
|             const f = layerFuncs.get(key); | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ import { Utils } from "../Utils"; | |||
| 
 | ||||
| export default class Constants { | ||||
|      | ||||
|     public static vNumber = "0.7.2c"; | ||||
|     public static vNumber = "0.7.2d"; | ||||
| 
 | ||||
|     // The user journey states thresholds when a new feature gets unlocked
 | ||||
|     public static userJourney = { | ||||
|  |  | |||
							
								
								
									
										202
									
								
								assets/layers/defibrillator/defibrillator.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								assets/layers/defibrillator/defibrillator.json
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,202 @@ | |||
| { | ||||
|   "id": "defibrillator", | ||||
|   "name": { | ||||
|     "en": "Defibrillators", | ||||
|     "ca": "Desfibril·ladors", | ||||
|     "es": "Desfibriladores", | ||||
|     "fr": "Défibrillateurs", | ||||
|     "nl": "Defibrillatoren", | ||||
|     "de": "Defibrillatoren" | ||||
|   }, | ||||
|   "source": { | ||||
|     "osmTags": "emergency=defibrillator" | ||||
|   }, | ||||
|   "minzoom": 12, | ||||
|   "title": { | ||||
|     "render": { | ||||
|       "en": "Defibrillator", | ||||
|       "ca": "Desfibril·lador", | ||||
|       "es": "Desfibrilador", | ||||
|       "fr": "Défibrillateur", | ||||
|       "nl": "Defibrillator", | ||||
|       "de": "Defibrillator" | ||||
|     } | ||||
|   }, | ||||
|   "icon": "./assets/themes/aed/aed.svg", | ||||
|   "color": "#0000ff", | ||||
|   "presets": [ | ||||
|     { | ||||
|       "title": { | ||||
|         "en": "Defibrillator", | ||||
|         "ca": "Desfibril·lador", | ||||
|         "es": "Desfibrilador", | ||||
|         "fr": "Défibrillateur", | ||||
|         "nl": "Defibrillator", | ||||
|         "de": "Defibrillator" | ||||
|       }, | ||||
|       "tags": [ | ||||
|         "emergency=defibrillator" | ||||
|       ] | ||||
|     } | ||||
|   ], | ||||
|   "tagRenderings": [ | ||||
|     "images", | ||||
|     { | ||||
|       "question": { | ||||
|         "en": "Is this defibrillator located indoors?", | ||||
|         "ca": "Està el desfibril·lador a l'interior?", | ||||
|         "es": "¿Esté el desfibrilador en interior?", | ||||
|         "fr": "Ce défibrillateur est-il disposé en intérieur ?", | ||||
|         "nl": "Hangt deze defibrillator binnen of buiten?", | ||||
|         "de": "Befindet sich dieser Defibrillator im Gebäude?" | ||||
|       }, | ||||
|       "mappings": [ | ||||
|         { | ||||
|           "if": "indoor=yes", | ||||
|           "then": { | ||||
|             "en": "This defibrillator is located indoors", | ||||
|             "ca": "Aquest desfibril·lador està a l'interior", | ||||
|             "es": "Este desfibrilador está en interior", | ||||
|             "fr": "Ce défibrillateur est en intérieur (dans un batiment)", | ||||
|             "nl": "Deze defibrillator bevindt zich in een gebouw", | ||||
|             "de": "Dieser Defibrillator befindet sich im Gebäude" | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           "if": "indoor=no", | ||||
|           "then": { | ||||
|             "en": "This defibrillator is located outdoors", | ||||
|             "ca": "Aquest desfibril·lador està a l'exterior", | ||||
|             "es": "Este desfibrilador está en exterior", | ||||
|             "fr": "Ce défibrillateur est situé en extérieur", | ||||
|             "nl": "Deze defibrillator hangt buiten", | ||||
|             "de": "Dieser Defibrillator befindet sich im Freien" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "question": { | ||||
|         "en": "Is this defibrillator freely accessible?", | ||||
|         "ca": "Està el desfibril·lador accessible lliurement?", | ||||
|         "es": "¿Está el desfibrilador accesible libremente?", | ||||
|         "fr": "Ce défibrillateur est-il librement accessible ?", | ||||
|         "nl": "Is deze defibrillator vrij toegankelijk?", | ||||
|         "de": "Ist dieser Defibrillator frei zugänglich?" | ||||
|       }, | ||||
|       "render": { | ||||
|         "en": "Access is {access}", | ||||
|         "ca": "L'accés és {access}", | ||||
|         "es": "El acceso es {access}", | ||||
|         "fr": "{access} accessible", | ||||
|         "nl": "Toegankelijkheid is {access}", | ||||
|         "de": "Zugang ist {access}" | ||||
|       }, | ||||
|       "condition": { | ||||
|         "or": [ | ||||
|           "indoor=yes", | ||||
|           "access~*" | ||||
|         ] | ||||
|       }, | ||||
|       "freeform": { | ||||
|         "key": "access", | ||||
|         "addExtraTags": [ | ||||
|           "fixme=Freeform field used for access - doublecheck the value" | ||||
|         ] | ||||
|       }, | ||||
|       "mappings": [ | ||||
|         { | ||||
|           "if": "access=yes", | ||||
|           "then": { | ||||
|             "en": "Publicly accessible", | ||||
|             "ca": "Accés lliure", | ||||
|             "es": "Acceso libre", | ||||
|             "fr": "Librement accessible", | ||||
|             "nl": "Publiek toegankelijk", | ||||
|             "de": "Öffentlich zugänglich" | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           "if": "access=public", | ||||
|           "then": { | ||||
|             "en": "Publicly accessible", | ||||
|             "ca": "Publicament accessible", | ||||
|             "es": "Publicament accesible", | ||||
|             "fr": "Librement accessible", | ||||
|             "nl": "Publiek toegankelijk", | ||||
|             "de": "Öffentlich zugänglich" | ||||
|           }, | ||||
|           "hideInAnswer": true | ||||
|         }, | ||||
|         { | ||||
|           "if": "access=customers", | ||||
|           "then": { | ||||
|             "en": "Only accessible to customers", | ||||
|             "ca": "Només accessible a clients", | ||||
|             "es": "Sólo accesible a clientes", | ||||
|             "fr": "Réservé aux clients du lieu", | ||||
|             "nl": "Enkel toegankelijk voor klanten", | ||||
|             "de": "Nur für Kunden zugänglich" | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           "if": "access=private", | ||||
|           "then": { | ||||
|             "en": "Not accessible to the general public (e.g. only accesible to staff, the owners, ...)", | ||||
|             "ca": "No accessible al públic en general (ex. només accesible a treballadors, propietaris, ...)", | ||||
|             "es": "No accesible al público en general (ex. sólo accesible a trabajadores, propietarios, ...)", | ||||
|             "fr": "Non accessible au public (par exemple réservé au personnel, au propriétaire, ...)", | ||||
|             "nl": "Niet toegankelijk voor het publiek (bv. enkel voor personneel, de eigenaar, ...)", | ||||
|             "de": "Nicht für die Öffentlichkeit zugänglich (z.B. nur für das Personal, die Eigentümer, ...)" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "question": { | ||||
|         "en": "On which floor is this defibrillator located?", | ||||
|         "ca": "A quina planta està el desfibril·lador localitzat?", | ||||
|         "es": "¿En qué planta se encuentra el defibrilador localizado?", | ||||
|         "fr": "À quel étage est situé ce défibrillateur ?", | ||||
|         "nl": "Op welke verdieping bevindt deze defibrillator zich?", | ||||
|         "de": "In welchem Stockwerk befindet sich dieser Defibrillator?" | ||||
|       }, | ||||
|       "condition": { | ||||
|         "and": [ | ||||
|           "indoor=yes", | ||||
|           "access!~private" | ||||
|         ] | ||||
|       }, | ||||
|       "freeform": { | ||||
|         "key": "level", | ||||
|         "type": "int" | ||||
|       }, | ||||
|       "render": { | ||||
|         "en": "This defibrallator is on floor {level}", | ||||
|         "ca": "Aquest desfibril·lador és a la planta {level}", | ||||
|         "es": "El desfibrilador se encuentra en la planta {level}", | ||||
|         "fr": "Ce défibrillateur est à l'étage {level}", | ||||
|         "nl": "De defibrillator bevindt zicht op verdieping {level}", | ||||
|         "de": "Dieser Defibrallator befindet sich im {level}. Stockwerk" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "render": { | ||||
|         "nl": "<i>Meer informatie over de locatie:</i><br/>{defibrillator:location}", | ||||
|         "en": "<i>Extra information about the location:</i><br/>{defibrillator:location}" | ||||
|       }, | ||||
|       "question": { | ||||
|         "en": "Please give some explanation on where the defibrillator can be found", | ||||
|         "ca": "Dóna detalls d'on es pot trobar el desfibril·lador", | ||||
|         "es": "Da detalles de dónde se puede encontrar el desfibrilador", | ||||
|         "fr": "Veuillez indiquez plus précisément où se situe le défibrillateur", | ||||
|         "nl": "Gelieve meer informatie te geven over de exacte locatie van de defibrillator", | ||||
|         "de": "Bitte geben Sie einige Erläuterungen dazu, wo der Defibrillator zu finden ist" | ||||
|       }, | ||||
|       "freeform": { | ||||
|         "type": "text", | ||||
|         "key": "defibrillator:location" | ||||
|       } | ||||
|     } | ||||
|   ] | ||||
| } | ||||
|  | @ -82,5 +82,9 @@ | |||
|     "#": "Gives some metainfo about the last edit and who did edit it - rendering only", | ||||
|     "condition": "_last_edit:contributor~*", | ||||
|     "render": "<div class='subtle' style='font-size: small; margin-top: 2em; margin-bottom: 0.5em;'><a href='https://www.openStreetMap.org/changeset/{_last_edit:changeset}' target='_blank'>Last edited on {_last_edit:timestamp}</a> by <a href='https://www.openStreetMap.org/user/{_last_edit:contributor}' target='_blank'>{_last_edit:contributor}</a></div>" | ||||
|   }, | ||||
|   "all_tags": { | ||||
|     "#": "Prints all the tags", | ||||
|     "render": "{all_tags()}" | ||||
|   } | ||||
| } | ||||
|  | @ -31,207 +31,6 @@ | |||
|   "startLon": 0, | ||||
|   "startZoom": 12, | ||||
|   "layers": [ | ||||
|     { | ||||
|       "id": "Defibrillator", | ||||
|       "name": { | ||||
|         "en": "Defibrillators", | ||||
|         "ca": "Desfibril·ladors", | ||||
|         "es": "Desfibriladores", | ||||
|         "fr": "Défibrillateurs", | ||||
|         "nl": "Defibrillatoren", | ||||
|         "de": "Defibrillatoren" | ||||
|       }, | ||||
|       "source": { | ||||
|         "osmTags": "emergency=defibrillator" | ||||
|       }, | ||||
|       "minzoom": 12, | ||||
|       "title": { | ||||
|         "render": { | ||||
|           "en": "Defibrillator", | ||||
|           "ca": "Desfibril·lador", | ||||
|           "es": "Desfibrilador", | ||||
|           "fr": "Défibrillateur", | ||||
|           "nl": "Defibrillator", | ||||
|           "de": "Defibrillator" | ||||
|         } | ||||
|       }, | ||||
|       "icon": "./assets/themes/aed/aed.svg", | ||||
|       "color": "#0000ff", | ||||
|       "presets": [ | ||||
|         { | ||||
|           "title": { | ||||
|             "en": "Defibrillator", | ||||
|             "ca": "Desfibril·lador", | ||||
|             "es": "Desfibrilador", | ||||
|             "fr": "Défibrillateur", | ||||
|             "nl": "Defibrillator", | ||||
|             "de": "Defibrillator" | ||||
|           }, | ||||
|           "tags": [ | ||||
|             "emergency=defibrillator" | ||||
|           ] | ||||
|         } | ||||
|       ], | ||||
|       "tagRenderings": [ | ||||
|         "images", | ||||
|         { | ||||
|           "question": { | ||||
|             "en": "Is this defibrillator located indoors?", | ||||
|             "ca": "Està el desfibril·lador a l'interior?", | ||||
|             "es": "¿Esté el desfibrilador en interior?", | ||||
|             "fr": "Ce défibrillateur est-il disposé en intérieur ?", | ||||
|             "nl": "Hangt deze defibrillator binnen of buiten?", | ||||
|             "de": "Befindet sich dieser Defibrillator im Gebäude?" | ||||
|           }, | ||||
|           "mappings": [ | ||||
|             { | ||||
|               "if": "indoor=yes", | ||||
|               "then": { | ||||
|                 "en": "This defibrillator is located indoors", | ||||
|                 "ca": "Aquest desfibril·lador està a l'interior", | ||||
|                 "es": "Este desfibrilador está en interior", | ||||
|                 "fr": "Ce défibrillateur est en intérieur (dans un batiment)", | ||||
|                 "nl": "Deze defibrillator bevindt zich in een gebouw", | ||||
|                 "de": "Dieser Defibrillator befindet sich im Gebäude" | ||||
|               } | ||||
|             }, | ||||
|             { | ||||
|               "if": "indoor=no", | ||||
|               "then": { | ||||
|                 "en": "This defibrillator is located outdoors", | ||||
|                 "ca": "Aquest desfibril·lador està a l'exterior", | ||||
|                 "es": "Este desfibrilador está en exterior", | ||||
|                 "fr": "Ce défibrillateur est situé en extérieur", | ||||
|                 "nl": "Deze defibrillator hangt buiten", | ||||
|                 "de": "Dieser Defibrillator befindet sich im Freien" | ||||
|               } | ||||
|             } | ||||
|           ] | ||||
|         }, | ||||
|         { | ||||
|           "question": { | ||||
|             "en": "Is this defibrillator freely accessible?", | ||||
|             "ca": "Està el desfibril·lador accessible lliurement?", | ||||
|             "es": "¿Está el desfibrilador accesible libremente?", | ||||
|             "fr": "Ce défibrillateur est-il librement accessible ?", | ||||
|             "nl": "Is deze defibrillator vrij toegankelijk?", | ||||
|             "de": "Ist dieser Defibrillator frei zugänglich?" | ||||
|           }, | ||||
|           "render": { | ||||
|             "en": "Access is {access}", | ||||
|             "ca": "L'accés és {access}", | ||||
|             "es": "El acceso es {access}", | ||||
|             "fr": "{access} accessible", | ||||
|             "nl": "Toegankelijkheid is {access}", | ||||
|             "de": "Zugang ist {access}" | ||||
|           }, | ||||
|           "condition": { | ||||
|             "or": [ | ||||
|               "indoor=yes", | ||||
|               "access~*" | ||||
|             ] | ||||
|           }, | ||||
|           "freeform": { | ||||
|             "key": "access", | ||||
|             "addExtraTags": [ | ||||
|               "fixme=Freeform field used for access - doublecheck the value" | ||||
|             ] | ||||
|           }, | ||||
|           "mappings": [ | ||||
|             { | ||||
|               "if": "access=yes", | ||||
|               "then": { | ||||
|                 "en": "Publicly accessible", | ||||
|                 "ca": "Accés lliure", | ||||
|                 "es": "Acceso libre", | ||||
|                 "fr": "Librement accessible", | ||||
|                 "nl": "Publiek toegankelijk", | ||||
|                 "de": "Öffentlich zugänglich" | ||||
|               } | ||||
|             }, | ||||
|             { | ||||
|               "if": "access=public", | ||||
|               "then": { | ||||
|                 "en": "Publicly accessible", | ||||
|                 "ca": "Publicament accessible", | ||||
|                 "es": "Publicament accesible", | ||||
|                 "fr": "Librement accessible", | ||||
|                 "nl": "Publiek toegankelijk", | ||||
|                 "de": "Öffentlich zugänglich" | ||||
|               }, | ||||
|               "hideInAnswer": true | ||||
|             }, | ||||
|             { | ||||
|               "if": "access=customers", | ||||
|               "then": { | ||||
|                 "en": "Only accessible to customers", | ||||
|                 "ca": "Només accessible a clients", | ||||
|                 "es": "Sólo accesible a clientes", | ||||
|                 "fr": "Réservé aux clients du lieu", | ||||
|                 "nl": "Enkel toegankelijk voor klanten", | ||||
|                 "de": "Nur für Kunden zugänglich" | ||||
|               } | ||||
|             }, | ||||
|             { | ||||
|               "if": "access=private", | ||||
|               "then": { | ||||
|                 "en": "Not accessible to the general public (e.g. only accesible to staff, the owners, ...)", | ||||
|                 "ca": "No accessible al públic en general (ex. només accesible a treballadors, propietaris, ...)", | ||||
|                 "es": "No accesible al público en general (ex. sólo accesible a trabajadores, propietarios, ...)", | ||||
|                 "fr": "Non accessible au public (par exemple réservé au personnel, au propriétaire, ...)", | ||||
|                 "nl": "Niet toegankelijk voor het publiek (bv. enkel voor personneel, de eigenaar, ...)", | ||||
|                 "de": "Nicht für die Öffentlichkeit zugänglich (z.B. nur für das Personal, die Eigentümer, ...)" | ||||
|               } | ||||
|             } | ||||
|           ] | ||||
|         }, | ||||
|         { | ||||
|           "question": { | ||||
|             "en": "On which floor is this defibrillator located?", | ||||
|             "ca": "A quina planta està el desfibril·lador localitzat?", | ||||
|             "es": "¿En qué planta se encuentra el defibrilador localizado?", | ||||
|             "fr": "À quel étage est situé ce défibrillateur ?", | ||||
|             "nl": "Op welke verdieping bevindt deze defibrillator zich?", | ||||
|             "de": "In welchem Stockwerk befindet sich dieser Defibrillator?" | ||||
|           }, | ||||
|           "condition": { | ||||
|             "and": [ | ||||
|               "indoor=yes", | ||||
|               "access!~private" | ||||
|             ] | ||||
|           }, | ||||
|           "freeform": { | ||||
|             "key": "level", | ||||
|             "type": "int" | ||||
|           }, | ||||
|           "render": { | ||||
|             "en": "This defibrallator is on floor {level}", | ||||
|             "ca": "Aquest desfibril·lador és a la planta {level}", | ||||
|             "es": "El desfibrilador se encuentra en la planta {level}", | ||||
|             "fr": "Ce défibrillateur est à l'étage {level}", | ||||
|             "nl": "De defibrillator bevindt zicht op verdieping {level}", | ||||
|             "de": "Dieser Defibrallator befindet sich im {level}. Stockwerk" | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           "render": { | ||||
|             "nl": "<i>Meer informatie over de locatie:</i><br/>{defibrillator:location}", | ||||
|             "en": "<i>Extra information about the location:</i><br/>{defibrillator:location}" | ||||
|           }, | ||||
|           "question": { | ||||
|             "en": "Please give some explanation on where the defibrillator can be found", | ||||
|             "ca": "Dóna detalls d'on es pot trobar el desfibril·lador", | ||||
|             "es": "Da detalles de dónde se puede encontrar el desfibrilador", | ||||
|             "fr": "Veuillez indiquez plus précisément où se situe le défibrillateur", | ||||
|             "nl": "Gelieve meer informatie te geven over de exacte locatie van de defibrillator", | ||||
|             "de": "Bitte geben Sie einige Erläuterungen dazu, wo der Defibrillator zu finden ist" | ||||
|           }, | ||||
|           "freeform": { | ||||
|             "type": "text", | ||||
|             "key": "defibrillator:location" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     } | ||||
|     "defibrillator" | ||||
|   ] | ||||
| } | ||||
|  |  | |||
							
								
								
									
										53
									
								
								assets/themes/aed/aed_brugge.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								assets/themes/aed/aed_brugge.json
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,53 @@ | |||
| { | ||||
|   "id": "aed_brugge", | ||||
|   "title": { | ||||
|     "nl": "Open AED-kaart - Brugge edition" | ||||
|   }, | ||||
|   "maintainer": "MapComplete", | ||||
|   "icon": "./assets/themes/aed/logo.svg", | ||||
|   "description": { | ||||
|     "nl": "Op deze kaart kan je informatie over AEDs vinden en verbeteren + een export van de brugse defibrillatoren" | ||||
|   }, | ||||
|   "language": [ | ||||
|     "nl" | ||||
|   ], | ||||
|   "version": "2021-05-16", | ||||
|   "startLat": 51.25634, | ||||
|   "startLon": 3.195682, | ||||
|   "startZoom": 12, | ||||
|   "layers": [ | ||||
|     "defibrillator", | ||||
|     { | ||||
|       "id": "Brugge", | ||||
|       "name": "Brugse dataset", | ||||
|       "source": { | ||||
|         "osmTags": "Brugs volgnummer~*", | ||||
|         "geoJson": "https://raw.githubusercontent.com/pietervdvn/pietervdvn.github.io/master/aeds_brugge.json" | ||||
|       }, | ||||
|       "calculatedTags": [ | ||||
|         "_closest_osm_aed=feat.closest('defibrillator')?.properties?.id", | ||||
|         "_closest_osm_aed_distance=feat.distanceTo(feat.properties._closest_osm_aed) * 1000", | ||||
|         "_has_closeby_feature=Number(feat.properties._closest_osm_aed_distance) < 25 ? 'yes' : 'no'" | ||||
|       ], | ||||
|       "title": "AED in Brugse dataset", | ||||
|       "icon": { | ||||
|         "render": "circle:green", | ||||
|         "mappings": [ | ||||
|           { | ||||
|             "if": "_has_closeby_feature=yes", | ||||
|             "then": "circle:#ffff00aa" | ||||
|           }, | ||||
|           { | ||||
|             "if": "Status=oud", | ||||
|             "then": "circle:red" | ||||
|           } | ||||
|         ] | ||||
|       }, | ||||
|       "iconSize": "20,20,center", | ||||
|       "tagRenderings": [ | ||||
|         "all_tags" | ||||
|       ] | ||||
|     } | ||||
|   ], | ||||
|   "hideFromOverview": true | ||||
| } | ||||
|  | @ -17,6 +17,8 @@ import MetaTagging from "../Logic/MetaTagging"; | |||
| import LayerConfig from "../Customizations/JSON/LayerConfig"; | ||||
| import {GeoOperations} from "../Logic/GeoOperations"; | ||||
| import {fail} from "assert"; | ||||
| import {UIEventSource} from "../Logic/UIEventSource"; | ||||
| import DummyFeatureSource from "../Logic/FeatureSource/DummyFeatureSource"; | ||||
| 
 | ||||
| 
 | ||||
| function createOverpassObject(theme: LayoutConfig) { | ||||
|  | @ -167,7 +169,7 @@ async function postProcess(targetdir: string, r: TileRange, theme: LayoutConfig, | |||
|             // Extract the relationship information
 | ||||
|             const relations = ExtractRelations.BuildMembershipTable(ExtractRelations.GetRelationElements(rawOsm)) | ||||
| 
 | ||||
|             MetaTagging.addMetatags(featuresFreshness, relations, theme.layers, false); | ||||
|             MetaTagging.addMetatags(featuresFreshness, new DummyFeatureSource(new UIEventSource<{feature: any; freshness: Date}[]>(featuresFreshness)) , relations, theme.layers, false); | ||||
| 
 | ||||
| 
 | ||||
|             for (const feature of geojson.features) { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue