Refactoring of metatagging and extrafunctions to splice out the relation memberships, add calculatedTags and metatags into cache

This commit is contained in:
Pieter Vander Vennet 2021-04-22 13:30:00 +02:00
parent 362abbf079
commit 0dec1d0f75
10 changed files with 98 additions and 54 deletions

View file

@ -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);
}
}

View file

@ -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 => {

View file

@ -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))

View file

@ -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);
});
}

View file

@ -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)

View file

@ -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) {

View file

@ -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 {