forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			132 lines
		
	
	
		
			No EOL
		
	
	
		
			5.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			132 lines
		
	
	
		
			No EOL
		
	
	
		
			5.6 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 SaveTileToLocalStorageActor from "../Actors/SaveTileToLocalStorageActor";
 | 
						|
import {Tiles} from "../../../Models/TileRange";
 | 
						|
import {BBox} from "../../BBox";
 | 
						|
 | 
						|
export default class TiledFromLocalStorageSource implements TileHierarchy<FeatureSourceForLayer & Tiled> {
 | 
						|
    public loadedTiles: Map<number, FeatureSourceForLayer & Tiled> = new Map<number, FeatureSourceForLayer & Tiled>();
 | 
						|
 | 
						|
    public static GetFreshnesses(layerId: string): Map<number, Date> {
 | 
						|
        const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layerId + "-"
 | 
						|
        const freshnesses = new Map<number, Date>()
 | 
						|
        for (const key of Object.keys(localStorage)) {
 | 
						|
            if(!(key.startsWith(prefix) && key.endsWith("-time"))){
 | 
						|
                continue
 | 
						|
            }
 | 
						|
            const index = Number(key.substring(prefix.length, key.length - "-time".length))
 | 
						|
            const time = Number(localStorage.getItem(key))
 | 
						|
            const freshness = new Date()
 | 
						|
            freshness.setTime(time)
 | 
						|
            freshnesses.set(index, freshness)
 | 
						|
        }
 | 
						|
        return freshnesses
 | 
						|
    }
 | 
						|
 | 
						|
    constructor(layer: FilteredLayer,
 | 
						|
                handleFeatureSource: (src: FeatureSourceForLayer & Tiled, index: number) => void,
 | 
						|
                state: {
 | 
						|
                    locationControl: UIEventSource<Loc>
 | 
						|
                    leafletMap: any
 | 
						|
                }) {
 | 
						|
 | 
						|
        const undefinedTiles = new Set<number>()
 | 
						|
        const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layer.layerDef.id + "-"
 | 
						|
        // @ts-ignore
 | 
						|
        const indexes: number[] = Object.keys(localStorage)
 | 
						|
            .filter(key => {
 | 
						|
                return key.startsWith(prefix) && !key.endsWith("-time") && !key.endsWith("-format");
 | 
						|
            })
 | 
						|
            .map(key => {
 | 
						|
                return Number(key.substring(prefix.length));
 | 
						|
            })
 | 
						|
            .filter(i => !isNaN(i))
 | 
						|
 | 
						|
        console.debug("Layer", layer.layerDef.id, "has following tiles in available in localstorage", indexes.map(i => Tiles.tile_from_index(i).join("/")).join(", "))
 | 
						|
        for (const index of indexes) {
 | 
						|
 | 
						|
            const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layer.layerDef.id + "-" + index;
 | 
						|
            const version = localStorage.getItem(prefix + "-format")
 | 
						|
            if (version === undefined || version !== SaveTileToLocalStorageActor.formatVersion) {
 | 
						|
                // Invalid version! Remove this tile from local storage
 | 
						|
                localStorage.removeItem(prefix)
 | 
						|
                localStorage.removeItem(prefix+"-time")
 | 
						|
                localStorage.removeItem(prefix+"-format")
 | 
						|
                undefinedTiles.add(index)
 | 
						|
                console.log("Dropped old format tile", prefix)
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        const zLevels = indexes.map(i => i % 100)
 | 
						|
        const indexesSet = new Set(indexes)
 | 
						|
        const maxZoom = Math.max(...zLevels)
 | 
						|
        const minZoom = Math.min(...zLevels)
 | 
						|
        const self = this;
 | 
						|
 | 
						|
        const neededTiles = state.locationControl.map(
 | 
						|
            location => {
 | 
						|
                if (!layer.isDisplayed.data) {
 | 
						|
                    // 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.leafletMap.data?.getBounds()
 | 
						|
                if (bounds === undefined) {
 | 
						|
                    // We'll retry later
 | 
						|
                    return undefined
 | 
						|
                }
 | 
						|
 | 
						|
                const needed = []
 | 
						|
                for (let z = minZoom; z <= maxZoom; z++) {
 | 
						|
 | 
						|
                    const tileRange = Tiles.TileRangeBetween(z, bounds.getNorth(), bounds.getEast(), bounds.getSouth(), bounds.getWest())
 | 
						|
                    const neededZ = Tiles.MapRange(tileRange, (x, y) => Tiles.tile_index(z, x, y))
 | 
						|
                        .filter(i => !self.loadedTiles.has(i) && !undefinedTiles.has(i) && indexesSet.has(i))
 | 
						|
                    needed.push(...neededZ)
 | 
						|
                }
 | 
						|
 | 
						|
                if (needed.length === 0) {
 | 
						|
                    return undefined
 | 
						|
                }
 | 
						|
                return needed
 | 
						|
            }
 | 
						|
            , [layer.isDisplayed, state.leafletMap]).stabilized(50);
 | 
						|
 | 
						|
        neededTiles.addCallbackAndRunD(neededIndexes => {
 | 
						|
            for (const neededIndex of neededIndexes) {
 | 
						|
                // We load the features from localStorage
 | 
						|
                try {
 | 
						|
                    const key = SaveTileToLocalStorageActor.storageKey + "-" + layer.layerDef.id + "-" + neededIndex
 | 
						|
                    const data = localStorage.getItem(key)
 | 
						|
                    const features = JSON.parse(data)
 | 
						|
                    const src = {
 | 
						|
                        layer: layer,
 | 
						|
                        features: new UIEventSource<{ feature: any; freshness: Date }[]>(features),
 | 
						|
                        name: "FromLocalStorage(" + key + ")",
 | 
						|
                        tileIndex: neededIndex,
 | 
						|
                        bbox: BBox.fromTileIndex(neededIndex)
 | 
						|
                    }
 | 
						|
                    handleFeatureSource(src, neededIndex)
 | 
						|
                    self.loadedTiles.set(neededIndex, src)
 | 
						|
                } catch (e) {
 | 
						|
                    console.error("Could not load data tile from local storage due to", e)
 | 
						|
                    undefinedTiles.add(neededIndex)
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
 | 
						|
        })
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
} |