forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			94 lines
		
	
	
		
			No EOL
		
	
	
		
			3.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			94 lines
		
	
	
		
			No EOL
		
	
	
		
			3.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import FeatureSource, {FeatureSourceForLayer, Tiled} from "./FeatureSource";
 | 
						|
import {UIEventSource} 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: UIEventSource<FilteredLayer[]>,
 | 
						|
                handleLayerData: (source: FeatureSourceForLayer & Tiled) => void,
 | 
						|
                upstream: FeatureSource,
 | 
						|
                options?: {
 | 
						|
                    tileIndex?: number,
 | 
						|
                    handleLeftovers?: (featuresWithoutLayer: any[]) => void
 | 
						|
                }) {
 | 
						|
 | 
						|
        const knownLayers = new Map<string, FeatureSourceForLayer & Tiled>()
 | 
						|
 | 
						|
        function update() {
 | 
						|
            const features = upstream.features?.data;
 | 
						|
            if (features === undefined) {
 | 
						|
                return;
 | 
						|
            }
 | 
						|
            if (layers.data === undefined) {
 | 
						|
                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 = []
 | 
						|
 | 
						|
            function addTo(layer: FilteredLayer, feature: { feature, freshness }) {
 | 
						|
                const id = layer.layerDef.id
 | 
						|
                const list = featuresPerLayer.get(id)
 | 
						|
                if (list !== undefined) {
 | 
						|
                    list.push(feature)
 | 
						|
                } else {
 | 
						|
                    featuresPerLayer.set(id, [feature])
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            for (const f of features) {
 | 
						|
                for (const layer of layers.data) {
 | 
						|
                    if (layer.layerDef.source.osmTags.matchesProperties(f.feature.properties)) {
 | 
						|
                        // We have found our matching layer!
 | 
						|
                        addTo(layer, f)
 | 
						|
                        if (!layer.layerDef.passAllFeatures) {
 | 
						|
                            // If not 'passAllFeatures', we are done for this feature
 | 
						|
                            break;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    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())
 | 
						|
    }
 | 
						|
} |