forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			88 lines
		
	
	
	
		
			3.4 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			88 lines
		
	
	
	
		
			3.4 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import FilteredLayer from "../../../Models/FilteredLayer"
 | 
						|
import { FeatureSourceForLayer, Tiled } from "../FeatureSource"
 | 
						|
import { UIEventSource } from "../../UIEventSource"
 | 
						|
import TileHierarchy from "./TileHierarchy"
 | 
						|
import { Tiles } from "../../../Models/TileRange"
 | 
						|
import { BBox } from "../../BBox"
 | 
						|
 | 
						|
/***
 | 
						|
 * A tiled source which dynamically loads the required tiles at a fixed zoom level
 | 
						|
 */
 | 
						|
export default class DynamicTileSource implements TileHierarchy<FeatureSourceForLayer & Tiled> {
 | 
						|
    public readonly loadedTiles: Map<number, FeatureSourceForLayer & Tiled>
 | 
						|
    private readonly _loadedTiles = new Set<number>()
 | 
						|
 | 
						|
    constructor(
 | 
						|
        layer: FilteredLayer,
 | 
						|
        zoomlevel: number,
 | 
						|
        constructTile: (zxy: [number, number, number]) => FeatureSourceForLayer & Tiled,
 | 
						|
        state: {
 | 
						|
            currentBounds: UIEventSource<BBox>
 | 
						|
            locationControl?: UIEventSource<{ zoom?: number }>
 | 
						|
        }
 | 
						|
    ) {
 | 
						|
        const self = this
 | 
						|
 | 
						|
        this.loadedTiles = new Map<number, FeatureSourceForLayer & Tiled>()
 | 
						|
        const neededTiles = state.currentBounds
 | 
						|
            .map(
 | 
						|
                (bounds) => {
 | 
						|
                    if (bounds === undefined) {
 | 
						|
                        // We'll retry later
 | 
						|
                        return undefined
 | 
						|
                    }
 | 
						|
 | 
						|
                    if (!layer.isDisplayed.data && !layer.layerDef.forceLoad) {
 | 
						|
                        // No need to download! - the layer is disabled
 | 
						|
                        return undefined
 | 
						|
                    }
 | 
						|
 | 
						|
                    if (
 | 
						|
                        state.locationControl?.data?.zoom !== undefined &&
 | 
						|
                        state.locationControl.data.zoom < layer.layerDef.minzoom
 | 
						|
                    ) {
 | 
						|
                        // No need to download! - the layer is disabled
 | 
						|
                        return undefined
 | 
						|
                    }
 | 
						|
 | 
						|
                    const tileRange = Tiles.TileRangeBetween(
 | 
						|
                        zoomlevel,
 | 
						|
                        bounds.getNorth(),
 | 
						|
                        bounds.getEast(),
 | 
						|
                        bounds.getSouth(),
 | 
						|
                        bounds.getWest()
 | 
						|
                    )
 | 
						|
                    if (tileRange.total > 10000) {
 | 
						|
                        console.error(
 | 
						|
                            "Got a really big tilerange, bounds and location might be out of sync"
 | 
						|
                        )
 | 
						|
                        return undefined
 | 
						|
                    }
 | 
						|
 | 
						|
                    const needed = Tiles.MapRange(tileRange, (x, y) =>
 | 
						|
                        Tiles.tile_index(zoomlevel, x, y)
 | 
						|
                    ).filter((i) => !self._loadedTiles.has(i))
 | 
						|
                    if (needed.length === 0) {
 | 
						|
                        return undefined
 | 
						|
                    }
 | 
						|
                    return needed
 | 
						|
                },
 | 
						|
                [layer.isDisplayed, state.locationControl]
 | 
						|
            )
 | 
						|
            .stabilized(250)
 | 
						|
 | 
						|
        neededTiles.addCallbackAndRunD((neededIndexes) => {
 | 
						|
            console.log("Tiled geojson source ", layer.layerDef.id, " needs", neededIndexes)
 | 
						|
            if (neededIndexes === undefined) {
 | 
						|
                return
 | 
						|
            }
 | 
						|
            for (const neededIndex of neededIndexes) {
 | 
						|
                self._loadedTiles.add(neededIndex)
 | 
						|
                const src = constructTile(Tiles.tile_from_index(neededIndex))
 | 
						|
                if (src !== undefined) {
 | 
						|
                    self.loadedTiles.set(neededIndex, src)
 | 
						|
                }
 | 
						|
            }
 | 
						|
        })
 | 
						|
    }
 | 
						|
}
 |