2023-03-26 05:58:28 +02:00
|
|
|
import { Store, Stores } from "../../UIEventSource"
|
2022-09-08 21:40:48 +02:00
|
|
|
import { Tiles } from "../../../Models/TileRange"
|
|
|
|
import { BBox } from "../../BBox"
|
2024-01-26 18:18:07 +01:00
|
|
|
import { FeatureSource, FeatureSourceForTile } from "../FeatureSource"
|
2023-03-26 05:58:28 +02:00
|
|
|
import FeatureSourceMerger from "../Sources/FeatureSourceMerger"
|
2024-01-26 18:18:07 +01:00
|
|
|
import { Feature } from "geojson"
|
|
|
|
import { Utils } from "../../../Utils"
|
|
|
|
import { GeoOperations } from "../../GeoOperations"
|
|
|
|
|
2021-09-20 17:14:55 +02:00
|
|
|
|
2021-09-21 02:10:42 +02:00
|
|
|
/***
|
2023-03-28 05:13:48 +02:00
|
|
|
* A tiled source which dynamically loads the required tiles at a fixed zoom level.
|
2024-01-04 23:42:47 +01:00
|
|
|
* A single featureSource will be initialized for every tile in view; which will later be merged into this featureSource
|
2021-09-21 02:10:42 +02:00
|
|
|
*/
|
2024-01-26 18:18:07 +01:00
|
|
|
export default class DynamicTileSource<Src extends FeatureSource = FeatureSource> extends FeatureSourceMerger<Src> {
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @param zoomlevel If {z} is specified in the source, the 'zoomlevel' will be used as zoomlevel to download from
|
|
|
|
* @param minzoom Only activate this feature source if zoomed in further then this
|
|
|
|
* @param constructSource
|
|
|
|
* @param mapProperties
|
|
|
|
* @param options
|
|
|
|
*/
|
2021-09-20 17:14:55 +02:00
|
|
|
constructor(
|
2024-01-22 01:42:05 +01:00
|
|
|
zoomlevel: Store<number>,
|
2023-11-15 03:59:02 +01:00
|
|
|
minzoom: number,
|
2024-01-26 18:18:07 +01:00
|
|
|
constructSource: (tileIndex: number) => Src,
|
2023-03-26 05:58:28 +02:00
|
|
|
mapProperties: {
|
|
|
|
bounds: Store<BBox>
|
|
|
|
zoom: Store<number>
|
|
|
|
},
|
|
|
|
options?: {
|
|
|
|
isActive?: Store<boolean>
|
2024-01-26 18:18:07 +01:00
|
|
|
},
|
2021-09-20 17:14:55 +02:00
|
|
|
) {
|
2023-03-26 05:58:28 +02:00
|
|
|
super()
|
|
|
|
const loadedTiles = new Set<number>()
|
|
|
|
const neededTiles: Store<number[]> = Stores.ListStabilized(
|
|
|
|
mapProperties.bounds
|
|
|
|
.mapD(
|
|
|
|
(bounds) => {
|
2023-11-15 03:59:02 +01:00
|
|
|
if (options?.isActive && !options?.isActive.data) {
|
|
|
|
return undefined
|
|
|
|
}
|
2023-11-15 04:13:02 +01:00
|
|
|
|
2023-11-15 03:59:02 +01:00
|
|
|
if (mapProperties.zoom.data < minzoom) {
|
|
|
|
return undefined
|
|
|
|
}
|
2024-01-26 18:18:07 +01:00
|
|
|
const z = Math.floor(zoomlevel.data)
|
2023-03-26 05:58:28 +02:00
|
|
|
const tileRange = Tiles.TileRangeBetween(
|
2024-01-22 01:42:05 +01:00
|
|
|
z,
|
2023-03-26 05:58:28 +02:00
|
|
|
bounds.getNorth(),
|
|
|
|
bounds.getEast(),
|
|
|
|
bounds.getSouth(),
|
2024-01-26 18:18:07 +01:00
|
|
|
bounds.getWest(),
|
2022-09-08 21:40:48 +02:00
|
|
|
)
|
2023-11-15 04:13:02 +01:00
|
|
|
if (tileRange.total > 500) {
|
|
|
|
console.warn(
|
2024-01-26 18:18:07 +01:00
|
|
|
"Got a really big tilerange, bounds and location might be out of sync",
|
2023-03-26 05:58:28 +02:00
|
|
|
)
|
|
|
|
return undefined
|
|
|
|
}
|
2021-09-21 02:10:42 +02:00
|
|
|
|
2023-03-26 05:58:28 +02:00
|
|
|
const needed = Tiles.MapRange(tileRange, (x, y) =>
|
2024-01-26 18:18:07 +01:00
|
|
|
Tiles.tile_index(z, x, y),
|
2023-03-26 05:58:28 +02:00
|
|
|
).filter((i) => !loadedTiles.has(i))
|
|
|
|
if (needed.length === 0) {
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
return needed
|
|
|
|
},
|
2024-01-26 18:18:07 +01:00
|
|
|
[options?.isActive, mapProperties.zoom],
|
2023-03-26 05:58:28 +02:00
|
|
|
)
|
2024-01-26 18:18:07 +01:00
|
|
|
.stabilized(250),
|
2023-03-26 05:58:28 +02:00
|
|
|
)
|
2022-09-08 21:40:48 +02:00
|
|
|
|
|
|
|
neededTiles.addCallbackAndRunD((neededIndexes) => {
|
2021-09-20 17:14:55 +02:00
|
|
|
for (const neededIndex of neededIndexes) {
|
2023-03-26 05:58:28 +02:00
|
|
|
loadedTiles.add(neededIndex)
|
|
|
|
super.addSource(constructSource(neededIndex))
|
2021-09-20 17:14:55 +02:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2021-09-21 02:10:42 +02:00
|
|
|
}
|
2024-01-26 18:18:07 +01:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The PolygonSourceMerger receives various small pieces of bigger polygons and stitches them together.
|
|
|
|
* This is used to reconstruct polygons of vector tiles
|
|
|
|
*/
|
|
|
|
export class PolygonSourceMerger extends DynamicTileSource<FeatureSourceForTile> {
|
|
|
|
constructor(
|
|
|
|
zoomlevel: Store<number>,
|
|
|
|
minzoom: number,
|
|
|
|
constructSource: (tileIndex: number) => FeatureSourceForTile,
|
|
|
|
mapProperties: {
|
|
|
|
bounds: Store<BBox>
|
|
|
|
zoom: Store<number>
|
|
|
|
},
|
|
|
|
options?: {
|
|
|
|
isActive?: Store<boolean>
|
|
|
|
},
|
|
|
|
) {
|
|
|
|
super(zoomlevel, minzoom, constructSource, mapProperties, options)
|
|
|
|
}
|
|
|
|
|
|
|
|
protected addDataFromSources(sources: FeatureSourceForTile[]) {
|
|
|
|
sources = Utils.NoNull(sources)
|
|
|
|
const all: Map<string, Feature> = new Map()
|
|
|
|
const zooms: Map<string, number> = new Map()
|
|
|
|
|
|
|
|
for (const source of sources) {
|
|
|
|
let z = source.z
|
|
|
|
for (const f of source.features.data) {
|
|
|
|
const id = f.properties.id
|
|
|
|
if(id.endsWith("146616907")){
|
|
|
|
console.log("Horeca totaal")
|
|
|
|
}
|
|
|
|
if (!all.has(id)) {
|
|
|
|
// No other parts of this polygon have been seen before, simply add it
|
|
|
|
all.set(id, f)
|
|
|
|
zooms.set(id, z)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// A part of this object has been seen before, eventually from a different zoom level
|
|
|
|
const oldV = all.get(id)
|
|
|
|
const oldZ = zooms.get(id)
|
|
|
|
if (oldZ > z) {
|
|
|
|
// The store contains more detailed information, so we ignore this part which has a lower accuraccy
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if (oldZ < z) {
|
|
|
|
// The old value has worse accuracy then what we receive now, we throw it away
|
|
|
|
all.set(id, f)
|
|
|
|
zooms.set(id, z)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
const merged = GeoOperations.union(f, oldV)
|
|
|
|
merged.properties = oldV.properties
|
|
|
|
all.set(id, merged)
|
|
|
|
zooms.set(id, z)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const newList = Array.from(all.values())
|
|
|
|
this.features.setData(newList)
|
|
|
|
this._featuresById.setData(all)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|