137 lines
5.2 KiB
TypeScript
137 lines
5.2 KiB
TypeScript
import GeoJsonSource from "./GeoJsonSource"
|
|
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
|
|
import FeatureSource from "../FeatureSource"
|
|
import { Or } from "../../Tags/Or"
|
|
import FeatureSwitchState from "../../State/FeatureSwitchState"
|
|
import OverpassFeatureSource from "./OverpassFeatureSource"
|
|
import { Store } from "../../UIEventSource"
|
|
import OsmFeatureSource from "./OsmFeatureSource"
|
|
import FeatureSourceMerger from "./FeatureSourceMerger"
|
|
import DynamicGeoJsonTileSource from "../TiledFeatureSource/DynamicGeoJsonTileSource"
|
|
import { BBox } from "../../BBox"
|
|
import LocalStorageFeatureSource from "../TiledFeatureSource/LocalStorageFeatureSource"
|
|
|
|
/**
|
|
* This source will fetch the needed data from various sources for the given layout.
|
|
*
|
|
* Note that special layers (with `source=null` will be ignored)
|
|
*/
|
|
export default class LayoutSource extends FeatureSourceMerger {
|
|
constructor(
|
|
layers: LayerConfig[],
|
|
featureSwitches: FeatureSwitchState,
|
|
newAndChangedElements: FeatureSource,
|
|
mapProperties: { bounds: Store<BBox>; zoom: Store<number> },
|
|
backend: string,
|
|
isDisplayed: (id: string) => Store<boolean>
|
|
) {
|
|
const { bounds, zoom } = mapProperties
|
|
// remove all 'special' layers
|
|
layers = layers.filter((layer) => layer.source !== null && layer.source !== undefined)
|
|
|
|
const geojsonlayers = layers.filter((layer) => layer.source.geojsonSource !== undefined)
|
|
const osmLayers = layers.filter((layer) => layer.source.geojsonSource === undefined)
|
|
const fromCache = osmLayers.map(
|
|
(l) =>
|
|
new LocalStorageFeatureSource(l.id, 15, mapProperties, {
|
|
isActive: isDisplayed(l.id),
|
|
})
|
|
)
|
|
const overpassSource = LayoutSource.setupOverpass(osmLayers, bounds, zoom, featureSwitches)
|
|
const osmApiSource = LayoutSource.setupOsmApiSource(
|
|
osmLayers,
|
|
bounds,
|
|
zoom,
|
|
backend,
|
|
featureSwitches
|
|
)
|
|
const geojsonSources: FeatureSource[] = geojsonlayers.map((l) =>
|
|
LayoutSource.setupGeojsonSource(l, mapProperties, isDisplayed(l.id))
|
|
)
|
|
|
|
const expiryInSeconds = Math.min(...(layers?.map((l) => l.maxAgeOfCache) ?? []))
|
|
super(overpassSource, osmApiSource, newAndChangedElements, ...geojsonSources, ...fromCache)
|
|
}
|
|
|
|
private static setupGeojsonSource(
|
|
layer: LayerConfig,
|
|
mapProperties: { zoom: Store<number>; bounds: Store<BBox> },
|
|
isActive?: Store<boolean>
|
|
): FeatureSource {
|
|
const source = layer.source
|
|
isActive = mapProperties.zoom.map(
|
|
(z) => (isActive?.data ?? true) && z >= layer.maxzoom,
|
|
[isActive]
|
|
)
|
|
if (source.geojsonZoomLevel === undefined) {
|
|
// This is a 'load everything at once' geojson layer
|
|
return new GeoJsonSource(layer, { isActive })
|
|
} else {
|
|
return new DynamicGeoJsonTileSource(layer, mapProperties, { isActive })
|
|
}
|
|
}
|
|
|
|
private static setupOsmApiSource(
|
|
osmLayers: LayerConfig[],
|
|
bounds: Store<BBox>,
|
|
zoom: Store<number>,
|
|
backend: string,
|
|
featureSwitches: FeatureSwitchState
|
|
): FeatureSource {
|
|
const minzoom = Math.min(...osmLayers.map((layer) => layer.minzoom))
|
|
const isActive = zoom.mapD((z) => {
|
|
if (z < minzoom) {
|
|
// We are zoomed out over the zoomlevel of any layer
|
|
console.debug("Disabling overpass source: zoom < minzoom")
|
|
return false
|
|
}
|
|
|
|
// Overpass should handle this if zoomed out a bit
|
|
return z > featureSwitches.overpassMaxZoom.data
|
|
})
|
|
const allowedFeatures = new Or(osmLayers.map((l) => l.source.osmTags)).optimize()
|
|
if (typeof allowedFeatures === "boolean") {
|
|
throw "Invalid filter to init OsmFeatureSource: it optimizes away to " + allowedFeatures
|
|
}
|
|
return new OsmFeatureSource({
|
|
allowedFeatures,
|
|
bounds,
|
|
backend,
|
|
isActive,
|
|
})
|
|
}
|
|
|
|
private static setupOverpass(
|
|
osmLayers: LayerConfig[],
|
|
bounds: Store<BBox>,
|
|
zoom: Store<number>,
|
|
featureSwitches: FeatureSwitchState
|
|
): FeatureSource {
|
|
const minzoom = Math.min(...osmLayers.map((layer) => layer.minzoom))
|
|
const isActive = zoom.mapD((z) => {
|
|
if (z < minzoom) {
|
|
// We are zoomed out over the zoomlevel of any layer
|
|
console.debug("Disabling overpass source: zoom < minzoom")
|
|
return false
|
|
}
|
|
|
|
return z <= featureSwitches.overpassMaxZoom.data
|
|
})
|
|
|
|
return new OverpassFeatureSource(
|
|
{
|
|
zoom,
|
|
bounds,
|
|
layers: osmLayers,
|
|
widenFactor: featureSwitches.layoutToUse.widenFactor,
|
|
overpassUrl: featureSwitches.overpassUrl,
|
|
overpassTimeout: featureSwitches.overpassTimeout,
|
|
overpassMaxZoom: featureSwitches.overpassMaxZoom,
|
|
},
|
|
{
|
|
padToTiles: zoom.map((zoom) => Math.min(15, zoom + 1)),
|
|
isActive,
|
|
}
|
|
)
|
|
}
|
|
}
|