forked from MapComplete/MapComplete
Full code cleanup
This commit is contained in:
parent
8e6ee8c87f
commit
bd21212eba
246 changed files with 19418 additions and 11729 deletions
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
|
@ -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)) {
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue