forked from MapComplete/MapComplete
		
	Refactoring of metatagging and extrafunctions to splice out the relation memberships, add calculatedTags and metatags into cache
This commit is contained in:
		
							parent
							
								
									362abbf079
								
							
						
					
					
						commit
						0dec1d0f75
					
				
					 10 changed files with 98 additions and 54 deletions
				
			
		|  | @ -2,6 +2,7 @@ import {GeoOperations} from "./GeoOperations"; | |||
| import {UIElement} from "../UI/UIElement"; | ||||
| import Combine from "../UI/Base/Combine"; | ||||
| import State from "../State"; | ||||
| import {Relation} from "./Osm/ExtractRelations"; | ||||
| 
 | ||||
| export class ExtraFunction { | ||||
| 
 | ||||
|  | @ -40,11 +41,11 @@ Some advanced functions are available on <b>feat</b> as well: | |||
|         "overlapWith", | ||||
|         "Gives a list of features from the specified layer which this feature overlaps with, the amount of overlap in m². The returned value is <b>{ feat: GeoJSONFeature, overlap: number}</b>", | ||||
|         ["...layerIds - one or more layer ids  of the layer from which every feature is checked for overlap)"], | ||||
|         (featuresPerLayer, feat) => { | ||||
|         (params, feat) => { | ||||
|             return (...layerIds: string[]) => { | ||||
|                 const result = [] | ||||
|                 for (const layerId of layerIds) { | ||||
|                     const otherLayer = featuresPerLayer.get(layerId); | ||||
|                     const otherLayer = params.featuresPerLayer.get(layerId); | ||||
|                     if (otherLayer === undefined) { | ||||
|                         continue; | ||||
|                     } | ||||
|  | @ -80,10 +81,10 @@ Some advanced functions are available on <b>feat</b> as well: | |||
|         "closest", | ||||
|         "Given either a list of geojson features or a single layer name, gives the single object which is nearest to the feature. In the case of ways/polygons, only the centerpoint is considered.", | ||||
|         ["list of features"], | ||||
|         (featuresPerLayer, feature) => { | ||||
|         (params, feature) => { | ||||
|             return (features) => { | ||||
|                 if (typeof features === "string") { | ||||
|                     features = featuresPerLayer.get(features) | ||||
|                     features = params.featuresPerLayer.get(features) | ||||
|                 } | ||||
|                 let closestFeature = undefined; | ||||
|                 let closestDistance = undefined; | ||||
|  | @ -118,11 +119,8 @@ Some advanced functions are available on <b>feat</b> as well: | |||
|         "memberships", | ||||
|         "Gives a list of {role: string, relation: Relation}-objects, containing all the relations that this feature is part of. \n\nFor example: `_part_of_walking_routes=feat.memberships().map(r => r.relation.tags.name).join(';')`", | ||||
|         [], | ||||
|         (featuresPerLayer, feature) => { | ||||
|             return () => { | ||||
|                return State.state.knownRelations.data?.get(feature.id) ?? []; | ||||
|             } | ||||
| 
 | ||||
|         (params, feature) => { | ||||
|             return () =>   params.relations ?? []; | ||||
|         } | ||||
|     ) | ||||
| 
 | ||||
|  | @ -130,9 +128,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: (featuresPerLayer: Map<string, any[]>, 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: ((featuresPerLayer: Map<string, any[]>, 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; | ||||
|  | @ -140,9 +138,9 @@ Some advanced functions are available on <b>feat</b> as well: | |||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public static FullPatchFeature(featuresPerLayer: Map<string, any[]>, feature) { | ||||
|     public static FullPatchFeature(featuresPerLayer: Map<string, any[]>,relations: {role: string, relation: Relation}[], feature) { | ||||
|         for (const func of ExtraFunction.allFuncs) { | ||||
|             func.PatchFeature(featuresPerLayer, feature); | ||||
|             func.PatchFeature(featuresPerLayer, relations, feature); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -168,7 +166,8 @@ Some advanced functions are available on <b>feat</b> as well: | |||
|         ]); | ||||
|     } | ||||
| 
 | ||||
|     public PatchFeature(featuresPerLayer: Map<string, any[]>, feature: any) { | ||||
|         feature[this._name] = this._f(featuresPerLayer, feature); | ||||
|     public PatchFeature(featuresPerLayer: Map<string, any[]>, relations: {role: string, relation: Relation}[], feature: any) { | ||||
|       | ||||
|         feature[this._name] = this._f({featuresPerLayer: featuresPerLayer, relations: relations}, feature); | ||||
|     } | ||||
| } | ||||
|  | @ -11,6 +11,10 @@ import LayerConfig from "../../Customizations/JSON/LayerConfig"; | |||
| export default class FeatureDuplicatorPerLayer implements FeatureSource { | ||||
|     public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; | ||||
| 
 | ||||
|      | ||||
|     public static GetMatchingLayerId(){ | ||||
|          | ||||
|     } | ||||
| 
 | ||||
|     constructor(layers: UIEventSource<{ layerDef: LayerConfig }[]>, upstream: FeatureSource) { | ||||
|         this.features = upstream.features.map(features => { | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ export default class FeaturePipeline implements FeatureSource { | |||
| 
 | ||||
|         const geojsonSources: FeatureSource [] = GeoJsonSource | ||||
|             .ConstructMultiSource(flayers.data, locationControl) | ||||
|             .map(geojsonSource => new RegisteringFeatureSource(new FeatureDuplicatorPerLayer(flayers, geojsonSource))); | ||||
|             .map(geojsonSource =>  new RegisteringFeatureSource(new FeatureDuplicatorPerLayer(flayers, geojsonSource))); | ||||
|          | ||||
|         const amendedLocalStorageSource = | ||||
|             new RememberingSource(new RegisteringFeatureSource(new FeatureDuplicatorPerLayer(flayers, new LocalStorageSource(layout)) | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ import {UIEventSource} from "../UIEventSource"; | |||
| import State from "../../State"; | ||||
| import Hash from "../Web/Hash"; | ||||
| import MetaTagging from "../MetaTagging"; | ||||
| import ExtractRelations from "../Osm/ExtractRelations"; | ||||
| 
 | ||||
| export default class MetaTaggingFeatureSource implements FeatureSource { | ||||
|     features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{feature: any; freshness: Date}[]>(undefined); | ||||
|  | @ -21,7 +22,7 @@ export default class MetaTaggingFeatureSource implements FeatureSource { | |||
|                     } | ||||
|                 }) | ||||
| 
 | ||||
|                 MetaTagging.addMetatags(featuresFreshness, State.state.layoutToUse.data.layers); | ||||
|                 MetaTagging.addMetatags(featuresFreshness, State.state.knownRelations.data, State.state.layoutToUse.data.layers); | ||||
|                 self.features.setData(featuresFreshness); | ||||
|         }); | ||||
|     } | ||||
|  |  | |||
|  | @ -1,6 +1,14 @@ | |||
| import LayerConfig from "../Customizations/JSON/LayerConfig"; | ||||
| import SimpleMetaTagger from "./SimpleMetaTagger"; | ||||
| import {ExtraFunction} from "./ExtraFunction"; | ||||
| import State from "../State"; | ||||
| import {Relation} from "./Osm/ExtractRelations"; | ||||
| 
 | ||||
| 
 | ||||
| interface Params { | ||||
|     featuresPerLayer: Map<string, any[]>, | ||||
|     memberships: Map<string, { role: string, relation: Relation }[]> | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Metatagging adds various tags to the elements, e.g. lat, lon, surface area, ... | ||||
|  | @ -14,7 +22,8 @@ export default class MetaTagging { | |||
|      * An actor which adds metatags on every feature in the given object | ||||
|      * The features are a list of geojson-features, with a "properties"-field and geometry | ||||
|      */ | ||||
|     static addMetatags(features: { feature: any; freshness: Date }[], layers: LayerConfig[]) { | ||||
|     static addMetatags(features: { feature: any; freshness: Date }[], | ||||
|                        relations: Map<string, { role: string, relation: Relation }[]>, layers: LayerConfig[]) { | ||||
| 
 | ||||
|         for (const metatag of SimpleMetaTagger.metatags) { | ||||
|             try { | ||||
|  | @ -26,7 +35,7 @@ export default class MetaTagging { | |||
|         } | ||||
| 
 | ||||
|         // The functions - per layer - which add the new keys
 | ||||
|         const layerFuncs = new Map<string, ((featursPerLayer: Map<string, any[]>, feature: any) => void)>(); | ||||
|         const layerFuncs = new Map<string, ((params: Params, feature: any) => void)>(); | ||||
|         for (const layer of layers) { | ||||
|             layerFuncs.set(layer.id, this.createRetaggingFunc(layer)); | ||||
|         } | ||||
|  | @ -48,27 +57,26 @@ export default class MetaTagging { | |||
|             if (f === undefined) { | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             f(featuresPerLayer, feature.feature) | ||||
|             f({featuresPerLayer: featuresPerLayer, memberships: relations}, feature.feature) | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     private static createRetaggingFunc(layer: LayerConfig): ((featuresPerLayer: Map<string, any[]>, feature: any) => void) { | ||||
|     private static createRetaggingFunc(layer: LayerConfig): | ||||
|         ((params: Params, feature: any) => void) { | ||||
|         const calculatedTags: [string, string][] = layer.calculatedTags; | ||||
|         if (calculatedTags === undefined) { | ||||
|             return undefined; | ||||
|         } | ||||
| 
 | ||||
|         const functions: ((featuresPerLayer: Map<string, any[]>, feature: any) => void)[] = []; | ||||
|         const functions: ((params: Params, feature: any) => void)[] = []; | ||||
|         for (const entry of calculatedTags) { | ||||
|             const key = entry[0] | ||||
|             const code = entry[1]; | ||||
|             if (code === undefined) { | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             const func = new Function("feat", "return " + code + ";"); | ||||
| 
 | ||||
|             const f = (featuresPerLayer, feature: any) => { | ||||
|  | @ -76,16 +84,17 @@ export default class MetaTagging { | |||
|             } | ||||
|             functions.push(f) | ||||
|         } | ||||
|         return (featuresPerLayer: Map<string, any[]>, feature) => { | ||||
|         return (params: Params, feature) => { | ||||
|             const tags = feature.properties | ||||
|             if (tags === undefined) { | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             ExtraFunction.FullPatchFeature(featuresPerLayer, feature); | ||||
|             const relations = params.memberships.get(feature.properties.id) | ||||
|             ExtraFunction.FullPatchFeature(params.featuresPerLayer, relations, feature); | ||||
|             try { | ||||
|                 for (const f of functions) { | ||||
|                     f(featuresPerLayer, feature); | ||||
|                     f(params, feature); | ||||
|                 } | ||||
|             } catch (e) { | ||||
|                 console.error("While calculating a tag value: ", e) | ||||
|  |  | |||
|  | @ -17,7 +17,6 @@ export default class ExtractRelations { | |||
| 
 | ||||
|     public static RegisterRelations(overpassJson: any) : void{ | ||||
|         const memberships = ExtractRelations.BuildMembershipTable(ExtractRelations.GetRelationElements(overpassJson)) | ||||
|         console.log("Assigned memberships: ", memberships) | ||||
|         State.state.knownRelations.setData(memberships) | ||||
|     } | ||||
| 
 | ||||
|  | @ -40,7 +39,7 @@ export default class ExtractRelations { | |||
|      * @param relations | ||||
|      * @constructor | ||||
|      */ | ||||
|     private static BuildMembershipTable(relations: Relation[]): Map<string, { role: string, relation: Relation, }[]> { | ||||
|     public static BuildMembershipTable(relations: Relation[]): Map<string, { role: string, relation: Relation }[]> { | ||||
|         const memberships = new Map<string, { role: string, relation: Relation }[]>() | ||||
| 
 | ||||
|         for (const relation of relations) { | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ import {UIElement} from "../UI/UIElement"; | |||
| import Combine from "../UI/Base/Combine"; | ||||
| import UpdateTagsFromOsmAPI from "./Actors/UpdateTagsFromOsmAPI"; | ||||
| 
 | ||||
| 
 | ||||
| export default class SimpleMetaTagger { | ||||
|     public readonly keys: string[]; | ||||
|     public readonly doc: string; | ||||
|  | @ -89,7 +90,12 @@ export default class SimpleMetaTagger { | |||
|         ["_isOpen", "_isOpen:description"], | ||||
|         "If 'opening_hours' is present, it will add the current state of the feature (being 'yes' or 'no')", | ||||
|         (feature => { | ||||
| 
 | ||||
|             if(Utils.runningFromConsole){ | ||||
|                 // We are running from console, thus probably creating a cache
 | ||||
|                 // isOpen is irrelevant
 | ||||
|                 return | ||||
|             } | ||||
|              | ||||
|             const tagsSource = State.state.allElements.getEventSourceById(feature.properties.id); | ||||
|             tagsSource.addCallbackAndRun(tags => { | ||||
|                 if (tags.opening_hours === undefined || tags._country === undefined) { | ||||
|  | @ -317,7 +323,7 @@ export default class SimpleMetaTagger { | |||
|     ]; | ||||
| 
 | ||||
|     static GetCountryCodeFor(lon: number, lat: number, callback: (country: string) => void) { | ||||
|         SimpleMetaTagger.coder.GetCountryCodeFor(lon, lat, callback) | ||||
|         SimpleMetaTagger.coder?.GetCountryCodeFor(lon, lat, callback) | ||||
|     } | ||||
| 
 | ||||
|     static HelpText(): UIElement { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue