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())
 | |
|     }
 | |
| } |