/** * Merges features from different featureSources for a single layer * Uses the freshest feature available in the case multiple sources offer data with the same identifier */ import {UIEventSource} from "../../UIEventSource"; import FeatureSource, {FeatureSourceForLayer, IndexedFeatureSource, Tiled} from "../FeatureSource"; import FilteredLayer from "../../../Models/FilteredLayer"; import {Tiles} from "../../../Models/TileRange"; import {BBox} from "../../BBox"; export default class FeatureSourceMerger implements FeatureSourceForLayer, Tiled, IndexedFeatureSource { public features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); public readonly name; public readonly layer: FilteredLayer public readonly tileIndex: number; public readonly bbox: BBox; public readonly containedIds: UIEventSource> = new UIEventSource>(new Set()) private readonly _sources: UIEventSource; constructor(layer: FilteredLayer, tileIndex: number, bbox: BBox, sources: UIEventSource) { this.tileIndex = tileIndex; this.bbox = bbox; this._sources = sources; this.layer = layer; this.name = "FeatureSourceMerger(" + layer.layerDef.id + ", " + Tiles.tile_from_index(tileIndex).join(",") + ")" const self = this; const handledSources = new Set(); sources.addCallbackAndRunD(sources => { let newSourceRegistered = false; for (let i = 0; i < sources.length; i++) { let source = sources[i]; if (handledSources.has(source)) { continue } handledSources.add(source) newSourceRegistered = true source.features.addCallback(() => { self.Update(); }); if (newSourceRegistered) { self.Update(); } } }) } private Update() { let somethingChanged = false; const all: Map = new Map(); // We seed the dictionary with the previously loaded features const oldValues = this.features.data ?? []; for (const oldValue of oldValues) { all.set(oldValue.feature.id, oldValue) } for (const source of this._sources.data) { if (source?.features?.data === undefined) { continue; } for (const f of source.features.data) { const id = f.feature.properties.id; if (!all.has(id)) { // This is a new feature somethingChanged = true; all.set(id, f); continue; } // This value has been seen already, either in a previous run or by a previous datasource // Let's figure out if something changed const oldV = all.get(id); if (oldV.freshness < f.freshness) { // Jup, this feature is fresher all.set(id, f); somethingChanged = true; } } } if (!somethingChanged) { // We don't bother triggering an update return; } const newList = []; all.forEach((value, _) => { newList.push(value) }) this.containedIds.setData(new Set(all.keys())) this.features.setData(newList); } }