forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			112 lines
		
	
	
	
		
			3.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			112 lines
		
	
	
	
		
			3.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
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<Set<string>> = new UIEventSource<Set<string>>(
 | 
						|
        new Set()
 | 
						|
    )
 | 
						|
    private readonly _sources: UIEventSource<FeatureSource[]>
 | 
						|
    /**
 | 
						|
     * 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
 | 
						|
     */
 | 
						|
    constructor(
 | 
						|
        layer: FilteredLayer,
 | 
						|
        tileIndex: number,
 | 
						|
        bbox: BBox,
 | 
						|
        sources: UIEventSource<FeatureSource[]>
 | 
						|
    ) {
 | 
						|
        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<FeatureSource>()
 | 
						|
 | 
						|
        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<string, { feature: any; freshness: Date }> = new Map<
 | 
						|
            string,
 | 
						|
            { feature: any; freshness: Date }
 | 
						|
        >()
 | 
						|
        // 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)
 | 
						|
    }
 | 
						|
}
 |