forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			156 lines
		
	
	
		
			No EOL
		
	
	
		
			6.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			156 lines
		
	
	
		
			No EOL
		
	
	
		
			6.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
 | 
						|
import FeaturePipeline from "../FeatureSource/FeaturePipeline";
 | 
						|
import {Tiles} from "../../Models/TileRange";
 | 
						|
import ShowDataLayer from "../../UI/ShowDataLayer/ShowDataLayer";
 | 
						|
import {TileHierarchyAggregator} from "../../UI/ShowDataLayer/TileHierarchyAggregator";
 | 
						|
import ShowTileInfo from "../../UI/ShowDataLayer/ShowTileInfo";
 | 
						|
import {UIEventSource} from "../UIEventSource";
 | 
						|
import MapState from "./MapState";
 | 
						|
import SelectedFeatureHandler from "../Actors/SelectedFeatureHandler";
 | 
						|
import Hash from "../Web/Hash";
 | 
						|
import {BBox} from "../BBox";
 | 
						|
import FeatureInfoBox from "../../UI/Popup/FeatureInfoBox";
 | 
						|
import {FeatureSourceForLayer, Tiled} from "../FeatureSource/FeatureSource";
 | 
						|
import MetaTagRecalculator from "../FeatureSource/Actors/MetaTagRecalculator";
 | 
						|
 | 
						|
export default class FeaturePipelineState extends MapState {
 | 
						|
 | 
						|
    /**
 | 
						|
     * The piece of code which fetches data from various sources and shows it on the background map
 | 
						|
     */
 | 
						|
    public readonly featurePipeline: FeaturePipeline;
 | 
						|
    private readonly featureAggregator: TileHierarchyAggregator;
 | 
						|
    private readonly metatagRecalculator: MetaTagRecalculator
 | 
						|
 | 
						|
    constructor(layoutToUse: LayoutConfig) {
 | 
						|
        super(layoutToUse);
 | 
						|
 | 
						|
        const clustering = layoutToUse.clustering
 | 
						|
        this.featureAggregator = TileHierarchyAggregator.createHierarchy(this);
 | 
						|
        const clusterCounter = this.featureAggregator
 | 
						|
        const self = this;
 | 
						|
 | 
						|
        /**
 | 
						|
         * We are a bit in a bind:
 | 
						|
         * There is the featurePipeline, which creates some sources during construction
 | 
						|
         * THere is the metatagger, which needs to have these sources registered AND which takes a FeaturePipeline as argument
 | 
						|
         *
 | 
						|
         * This is a bit of a catch-22 (except that it isn't)
 | 
						|
         * The sources that are registered in the constructor are saved into 'registeredSources' temporary
 | 
						|
         *
 | 
						|
         */
 | 
						|
        const sourcesToRegister = []
 | 
						|
 | 
						|
        function registerRaw(source: FeatureSourceForLayer & Tiled) {
 | 
						|
            if (self.metatagRecalculator === undefined) {
 | 
						|
                sourcesToRegister.push(source)
 | 
						|
            } else {
 | 
						|
                self.metatagRecalculator.registerSource(source)
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        function registerSource(source: FeatureSourceForLayer & Tiled) {
 | 
						|
 | 
						|
            clusterCounter.addTile(source)
 | 
						|
            const sourceBBox = source.features.map(allFeatures => BBox.bboxAroundAll(allFeatures.map(f => BBox.get(f.feature))))
 | 
						|
 | 
						|
            // Do show features indicates if the respective 'showDataLayer' should be shown. It can be hidden by e.g. clustering
 | 
						|
            const doShowFeatures = source.features.map(
 | 
						|
                f => {
 | 
						|
                    const z = self.locationControl.data.zoom
 | 
						|
 | 
						|
                    if (!source.layer.isDisplayed.data) {
 | 
						|
                        return false;
 | 
						|
                    }
 | 
						|
 | 
						|
                    const bounds = self.currentBounds.data
 | 
						|
                    if (bounds === undefined) {
 | 
						|
                        // Map is not yet displayed
 | 
						|
                        return false;
 | 
						|
                    }
 | 
						|
 | 
						|
                    if (!sourceBBox.data.overlapsWith(bounds)) {
 | 
						|
                        // Not within range -> features are hidden
 | 
						|
                        return false
 | 
						|
                    }
 | 
						|
 | 
						|
 | 
						|
                    if (z < source.layer.layerDef.minzoom) {
 | 
						|
                        // Layer is always hidden for this zoom level
 | 
						|
                        return false;
 | 
						|
                    }
 | 
						|
 | 
						|
                    if (z > clustering.maxZoom) {
 | 
						|
                        return true
 | 
						|
                    }
 | 
						|
 | 
						|
                    if (f.length > clustering.minNeededElements) {
 | 
						|
                        // This tile alone already has too much features
 | 
						|
                        return false
 | 
						|
                    }
 | 
						|
 | 
						|
                    let [tileZ, tileX, tileY] = Tiles.tile_from_index(source.tileIndex);
 | 
						|
                    if (tileZ >= z) {
 | 
						|
 | 
						|
                        while (tileZ > z) {
 | 
						|
                            tileZ--
 | 
						|
                            tileX = Math.floor(tileX / 2)
 | 
						|
                            tileY = Math.floor(tileY / 2)
 | 
						|
                        }
 | 
						|
 | 
						|
                        if (clusterCounter.getTile(Tiles.tile_index(tileZ, tileX, tileY))?.totalValue > clustering.minNeededElements) {
 | 
						|
                            // To much elements
 | 
						|
                            return false
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
 | 
						|
 | 
						|
                    return true
 | 
						|
                }, [self.currentBounds, source.layer.isDisplayed, sourceBBox]
 | 
						|
            )
 | 
						|
 | 
						|
            new ShowDataLayer(
 | 
						|
                {
 | 
						|
                    features: source,
 | 
						|
                    leafletMap: self.leafletMap,
 | 
						|
                    layerToShow: source.layer.layerDef,
 | 
						|
                    doShowLayer: doShowFeatures,
 | 
						|
                    selectedElement: self.selectedElement,
 | 
						|
                    state: self,
 | 
						|
                    popup: (tags, layer) => new FeatureInfoBox(tags, layer, self)
 | 
						|
                }
 | 
						|
            )
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        this.featurePipeline = new FeaturePipeline(registerSource, this, {handleRawFeatureSource: registerRaw});
 | 
						|
        this.metatagRecalculator = new MetaTagRecalculator(this, this.featurePipeline)
 | 
						|
        this.metatagRecalculator.registerSource(this.currentView, true)
 | 
						|
 | 
						|
        sourcesToRegister.forEach(source => self.metatagRecalculator.registerSource(source))
 | 
						|
 | 
						|
        new SelectedFeatureHandler(Hash.hash, this)
 | 
						|
 | 
						|
        this.AddClusteringToMap(this.leafletMap)
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Adds the cluster-tiles to the given map
 | 
						|
     * @param leafletMap: a UIEventSource possible having a leaflet map
 | 
						|
     * @constructor
 | 
						|
     */
 | 
						|
    public AddClusteringToMap(leafletMap: UIEventSource<any>) {
 | 
						|
        const clustering = this.layoutToUse.clustering
 | 
						|
        const self = this;
 | 
						|
        new ShowDataLayer({
 | 
						|
            features: this.featureAggregator.getCountsForZoom(clustering, this.locationControl, clustering.minNeededElements),
 | 
						|
            leafletMap: leafletMap,
 | 
						|
            layerToShow: ShowTileInfo.styling,
 | 
						|
            popup: this.featureSwitchIsDebugging.data ? (tags, layer) => new FeatureInfoBox(tags, layer, self) : undefined,
 | 
						|
            state: this
 | 
						|
        })
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
} |