forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			91 lines
		
	
	
	
		
			3.7 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			91 lines
		
	
	
	
		
			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())
 | 
						|
    }
 | 
						|
}
 |