mapcomplete/Logic/FeatureSource/PerLayerFeatureSourceSplitter.ts

92 lines
No EOL
3.7 KiB
TypeScript

import FeatureSource, {FeatureSourceForLayer, Tiled} from "./FeatureSource";
import {Store} from "../UIEventSource";
import FilteredLayer from "../../Models/FilteredLayer";
import SimpleFeatureSource from "./Sources/SimpleFeatureSource";
/**
* In some rare cases, some elements are shown on multiple layers (when 'passthrough' is enabled)
* If this is the case, multiple objects with a different _matching_layer_id are generated.
* In any case, this featureSource marks the objects with _matching_layer_id
*/
export default class PerLayerFeatureSourceSplitter {
constructor(layers: Store<FilteredLayer[]>,
handleLayerData: (source: FeatureSourceForLayer & Tiled) => void,
upstream: FeatureSource,
options?: {
tileIndex?: number,
handleLeftovers?: (featuresWithoutLayer: any[]) => void
}) {
const knownLayers = new Map<string, SimpleFeatureSource>()
function update() {
const features = upstream.features?.data;
if (features === undefined) {
return;
}
if (layers.data === undefined || layers.data.length === 0) {
return;
}
// We try to figure out (for each feature) in which feature store it should be saved.
// Note that this splitter is only run when it is invoked by the overpass feature source, so we can't be sure in which layer it should go
const featuresPerLayer = new Map<string, { feature, freshness } []>();
const noLayerFound = []
for (const layer of layers.data) {
featuresPerLayer.set(layer.layerDef.id, [])
}
for (const f of features) {
let foundALayer = false;
for (const layer of layers.data) {
if (layer.layerDef.source.osmTags.matchesProperties(f.feature.properties)) {
// We have found our matching layer!
featuresPerLayer.get(layer.layerDef.id).push(f)
foundALayer = true;
if (!layer.layerDef.passAllFeatures) {
// If not 'passAllFeatures', we are done for this feature
break
}
}
}
if(!foundALayer){
noLayerFound.push(f)
}
}
// At this point, we have our features per layer as a list
// We assign them to the correct featureSources
for (const layer of layers.data) {
const id = layer.layerDef.id;
const features = featuresPerLayer.get(id)
if (features === undefined) {
// No such features for this layer
continue;
}
let featureSource = knownLayers.get(id)
if (featureSource === undefined) {
// Not yet initialized - now is a good time
featureSource = new SimpleFeatureSource(layer, options?.tileIndex)
featureSource.features.setData(features)
knownLayers.set(id, featureSource)
handleLayerData(featureSource)
} else {
featureSource.features.setData(features)
}
}
// AT last, the leftovers are handled
if (options?.handleLeftovers !== undefined && noLayerFound.length > 0) {
options.handleLeftovers(noLayerFound)
}
}
layers.addCallback(_ => update())
upstream.features.addCallbackAndRunD(_ => update())
}
}