forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			79 lines
		
	
	
	
		
			3 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			79 lines
		
	
	
	
		
			3 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import FilteredLayer from "../../../Models/FilteredLayer";
 | |
| import {FeatureSourceForLayer, Tiled} from "../FeatureSource";
 | |
| import {UIEventSource} from "../../UIEventSource";
 | |
| import Loc from "../../../Models/Loc";
 | |
| 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<Loc>
 | |
|         }
 | |
|     ) {
 | |
|         const self = this;
 | |
| 
 | |
|         this.loadedTiles = new Map<number, FeatureSourceForLayer & Tiled>()
 | |
|         const neededTiles = state.locationControl.map(
 | |
|             location => {
 | |
|                 if (!layer.isDisplayed.data && !layer.layerDef.forceLoad) {
 | |
|                     // No need to download! - the layer is disabled
 | |
|                     return undefined;
 | |
|                 }
 | |
| 
 | |
|                 if (location.zoom < layer.layerDef.minzoom) {
 | |
|                     // No need to download! - the layer is disabled
 | |
|                     return undefined;
 | |
|                 }
 | |
| 
 | |
|                 // Yup, this is cheating to just get the bounds here
 | |
|                 const bounds = state.currentBounds.data
 | |
|                 if (bounds === undefined) {
 | |
|                     // We'll retry later
 | |
|                     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.currentBounds]).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)
 | |
|                 }
 | |
|             }
 | |
|         })
 | |
| 
 | |
| 
 | |
|     }
 | |
| 
 | |
| 
 | |
| }
 | |
| 
 |