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
|
@ -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() {
|
||||
|
||||
|
@ -24,11 +25,16 @@ export class ElementStorage {
|
|||
addOrGetElement(feature: any): UIEventSource<any> {
|
||||
const elementId = feature.properties.id;
|
||||
const newProperties = feature.properties;
|
||||
|
||||
|
||||
const es = this.addOrGetById(elementId, newProperties)
|
||||
|
||||
// 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,8 +189,8 @@ 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 },
|
||||
|
@ -27,6 +28,8 @@ export default class GeoJsonSource implements FeatureSource {
|
|||
let url = flayer.layerDef.source.geojsonSource.replace("{layer}", flayer.layerDef.id);
|
||||
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 }[]>([])
|
||||
|
||||
|
|
|
@ -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 name;
|
||||
|
||||
constructor(source: FeatureSource) {
|
||||
const self = this;
|
||||
this.name = "MetaTagging of "+source.name
|
||||
source.features.addCallbackAndRun((featuresFreshness: { feature: any, freshness: Date }[]) => {
|
||||
if (featuresFreshness === undefined) {
|
||||
return;
|
||||
}
|
||||
featuresFreshness.forEach(featureFresh => {
|
||||
const feature = featureFresh.feature;
|
||||
|
||||
if (Hash.hash.data === feature.properties.id) {
|
||||
State.state.selectedElement.setData(feature);
|
||||
}
|
||||
})
|
||||
public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>(undefined);
|
||||
|
||||
MetaTagging.addMetatags(featuresFreshness, State.state.knownRelations.data, State.state.layoutToUse.data.layers);
|
||||
self.features.setData(featuresFreshness);
|
||||
});
|
||||
public readonly name;
|
||||
|
||||
constructor(allFeaturesSource: FeatureSource, source: FeatureSource, updateTrigger?: UIEventSource<any>) {
|
||||
const self = this;
|
||||
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);
|
||||
}
|
||||
})
|
||||
|
||||
MetaTagging.addMetatags(featuresFreshness,
|
||||
allFeaturesSource,
|
||||
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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue