Full code cleanup

This commit is contained in:
Pieter Vander Vennet 2021-11-07 16:34:51 +01:00
parent 8e6ee8c87f
commit bd21212eba
246 changed files with 19418 additions and 11729 deletions

View file

@ -39,7 +39,7 @@ export default class SaveTileToLocalStorageActor {
}
}
public static poison(layers: string[], lon: number, lat: number) {
public static poison(layers: string[], lon: number, lat: number) {
for (let z = 0; z < 25; z++) {
const {x, y} = Tiles.embedded_tile(lat, lon, z)

View file

@ -129,7 +129,7 @@ export default class FeaturePipeline {
// This will already contain the merged features for this tile. In other words, this will only be triggered once for every tile
const srcFiltered =
new FilteringFeatureSource(state, src.tileIndex,
new ChangeGeometryApplicator(src, state.changes)
new ChangeGeometryApplicator(src, state.changes)
)
handleFeatureSource(srcFiltered)
@ -147,7 +147,7 @@ export default class FeaturePipeline {
this.freshnesses.set(id, new TileFreshnessCalculator())
if(id === "type_node"){
if (id === "type_node") {
// Handles by the 'FullNodeDatabaseSource'
continue;
}
@ -226,15 +226,15 @@ export default class FeaturePipeline {
self.freshnesses.get(flayer.layerDef.id).addTileLoad(tileId, new Date())
})
})
if(state.layoutToUse.trackAllNodes){
const fullNodeDb = new FullNodeDatabaseSource(
state.filteredLayers.data.filter(l => l.layerDef.id === "type_node")[0],
tile => {
new RegisteringAllFromFeatureSourceActor(tile)
perLayerHierarchy.get(tile.layer.layerDef.id).registerTile(tile)
tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile))
})
if (state.layoutToUse.trackAllNodes) {
const fullNodeDb = new FullNodeDatabaseSource(
state.filteredLayers.data.filter(l => l.layerDef.id === "type_node")[0],
tile => {
new RegisteringAllFromFeatureSourceActor(tile)
perLayerHierarchy.get(tile.layer.layerDef.id).registerTile(tile)
tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile))
})
osmFeatureSource.rawDataHandlers.push((osmJson, tileId) => fullNodeDb.handleOsmJson(osmJson, tileId))
}
@ -299,6 +299,34 @@ export default class FeaturePipeline {
}
public GetAllFeaturesWithin(bbox: BBox): any[][] {
const self = this
const tiles = []
Array.from(this.perLayerHierarchy.keys())
.forEach(key => tiles.push(...self.GetFeaturesWithin(key, bbox)))
return tiles;
}
public GetFeaturesWithin(layerId: string, bbox: BBox): any[][] {
if (layerId === "*") {
return this.GetAllFeaturesWithin(bbox)
}
const requestedHierarchy = this.perLayerHierarchy.get(layerId)
if (requestedHierarchy === undefined) {
console.warn("Layer ", layerId, "is not defined. Try one of ", Array.from(this.perLayerHierarchy.keys()))
return undefined;
}
return TileHierarchyTools.getTiles(requestedHierarchy, bbox)
.filter(featureSource => featureSource.features?.data !== undefined)
.map(featureSource => featureSource.features.data.map(fs => fs.feature))
}
public GetTilesPerLayerWithin(bbox: BBox, handleTile: (tile: FeatureSourceForLayer & Tiled) => void) {
Array.from(this.perLayerHierarchy.values()).forEach(hierarchy => {
TileHierarchyTools.getTiles(hierarchy, bbox).forEach(handleTile)
})
}
private freshnessForVisibleLayers(z: number, x: number, y: number): Date {
let oldestDate = undefined;
for (const flayer of this.state.filteredLayers.data) {
@ -438,32 +466,4 @@ export default class FeaturePipeline {
}
public GetAllFeaturesWithin(bbox: BBox): any[][] {
const self = this
const tiles = []
Array.from(this.perLayerHierarchy.keys())
.forEach(key => tiles.push(...self.GetFeaturesWithin(key, bbox)))
return tiles;
}
public GetFeaturesWithin(layerId: string, bbox: BBox): any[][] {
if (layerId === "*") {
return this.GetAllFeaturesWithin(bbox)
}
const requestedHierarchy = this.perLayerHierarchy.get(layerId)
if (requestedHierarchy === undefined) {
console.warn("Layer ", layerId, "is not defined. Try one of ", Array.from(this.perLayerHierarchy.keys()))
return undefined;
}
return TileHierarchyTools.getTiles(requestedHierarchy, bbox)
.filter(featureSource => featureSource.features?.data !== undefined)
.map(featureSource => featureSource.features.data.map(fs => fs.feature))
}
public GetTilesPerLayerWithin(bbox: BBox, handleTile: (tile: FeatureSourceForLayer & Tiled) => void) {
Array.from(this.perLayerHierarchy.values()).forEach(hierarchy => {
TileHierarchyTools.getTiles(hierarchy, bbox).forEach(handleTile)
})
}
}

View file

@ -1,5 +1,4 @@
import {UIEventSource} from "../UIEventSource";
import {Utils} from "../../Utils";
import FilteredLayer from "../../Models/FilteredLayer";
import {BBox} from "../BBox";
@ -19,7 +18,7 @@ export interface Tiled {
/**
* A feature source which only contains features for the defined layer
*/
export interface FeatureSourceForLayer extends FeatureSource{
export interface FeatureSourceForLayer extends FeatureSource {
readonly layer: FilteredLayer
}

View file

@ -14,9 +14,9 @@ export default class PerLayerFeatureSourceSplitter {
constructor(layers: UIEventSource<FilteredLayer[]>,
handleLayerData: (source: FeatureSourceForLayer & Tiled) => void,
upstream: FeatureSource,
options?:{
tileIndex?: number,
handleLeftovers?: (featuresWithoutLayer: any[]) => void
options?: {
tileIndex?: number,
handleLeftovers?: (featuresWithoutLayer: any[]) => void
}) {
const knownLayers = new Map<string, FeatureSourceForLayer & Tiled>()
@ -35,6 +35,7 @@ export default class PerLayerFeatureSourceSplitter {
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)
@ -80,9 +81,9 @@ export default class PerLayerFeatureSourceSplitter {
featureSource.features.setData(features)
}
}
// AT last, the leftovers are handled
if(options?.handleLeftovers !== undefined && noLayerFound.length > 0){
if (options?.handleLeftovers !== undefined && noLayerFound.length > 0) {
options.handleLeftovers(noLayerFound)
}
}

View file

@ -11,9 +11,9 @@ import {ChangeDescription, ChangeDescriptionTools} from "../../Osm/Actions/Chang
export default class ChangeGeometryApplicator implements FeatureSourceForLayer {
public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]);
public readonly name: string;
public readonly layer: FilteredLayer
private readonly source: IndexedFeatureSource;
private readonly changes: Changes;
public readonly layer: FilteredLayer
constructor(source: (IndexedFeatureSource & FeatureSourceForLayer), changes: Changes) {
this.source = source;
@ -22,10 +22,10 @@ export default class ChangeGeometryApplicator implements FeatureSourceForLayer {
this.name = "ChangesApplied(" + source.name + ")"
this.features = new UIEventSource<{ feature: any; freshness: Date }[]>(undefined)
const self = this;
source.features.addCallbackAndRunD(_ => self.update())
changes.allChanges.addCallbackAndRunD(_ => self.update())
}
@ -52,9 +52,9 @@ export default class ChangeGeometryApplicator implements FeatureSourceForLayer {
const changesPerId = new Map<string, ChangeDescription[]>()
for (const ch of changesToApply) {
const key = ch.type + "/" + ch.id
if(changesPerId.has(key)){
if (changesPerId.has(key)) {
changesPerId.get(key).push(ch)
}else{
} else {
changesPerId.set(key, [ch])
}
}
@ -66,7 +66,7 @@ export default class ChangeGeometryApplicator implements FeatureSourceForLayer {
newFeatures.push(feature)
continue;
}
// Allright! We have a feature to rewrite!
const copy = {
...feature

View file

@ -5,7 +5,6 @@
import {UIEventSource} from "../../UIEventSource";
import FeatureSource, {FeatureSourceForLayer, IndexedFeatureSource, Tiled} from "../FeatureSource";
import FilteredLayer from "../../../Models/FilteredLayer";
import {Utils} from "../../../Utils";
import {Tiles} from "../../../Models/TileRange";
import {BBox} from "../../BBox";
@ -14,17 +13,17 @@ export default class FeatureSourceMerger implements FeatureSourceForLayer, Tiled
public features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]);
public readonly name;
public readonly layer: FilteredLayer
private readonly _sources: UIEventSource<FeatureSource[]>;
public readonly tileIndex: number;
public readonly bbox: BBox;
public readonly containedIds: UIEventSource<Set<string>> = new UIEventSource<Set<string>>(new Set())
private readonly _sources: UIEventSource<FeatureSource[]>;
constructor(layer: FilteredLayer, tileIndex: number, bbox: BBox, sources: UIEventSource<FeatureSource[]>) {
this.tileIndex = tileIndex;
this.bbox = bbox;
this._sources = sources;
this.layer = layer;
this.name = "FeatureSourceMerger("+layer.layerDef.id+", "+Tiles.tile_from_index(tileIndex).join(",")+")"
this.name = "FeatureSourceMerger(" + layer.layerDef.id + ", " + Tiles.tile_from_index(tileIndex).join(",") + ")"
const self = this;
const handledSources = new Set<FeatureSource>();

View file

@ -18,6 +18,8 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
locationControl: UIEventSource<{ zoom: number }>; selectedElement: UIEventSource<any>,
allElements: ElementStorage
};
private readonly _alreadyRegistered = new Set<UIEventSource<any>>();
private readonly _is_dirty = new UIEventSource(false)
constructor(
state: {
@ -55,24 +57,6 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
this.update();
}
private readonly _alreadyRegistered = new Set<UIEventSource<any>>();
private readonly _is_dirty = new UIEventSource(false)
private registerCallback(feature: any, layer: LayerConfig) {
const src = this.state.allElements.addOrGetElement(feature)
if (this._alreadyRegistered.has(src)) {
return
}
this._alreadyRegistered.add(src)
if (layer.isShown !== undefined) {
const self = this;
src.map(tags => layer.isShown?.GetRenderValue(tags, "yes").txt).addCallbackAndRunD(isShown => {
self._is_dirty.setData(true)
})
}
}
public update() {
const self = this;
const layer = this.upstream.layer;
@ -116,4 +100,19 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
this._is_dirty.setData(false)
}
private registerCallback(feature: any, layer: LayerConfig) {
const src = this.state.allElements.addOrGetElement(feature)
if (this._alreadyRegistered.has(src)) {
return
}
this._alreadyRegistered.add(src)
if (layer.isShown !== undefined) {
const self = this;
src.map(tags => layer.isShown?.GetRenderValue(tags, "yes").txt).addCallbackAndRunD(isShown => {
self._is_dirty.setData(true)
})
}
}
}

View file

@ -15,12 +15,10 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled {
public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>;
public readonly name;
public readonly isOsmCache: boolean
private readonly seenids: Set<string> = new Set<string>()
public readonly layer: FilteredLayer;
public readonly tileIndex
public readonly bbox;
private readonly seenids: Set<string> = new Set<string>()
/**
* Only used if the actual source is a tiled geojson.
* A big feature might be contained in multiple tiles.
@ -32,7 +30,7 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled {
public constructor(flayer: FilteredLayer,
zxy?: [number, number, number],
options?: {
featureIdBlacklist?: UIEventSource<Set<string>>
featureIdBlacklist?: UIEventSource<Set<string>>
}) {
if (flayer.layerDef.source.geojsonZoomLevel !== undefined && zxy === undefined) {
@ -45,18 +43,18 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled {
if (zxy !== undefined) {
const [z, x, y] = zxy;
let tile_bbox = BBox.fromTile(z, x, y)
let bounds : { minLat: number, maxLat: number, minLon: number, maxLon: number } = tile_bbox
if(this.layer.layerDef.source.mercatorCrs){
let bounds: { minLat: number, maxLat: number, minLon: number, maxLon: number } = tile_bbox
if (this.layer.layerDef.source.mercatorCrs) {
bounds = tile_bbox.toMercator()
}
url = url
.replace('{z}', "" + z)
.replace('{x}', "" + x)
.replace('{y}', "" + y)
.replace('{y_min}',""+bounds.minLat)
.replace('{y_max}',""+bounds.maxLat)
.replace('{x_min}',""+bounds.minLon)
.replace('{x_max}',""+bounds.maxLon)
.replace('{y_min}', "" + bounds.minLat)
.replace('{y_max}', "" + bounds.maxLat)
.replace('{x_min}', "" + bounds.minLon)
.replace('{x_max}', "" + bounds.maxLon)
this.tileIndex = Tiles.tile_index(z, x, y)
this.bbox = BBox.fromTile(z, x, y)
@ -78,11 +76,11 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled {
const self = this;
Utils.downloadJson(url)
.then(json => {
if(json.features === undefined || json.features === null){
if (json.features === undefined || json.features === null) {
return;
}
if(self.layer.layerDef.source.mercatorCrs){
if (self.layer.layerDef.source.mercatorCrs) {
json = GeoOperations.GeoJsonToWGS84(json)
}
@ -109,8 +107,8 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled {
continue;
}
self.seenids.add(props.id)
if(self.featureIdBlacklist?.data?.has(props.id)){
if (self.featureIdBlacklist?.data?.has(props.id)) {
continue;
}
@ -122,7 +120,7 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled {
newFeatures.push({feature: feature, freshness: freshness})
}
if ( newFeatures.length == 0) {
if (newFeatures.length == 0) {
return;
}

View file

@ -7,7 +7,7 @@ import State from "../../../State";
export class NewGeometryFromChangesFeatureSource implements FeatureSource {
// This class name truly puts the 'Java' into 'Javascript'
/**
* A feature source containing exclusively new elements
*/
@ -53,10 +53,10 @@ export class NewGeometryFromChangesFeatureSource implements FeatureSource {
for (const kv of change.tags) {
tags[kv.k] = kv.v
}
tags["id"] = change.type+"/"+change.id
tags["id"] = change.type + "/" + change.id
tags["_backend"] = State.state.osmConnection._oauth_config.url
switch (change.type) {
case "node":
const n = new OsmNode(change.id)
@ -85,7 +85,7 @@ export class NewGeometryFromChangesFeatureSource implements FeatureSource {
}
}
self.features.ping()
})
}

View file

@ -6,19 +6,19 @@ import FeatureSource, {Tiled} from "../FeatureSource";
import {UIEventSource} from "../../UIEventSource";
import {BBox} from "../../BBox";
export default class RememberingSource implements FeatureSource , Tiled{
export default class RememberingSource implements FeatureSource, Tiled {
public readonly features: UIEventSource<{ feature: any, freshness: Date }[]>;
public readonly name;
public readonly tileIndex : number
public readonly bbox : BBox
public readonly tileIndex: number
public readonly bbox: BBox
constructor(source: FeatureSource & Tiled) {
const self = this;
this.name = "RememberingSource of " + source.name;
this.tileIndex= source.tileIndex
this.tileIndex = source.tileIndex
this.bbox = source.bbox;
const empty = [];
this.features = source.features.map(features => {
const oldFeatures = self.features?.data ?? empty;

View file

@ -32,7 +32,7 @@ export default class RenderingMultiPlexerFeatureSource {
const withIndex: (any & { pointRenderingIndex: number | undefined, lineRenderingIndex: number | undefined })[] = [];
function addAsPoint(feat, rendering, coordinate) {
function addAsPoint(feat, rendering, coordinate) {
const patched = {
...feat,
pointRenderingIndex: rendering.index

View file

@ -13,35 +13,35 @@ export default class TileFreshnessCalculator {
* @param tileId
* @param freshness
*/
public addTileLoad(tileId: number, freshness: Date){
public addTileLoad(tileId: number, freshness: Date) {
const existingFreshness = this.freshnessFor(...Tiles.tile_from_index(tileId))
if(existingFreshness >= freshness){
if (existingFreshness >= freshness) {
return;
}
this.freshnesses.set(tileId, freshness)
// Do we have freshness for the neighbouring tiles? If so, we can mark the tile above as loaded too!
let [z, x, y] = Tiles.tile_from_index(tileId)
if(z === 0){
if (z === 0) {
return;
}
x = x - (x % 2) // Make the tiles always even
y = y - (y % 2)
const ul = this.freshnessFor(z, x, y)?.getTime()
if(ul === undefined){
if (ul === undefined) {
return
}
const ur = this.freshnessFor(z, x + 1, y)?.getTime()
if(ur === undefined){
if (ur === undefined) {
return
}
const ll = this.freshnessFor(z, x, y + 1)?.getTime()
if(ll === undefined){
if (ll === undefined) {
return
}
const lr = this.freshnessFor(z, x + 1, y + 1)?.getTime()
if(lr === undefined){
if (lr === undefined) {
return
}
@ -50,22 +50,22 @@ export default class TileFreshnessCalculator {
date.setTime(leastFresh)
this.addTileLoad(
Tiles.tile_index(z - 1, Math.floor(x / 2), Math.floor(y / 2)),
date
date
)
}
public freshnessFor(z: number, x: number, y:number): Date {
if(z < 0){
public freshnessFor(z: number, x: number, y: number): Date {
if (z < 0) {
return undefined
}
const tileId = Tiles.tile_index(z, x, y)
if(this.freshnesses.has(tileId)) {
if (this.freshnesses.has(tileId)) {
return this.freshnesses.get(tileId)
}
// recurse up
return this.freshnessFor(z - 1, Math.floor(x /2), Math.floor(y / 2))
return this.freshnessFor(z - 1, Math.floor(x / 2), Math.floor(y / 2))
}
}

View file

@ -9,9 +9,8 @@ import {Tiles} from "../../../Models/TileRange";
* A tiled source which dynamically loads the required tiles at a fixed zoom level
*/
export default class DynamicTileSource implements TileHierarchy<FeatureSourceForLayer & Tiled> {
private readonly _loadedTiles = new Set<number>();
public readonly loadedTiles: Map<number, FeatureSourceForLayer & Tiled>;
private readonly _loadedTiles = new Set<number>();
constructor(
layer: FilteredLayer,
@ -24,7 +23,7 @@ export default class DynamicTileSource implements TileHierarchy<FeatureSourceFor
) {
const self = this;
this.loadedTiles = new Map<number,FeatureSourceForLayer & Tiled>()
this.loadedTiles = new Map<number, FeatureSourceForLayer & Tiled>()
const neededTiles = state.locationControl.map(
location => {
if (!layer.isDisplayed.data) {
@ -54,14 +53,14 @@ export default class DynamicTileSource implements TileHierarchy<FeatureSourceFor
, [layer.isDisplayed, state.leafletMap]).stabilized(250);
neededTiles.addCallbackAndRunD(neededIndexes => {
console.log("Tiled geojson source ",layer.layerDef.id," needs", 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){
if (src !== undefined) {
self.loadedTiles.set(neededIndex, src)
}
}

View file

@ -19,7 +19,7 @@ export default class FullNodeDatabaseSource implements TileHierarchy<FeatureSour
throw "Layer is undefined"
}
}
public handleOsmJson(osmJson: any, tileId: number) {
const allObjects = OsmObject.ParseObjects(osmJson.elements)

View file

@ -13,9 +13,10 @@ import {Or} from "../../Tags/Or";
import {TagsFilter} from "../../Tags/TagsFilter";
export default class OsmFeatureSource {
private readonly _backend: string;
public readonly isRunning: UIEventSource<boolean> = new UIEventSource<boolean>(false)
public readonly downloadedTiles = new Set<number>()
public rawDataHandlers: ((osmJson: any, tileId: number) => void)[] = []
private readonly _backend: string;
private readonly filteredLayers: UIEventSource<FilteredLayer[]>;
private readonly handleTile: (fs: (FeatureSourceForLayer & Tiled)) => void;
private isActive: UIEventSource<boolean>;
@ -28,10 +29,7 @@ export default class OsmFeatureSource {
},
markTileVisited?: (tileId: number) => void
};
public readonly downloadedTiles = new Set<number>()
private readonly allowedTags: TagsFilter;
public rawDataHandlers: ((osmJson: any, tileId: number) => void)[] = []
constructor(options: {
handleTile: (tile: FeatureSourceForLayer & Tiled) => void;
@ -54,13 +52,13 @@ export default class OsmFeatureSource {
if (options.isActive?.data === false) {
return;
}
neededTiles = neededTiles.filter(tile => !self.downloadedTiles.has(tile))
if(neededTiles.length == 0){
if (neededTiles.length == 0) {
return;
}
self.isRunning.setData(true)
try {
@ -73,7 +71,7 @@ export default class OsmFeatureSource {
}
} catch (e) {
console.error(e)
}finally {
} finally {
console.log("Done")
self.isRunning.setData(false)
}
@ -111,7 +109,7 @@ export default class OsmFeatureSource {
geojson.features = geojson.features.filter(feature => this.allowedTags.matchesProperties(feature.properties))
geojson.features.forEach(f => f.properties["_backend"] = this._backend)
const index = Tiles.tile_index(z, x, y);
new PerLayerFeatureSourceSplitter(this.filteredLayers,
this.handleTile,

View file

@ -11,17 +11,14 @@ Currently, they are:
When the data enters from Overpass or from the OSM-API, they are first distributed per layer:
OVERPASS | ---PerLayerFeatureSource---> FeatureSourceForLayer[]
OSM |
OSM |
The GeoJSon files (not tiled) are then added to this list
A single FeatureSourcePerLayer is then further handled by splitting it into a tile hierarchy.
In order to keep thins snappy, they are distributed over a tiled database per layer.
## Notes
`cached-featuresbookcases` is the old key used `cahced-features{themeid}` and should be cleaned up

View file

@ -8,9 +8,8 @@ import {BBox} from "../../BBox";
export class TileHierarchyMerger implements TileHierarchy<FeatureSourceForLayer & Tiled> {
public readonly loadedTiles: Map<number, FeatureSourceForLayer & Tiled> = new Map<number, FeatureSourceForLayer & Tiled>();
private readonly sources: Map<number, UIEventSource<FeatureSource[]>> = new Map<number, UIEventSource<FeatureSource[]>>();
public readonly layer: FilteredLayer;
private readonly sources: Map<number, UIEventSource<FeatureSource[]>> = new Map<number, UIEventSource<FeatureSource[]>>();
private _handleTile: (src: FeatureSourceForLayer & IndexedFeatureSource, index: number) => void;
constructor(layer: FilteredLayer, handleTile: (src: FeatureSourceForLayer & IndexedFeatureSource & Tiled, index: number) => void) {
@ -24,7 +23,7 @@ export class TileHierarchyMerger implements TileHierarchy<FeatureSourceForLayer
* @param src
* @param index
*/
public registerTile(src: FeatureSource & Tiled) {
public registerTile(src: FeatureSource & Tiled) {
const index = src.tileIndex
if (this.sources.has(index)) {

View file

@ -1,6 +1,5 @@
import FeatureSource, {FeatureSourceForLayer, IndexedFeatureSource, Tiled} from "../FeatureSource";
import {UIEventSource} from "../../UIEventSource";
import {Utils} from "../../../Utils";
import FilteredLayer from "../../../Models/FilteredLayer";
import TileHierarchy from "./TileHierarchy";
import {Tiles} from "../../../Models/TileRange";
@ -28,13 +27,13 @@ export default class TiledFeatureSource implements Tiled, IndexedFeatureSource,
public readonly containedIds: UIEventSource<Set<string>>
public readonly bbox: BBox;
public readonly tileIndex: number;
private upper_left: TiledFeatureSource
private upper_right: TiledFeatureSource
private lower_left: TiledFeatureSource
private lower_right: TiledFeatureSource
private readonly maxzoom: number;
private readonly options: TiledFeatureSourceOptions
public readonly tileIndex: number;
private constructor(z: number, x: number, y: number, parent: TiledFeatureSource, options?: TiledFeatureSourceOptions) {
this.z = z;
@ -92,25 +91,25 @@ export default class TiledFeatureSource implements Tiled, IndexedFeatureSource,
return root;
}
private isSplitNeeded(featureCount: number){
if(this.upper_left !== undefined){
private isSplitNeeded(featureCount: number) {
if (this.upper_left !== undefined) {
// This tile has been split previously, so we keep on splitting
return true;
}
if(this.z >= this.maxzoom){
if (this.z >= this.maxzoom) {
// We are not allowed to split any further
return false
}
if(this.options.minZoomLevel !== undefined && this.z < this.options.minZoomLevel){
if (this.options.minZoomLevel !== undefined && this.z < this.options.minZoomLevel) {
// We must have at least this zoom level before we are allowed to start splitting
return true
}
// To much features - we split
return featureCount > this.maxFeatureCount
}
/***
* Adds the list of features to this hierarchy.
* If there are too much features, the list will be broken down and distributed over the subtiles (only retaining features that don't fit a subtile on this level)
@ -121,7 +120,7 @@ export default class TiledFeatureSource implements Tiled, IndexedFeatureSource,
if (features === undefined || features.length === 0) {
return;
}
if (!this.isSplitNeeded(features.length)) {
this.features.setData(features)
return;
@ -155,7 +154,7 @@ export default class TiledFeatureSource implements Tiled, IndexedFeatureSource,
} else {
overlapsboundary.push(feature)
}
}else if (this.options.minZoomLevel === undefined) {
} else if (this.options.minZoomLevel === undefined) {
if (bbox.isContainedIn(this.upper_left.bbox)) {
ulf.push(feature)
} else if (bbox.isContainedIn(this.upper_right.bbox)) {

View file

@ -13,44 +13,6 @@ export default class TiledFromLocalStorageSource implements TileHierarchy<Featur
private readonly handleFeatureSource: (src: FeatureSourceForLayer & Tiled, index: number) => void;
private readonly undefinedTiles: Set<number>;
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
}
static cleanCacheForLayer(layer: LayerConfig) {
const now = new Date()
const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layer.id + "-"
console.log("Cleaning tiles of ", prefix, "with max age",layer.maxAgeOfCache)
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 timeDiff = (now.getTime() - time) / 1000
if(timeDiff >= layer.maxAgeOfCache){
const k = prefix+index;
localStorage.removeItem(k)
localStorage.removeItem(k+"-format")
localStorage.removeItem(k+"-time")
}
}
}
constructor(layer: FilteredLayer,
handleFeatureSource: (src: FeatureSourceForLayer & Tiled, index: number) => void,
state: {
@ -110,6 +72,43 @@ export default class TiledFromLocalStorageSource implements TileHierarchy<Featur
}
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
}
static cleanCacheForLayer(layer: LayerConfig) {
const now = new Date()
const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layer.id + "-"
console.log("Cleaning tiles of ", prefix, "with max age", layer.maxAgeOfCache)
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 timeDiff = (now.getTime() - time) / 1000
if (timeDiff >= layer.maxAgeOfCache) {
const k = prefix + index;
localStorage.removeItem(k)
localStorage.removeItem(k + "-format")
localStorage.removeItem(k + "-time")
}
}
}
private loadTile(neededIndex: number) {
try {
const key = SaveTileToLocalStorageActor.storageKey + "-" + this.layer.layerDef.id + "-" + neededIndex