forked from MapComplete/MapComplete
Docs: improve typing of FeatureSources
This commit is contained in:
parent
c71036d6c6
commit
34924fd4b1
21 changed files with 140 additions and 127 deletions
|
@ -36,6 +36,6 @@ export interface FeatureSourceForTile<T extends Feature = Feature> extends Featu
|
|||
/**
|
||||
* A feature source which is aware of the indexes it contains
|
||||
*/
|
||||
export interface IndexedFeatureSource extends FeatureSource {
|
||||
export interface IndexedFeatureSource<T extends Feature> extends FeatureSource<T> {
|
||||
readonly featuresById: Store<Map<string, Feature>>
|
||||
}
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
import { Store, UIEventSource } from "../../UIEventSource"
|
||||
import { FeatureSource, IndexedFeatureSource, UpdatableFeatureSource } from "../FeatureSource"
|
||||
import { Feature } from "geojson"
|
||||
import { OsmFeature } from "../../../Models/OsmFeature"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
||||
/**
|
||||
* The featureSourceMerger receives complete geometries from various sources.
|
||||
* If multiple sources contain the same object (as determined by 'id'), only one copy of them is retained
|
||||
*/
|
||||
export default class FeatureSourceMerger<Src extends FeatureSource = FeatureSource>
|
||||
implements IndexedFeatureSource
|
||||
export default class FeatureSourceMerger<T extends Feature, Src extends FeatureSource<T> = FeatureSource<T>>
|
||||
implements IndexedFeatureSource<T>
|
||||
{
|
||||
public features: UIEventSource<Feature[]> = new UIEventSource([])
|
||||
public features: UIEventSource<T[]> = new UIEventSource([])
|
||||
public readonly featuresById: Store<Map<string, Feature>>
|
||||
protected readonly _featuresById: UIEventSource<Map<string, Feature>>
|
||||
protected readonly _sources: Src[]
|
||||
|
@ -55,7 +54,7 @@ export default class FeatureSourceMerger<Src extends FeatureSource = FeatureSour
|
|||
* Returns 'true' if this was a previously unseen item.
|
||||
* If the item was already present, nothing will happen
|
||||
*/
|
||||
public addItem(f: OsmFeature): boolean {
|
||||
public addItem(f: T): boolean {
|
||||
const id = f.properties.id
|
||||
|
||||
const all = this._featuresById.data
|
||||
|
@ -68,10 +67,10 @@ export default class FeatureSourceMerger<Src extends FeatureSource = FeatureSour
|
|||
}
|
||||
}
|
||||
|
||||
protected addData(sources: Feature[][]) {
|
||||
protected addData(sources: T[][]) {
|
||||
sources = Lists.noNull(sources)
|
||||
let somethingChanged = false
|
||||
const all: Map<string, Feature> = new Map()
|
||||
const all: Map<string, T> = new Map()
|
||||
const unseen = new Set<string>()
|
||||
// We seed the dictionary with the previously loaded features
|
||||
const oldValues = this.features.data ?? []
|
||||
|
@ -118,10 +117,11 @@ export default class FeatureSourceMerger<Src extends FeatureSource = FeatureSour
|
|||
}
|
||||
|
||||
export class UpdatableFeatureSourceMerger<
|
||||
Src extends UpdatableFeatureSource = UpdatableFeatureSource
|
||||
T extends Feature,
|
||||
Src extends UpdatableFeatureSource<T> = UpdatableFeatureSource<T>
|
||||
>
|
||||
extends FeatureSourceMerger<Src>
|
||||
implements IndexedFeatureSource, UpdatableFeatureSource
|
||||
extends FeatureSourceMerger<T, Src>
|
||||
implements IndexedFeatureSource<T>, UpdatableFeatureSource<T>
|
||||
{
|
||||
constructor(...sources: Src[]) {
|
||||
super(...sources)
|
||||
|
|
|
@ -3,13 +3,15 @@ import { Utils } from "../../../Utils"
|
|||
import { FeatureSource } from "../FeatureSource"
|
||||
import { BBox } from "../../BBox"
|
||||
import { GeoOperations } from "../../GeoOperations"
|
||||
import { Feature } from "geojson"
|
||||
import { Feature, Geometry } from "geojson"
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
|
||||
import { Tiles } from "../../../Models/TileRange"
|
||||
|
||||
export default class GeoJsonSource implements FeatureSource {
|
||||
private readonly _features: UIEventSource<Feature[]> = new UIEventSource<Feature[]>(undefined)
|
||||
public readonly features: Store<Feature[]> = this._features
|
||||
export default class GeoJsonSource<T extends Feature<Geometry, {
|
||||
id: string
|
||||
} & Record<string, any>>> implements FeatureSource<T> {
|
||||
private readonly _features: UIEventSource<T[]> = new UIEventSource(undefined)
|
||||
public readonly features: Store<T[]> = this._features
|
||||
private readonly seenids: Set<string>
|
||||
private readonly idKey?: string
|
||||
private readonly url: string
|
||||
|
@ -96,7 +98,7 @@ export default class GeoJsonSource implements FeatureSource {
|
|||
const url = this.url
|
||||
try {
|
||||
const cacheAge = (options?.maxCacheAgeSec ?? 300) * 1000
|
||||
let json = <{ features: Feature[] }>await Utils.downloadJsonCached(url, cacheAge)
|
||||
let json = <{ features: T[] }>await Utils.downloadJsonCached(url, cacheAge)
|
||||
|
||||
if (json.features === undefined || json.features === null) {
|
||||
json.features = []
|
||||
|
@ -106,7 +108,7 @@ export default class GeoJsonSource implements FeatureSource {
|
|||
json = GeoOperations.GeoJsonToWGS84(json)
|
||||
}
|
||||
|
||||
const newFeatures: Feature[] = []
|
||||
const newFeatures: T[] = []
|
||||
let i = 0
|
||||
for (const feature of json.features) {
|
||||
if (feature.geometry.type === "Point") {
|
||||
|
|
|
@ -5,8 +5,8 @@ import { FeatureSourceForTile, UpdatableFeatureSource } from "../FeatureSource"
|
|||
import { MvtToGeojson } from "mvt-to-geojson"
|
||||
import { OsmTags } from "../../../Models/OsmFeature"
|
||||
|
||||
export default class MvtSource implements FeatureSourceForTile, UpdatableFeatureSource {
|
||||
public readonly features: Store<GeojsonFeature<Geometry, OsmTags>[]>
|
||||
export default class MvtSource<T extends Feature<Geometry, OsmTags>> implements FeatureSourceForTile<T>, UpdatableFeatureSource<T> {
|
||||
public readonly features: Store<T[]>
|
||||
public readonly x: number
|
||||
public readonly y: number
|
||||
public readonly z: number
|
||||
|
|
|
@ -4,16 +4,16 @@ import { ImmutableStore, Store, UIEventSource } from "../../UIEventSource"
|
|||
import { Tiles } from "../../../Models/TileRange"
|
||||
import { BBox } from "../../BBox"
|
||||
import { TagsFilter } from "../../Tags/TagsFilter"
|
||||
import { Feature } from "geojson"
|
||||
import FeatureSourceMerger from "../Sources/FeatureSourceMerger"
|
||||
import OsmObjectDownloader from "../../Osm/OsmObjectDownloader"
|
||||
import FullNodeDatabaseSource from "../TiledFeatureSource/FullNodeDatabaseSource"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
import { OsmFeature } from "../../../Models/OsmFeature"
|
||||
|
||||
/**
|
||||
* If a tile is needed (requested via the UIEventSource in the constructor), will download the appropriate tile and pass it via 'handleTile'
|
||||
*/
|
||||
export default class OsmFeatureSource extends FeatureSourceMerger {
|
||||
export default class OsmFeatureSource<T extends OsmFeature> extends FeatureSourceMerger<T> {
|
||||
private readonly _bounds: Store<BBox>
|
||||
private readonly isActive: Store<boolean>
|
||||
private readonly _backend: string
|
||||
|
@ -33,7 +33,7 @@ export default class OsmFeatureSource extends FeatureSourceMerger {
|
|||
public readonly isRunning: UIEventSource<boolean> = new UIEventSource<boolean>(false)
|
||||
|
||||
private readonly _downloadedTiles: Set<number> = new Set<number>()
|
||||
private readonly _downloadedData: Feature[][] = []
|
||||
private readonly _downloadedData: T[][] = []
|
||||
private readonly _patchRelations: boolean
|
||||
/**
|
||||
* Downloads data directly from the OSM-api within the given bounds.
|
||||
|
@ -90,7 +90,7 @@ export default class OsmFeatureSource extends FeatureSourceMerger {
|
|||
}
|
||||
}
|
||||
|
||||
private registerFeatures(features: Feature[]): void {
|
||||
private registerFeatures(features: T[]): void {
|
||||
this._downloadedData.push(features)
|
||||
super.addData(this._downloadedData)
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ export default class OsmFeatureSource extends FeatureSourceMerger {
|
|||
const osmJson = await Utils.downloadJsonCached(url, 2000)
|
||||
try {
|
||||
this.options?.fullNodeDatabase?.handleOsmJson(osmJson, z, x, y)
|
||||
let features = <Feature<any, { id: string }>[]>OsmToGeoJson(osmJson, {
|
||||
let features = <T[]>OsmToGeoJson(osmJson, {
|
||||
flatProperties: true,
|
||||
}).features
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { Feature, FeatureCollection, Geometry } from "geojson"
|
||||
import { UpdatableFeatureSource } from "../FeatureSource"
|
||||
import { ImmutableStore, Store, UIEventSource } from "../../UIEventSource"
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
|
||||
|
@ -7,20 +6,20 @@ import { Overpass } from "../../Osm/Overpass"
|
|||
import { Utils } from "../../../Utils"
|
||||
import { TagsFilter } from "../../Tags/TagsFilter"
|
||||
import { BBox } from "../../BBox"
|
||||
import { OsmTags } from "../../../Models/OsmFeature"
|
||||
import { OsmFeature } from "../../../Models/OsmFeature"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
||||
;("use strict")
|
||||
("use strict")
|
||||
|
||||
/**
|
||||
* A wrapper around the 'Overpass'-object.
|
||||
* It has more logic and will automatically fetch the data for the right bbox and the active layers
|
||||
*/
|
||||
export default class OverpassFeatureSource implements UpdatableFeatureSource {
|
||||
export default class OverpassFeatureSource<T extends OsmFeature = OsmFeature> implements UpdatableFeatureSource<T> {
|
||||
/**
|
||||
* The last loaded features, as geojson
|
||||
*/
|
||||
public readonly features: UIEventSource<Feature[]> = new UIEventSource(undefined)
|
||||
public readonly features: UIEventSource<T[]> = new UIEventSource(undefined)
|
||||
|
||||
public readonly runningQuery: UIEventSource<boolean> = new UIEventSource<boolean>(false)
|
||||
public readonly timeout: UIEventSource<number> = new UIEventSource<number>(0)
|
||||
|
@ -111,7 +110,7 @@ export default class OverpassFeatureSource implements UpdatableFeatureSource {
|
|||
if (!navigator.onLine) {
|
||||
return
|
||||
}
|
||||
let data: FeatureCollection<Geometry, OsmTags> = undefined
|
||||
let data: { features: T[] } = undefined
|
||||
let lastUsed = 0
|
||||
const start = new Date()
|
||||
const layersToDownload = this._layersToDownload.data
|
||||
|
@ -143,7 +142,7 @@ export default class OverpassFeatureSource implements UpdatableFeatureSource {
|
|||
return undefined
|
||||
}
|
||||
this.runningQuery.setData(true)
|
||||
data = (await overpass.queryGeoJson(bounds))[0]
|
||||
data = (await overpass.queryGeoJson<T>(bounds))[0]
|
||||
} catch (e) {
|
||||
this.retries.data++
|
||||
this.retries.ping()
|
||||
|
|
|
@ -26,9 +26,6 @@ export default class StaticFeatureSource<T extends Feature = Feature> implements
|
|||
}
|
||||
}
|
||||
|
||||
public static fromGeojson<T extends Feature>(geojson: T[]): StaticFeatureSource<T> {
|
||||
return new StaticFeatureSource(geojson)
|
||||
}
|
||||
}
|
||||
|
||||
export class WritableStaticFeatureSource<T extends Feature = Feature>
|
||||
|
|
|
@ -12,7 +12,7 @@ import LocalStorageFeatureSource from "../TiledFeatureSource/LocalStorageFeature
|
|||
import FullNodeDatabaseSource from "../TiledFeatureSource/FullNodeDatabaseSource"
|
||||
import DynamicMvtileSource from "../TiledFeatureSource/DynamicMvtTileSource"
|
||||
import FeatureSourceMerger from "./FeatureSourceMerger"
|
||||
import { Feature } from "geojson"
|
||||
import { Feature, Geometry } from "geojson"
|
||||
import { OsmFeature } from "../../../Models/OsmFeature"
|
||||
|
||||
/**
|
||||
|
@ -20,7 +20,7 @@ import { OsmFeature } from "../../../Models/OsmFeature"
|
|||
*
|
||||
* Note that special layers (with `source=null` will be ignored)
|
||||
*/
|
||||
export default class ThemeSource implements IndexedFeatureSource {
|
||||
export default class ThemeSource<T extends Feature<Geometry, Record<string, any> & {id: string}>> implements IndexedFeatureSource<T> {
|
||||
/**
|
||||
* Indicates if a data source is loading something
|
||||
*/
|
||||
|
@ -28,12 +28,12 @@ export default class ThemeSource implements IndexedFeatureSource {
|
|||
|
||||
public static readonly fromCacheZoomLevel = 15
|
||||
|
||||
public features: UIEventSource<Feature[]> = new UIEventSource([])
|
||||
public readonly featuresById: Store<Map<string, Feature>>
|
||||
private readonly core: Store<ThemeSourceCore>
|
||||
public features: UIEventSource<T[]> = new UIEventSource([])
|
||||
public readonly featuresById: Store<Map<string, T>>
|
||||
private readonly core: Store<ThemeSourceCore<T>>
|
||||
|
||||
private readonly addedSources: FeatureSource[] = []
|
||||
private readonly addedItems: OsmFeature[] = []
|
||||
private readonly addedSources: FeatureSource<T>[] = []
|
||||
private readonly addedItems: T[] = []
|
||||
|
||||
constructor(
|
||||
layers: LayerConfig[],
|
||||
|
@ -47,11 +47,11 @@ export default class ThemeSource implements IndexedFeatureSource {
|
|||
const isLoading = new UIEventSource(true)
|
||||
this.isLoading = isLoading
|
||||
|
||||
const features = (this.features = new UIEventSource<Feature[]>([]))
|
||||
const features = (this.features = new UIEventSource<T[]>([]))
|
||||
const featuresById = (this.featuresById = new UIEventSource(new Map()))
|
||||
this.core = mvtAvailableLayers.mapD((mvtAvailableLayers) => {
|
||||
this.core?.data?.destruct()
|
||||
const core = new ThemeSourceCore(
|
||||
const core = new ThemeSourceCore<T>(
|
||||
layers,
|
||||
featureSwitches,
|
||||
mapProperties,
|
||||
|
@ -73,12 +73,12 @@ export default class ThemeSource implements IndexedFeatureSource {
|
|||
return this.core.data.downloadAll()
|
||||
}
|
||||
|
||||
public addSource(source: FeatureSource) {
|
||||
public addSource(source: FeatureSource<T>) {
|
||||
this.core.data?.addSource(source)
|
||||
this.addedSources.push(source)
|
||||
}
|
||||
|
||||
public addItem(obj: OsmFeature) {
|
||||
public addItem(obj: T) {
|
||||
this.core.data?.addItem(obj)
|
||||
this.addedItems.push(obj)
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ export default class ThemeSource implements IndexedFeatureSource {
|
|||
*
|
||||
* Note that special layers (with `source=null` will be ignored)
|
||||
*/
|
||||
class ThemeSourceCore extends FeatureSourceMerger {
|
||||
class ThemeSourceCore<T extends OsmFeature> extends FeatureSourceMerger<T> {
|
||||
/**
|
||||
* This source is _only_ triggered when the data is downloaded for CSV export
|
||||
* @private
|
||||
|
@ -113,10 +113,10 @@ class ThemeSourceCore extends FeatureSourceMerger {
|
|||
|
||||
const geojsonlayers = layers.filter((layer) => layer.source.geojsonSource !== undefined)
|
||||
const osmLayers = layers.filter((layer) => layer.source.geojsonSource === undefined)
|
||||
const fromCache = new Map<string, LocalStorageFeatureSource>()
|
||||
const fromCache = new Map<string, LocalStorageFeatureSource<T>>()
|
||||
if (featureSwitches.featureSwitchCache.data) {
|
||||
for (const layer of osmLayers) {
|
||||
const src = new LocalStorageFeatureSource(
|
||||
const src = new LocalStorageFeatureSource<T>(
|
||||
backend,
|
||||
layer,
|
||||
ThemeSource.fromCacheZoomLevel,
|
||||
|
@ -129,13 +129,13 @@ class ThemeSourceCore extends FeatureSourceMerger {
|
|||
fromCache.set(layer.id, src)
|
||||
}
|
||||
}
|
||||
const mvtSources: UpdatableFeatureSource[] = osmLayers
|
||||
const mvtSources: UpdatableFeatureSource<T>[] = osmLayers
|
||||
.filter((f) => mvtAvailableLayers.has(f.id))
|
||||
.map((l) => ThemeSourceCore.setupMvtSource(l, mapProperties, isDisplayed(l.id)))
|
||||
const nonMvtSources: FeatureSource[] = []
|
||||
.map((l) => ThemeSourceCore.setupMvtSource<T>(l, mapProperties, isDisplayed(l.id)))
|
||||
const nonMvtSources: FeatureSource<T>[] = []
|
||||
const nonMvtLayers: LayerConfig[] = osmLayers.filter((l) => !mvtAvailableLayers.has(l.id))
|
||||
|
||||
const osmApiSource = ThemeSourceCore.setupOsmApiSource(
|
||||
const osmApiSource = ThemeSourceCore.setupOsmApiSource<T>(
|
||||
osmLayers,
|
||||
bounds,
|
||||
zoom,
|
||||
|
@ -145,14 +145,14 @@ class ThemeSourceCore extends FeatureSourceMerger {
|
|||
)
|
||||
nonMvtSources.push(osmApiSource)
|
||||
|
||||
let overpassSource: OverpassFeatureSource = undefined
|
||||
let overpassSource: OverpassFeatureSource<T> = undefined
|
||||
if (nonMvtLayers.length > 0) {
|
||||
console.log(
|
||||
"Layers ",
|
||||
nonMvtLayers.map((l) => l.id),
|
||||
" cannot be fetched from the cache server, defaulting to overpass/OSM-api"
|
||||
)
|
||||
overpassSource = ThemeSourceCore.setupOverpass(osmLayers, bounds, zoom, featureSwitches)
|
||||
overpassSource = ThemeSourceCore.setupOverpass<T>(osmLayers, bounds, zoom, featureSwitches)
|
||||
nonMvtSources.push(overpassSource)
|
||||
}
|
||||
|
||||
|
@ -164,11 +164,11 @@ class ThemeSourceCore extends FeatureSourceMerger {
|
|||
overpassSource?.runningQuery?.addCallbackAndRun(() => setIsLoading())
|
||||
osmApiSource?.isRunning?.addCallbackAndRun(() => setIsLoading())
|
||||
|
||||
const geojsonSources: UpdatableFeatureSource[] = geojsonlayers.map((l) =>
|
||||
const geojsonSources: UpdatableFeatureSource<T>[] = geojsonlayers.map((l) =>
|
||||
ThemeSourceCore.setupGeojsonSource(l, mapProperties, isDisplayed(l.id))
|
||||
)
|
||||
|
||||
const downloadAll = new OverpassFeatureSource(
|
||||
const downloadAll = new OverpassFeatureSource<T>(
|
||||
{
|
||||
layers: layers.filter((l) => l.isNormal()),
|
||||
bounds: mapProperties.bounds,
|
||||
|
@ -196,19 +196,19 @@ class ThemeSourceCore extends FeatureSourceMerger {
|
|||
this._mapBounds = mapProperties.bounds
|
||||
}
|
||||
|
||||
private static setupMvtSource(
|
||||
private static setupMvtSource<T extends OsmFeature>(
|
||||
layer: LayerConfig,
|
||||
mapProperties: { zoom: Store<number>; bounds: Store<BBox> },
|
||||
isActive?: Store<boolean>
|
||||
): UpdatableFeatureSource {
|
||||
return new DynamicMvtileSource(layer, mapProperties, { isActive })
|
||||
): UpdatableFeatureSource<T> {
|
||||
return new DynamicMvtileSource<T>(layer, mapProperties, { isActive })
|
||||
}
|
||||
|
||||
private static setupGeojsonSource(
|
||||
private static setupGeojsonSource<T extends OsmFeature>(
|
||||
layer: LayerConfig,
|
||||
mapProperties: { zoom: Store<number>; bounds: Store<BBox> },
|
||||
isActiveByFilter?: Store<boolean>
|
||||
): UpdatableFeatureSource {
|
||||
): UpdatableFeatureSource<T> {
|
||||
const source = layer.source
|
||||
const isActive = mapProperties.zoom.map(
|
||||
(z) => (isActiveByFilter?.data ?? true) && z >= layer.minzoom,
|
||||
|
@ -216,20 +216,20 @@ class ThemeSourceCore extends FeatureSourceMerger {
|
|||
)
|
||||
if (source.geojsonZoomLevel === undefined) {
|
||||
// This is a 'load everything at once' geojson layer
|
||||
return new GeoJsonSource(layer, { isActive })
|
||||
return new GeoJsonSource<T>(layer, { isActive })
|
||||
} else {
|
||||
return new DynamicGeoJsonTileSource(layer, mapProperties, { isActive })
|
||||
return new DynamicGeoJsonTileSource<T>(layer, mapProperties, { isActive })
|
||||
}
|
||||
}
|
||||
|
||||
private static setupOsmApiSource(
|
||||
private static setupOsmApiSource<T extends OsmFeature>(
|
||||
osmLayers: LayerConfig[],
|
||||
bounds: Store<BBox>,
|
||||
zoom: Store<number>,
|
||||
backend: string,
|
||||
featureSwitches: FeatureSwitchState,
|
||||
fullNodeDatabase: FullNodeDatabaseSource
|
||||
): OsmFeatureSource | undefined {
|
||||
): OsmFeatureSource<T> | undefined {
|
||||
if (osmLayers.length == 0) {
|
||||
return undefined
|
||||
}
|
||||
|
@ -248,7 +248,7 @@ class ThemeSourceCore extends FeatureSourceMerger {
|
|||
if (typeof allowedFeatures === "boolean") {
|
||||
throw "Invalid filter to init OsmFeatureSource: it optimizes away to " + allowedFeatures
|
||||
}
|
||||
return new OsmFeatureSource({
|
||||
return new OsmFeatureSource<T>({
|
||||
allowedFeatures,
|
||||
bounds,
|
||||
backend,
|
||||
|
@ -258,12 +258,12 @@ class ThemeSourceCore extends FeatureSourceMerger {
|
|||
})
|
||||
}
|
||||
|
||||
private static setupOverpass(
|
||||
private static setupOverpass<T extends OsmFeature>(
|
||||
osmLayers: LayerConfig[],
|
||||
bounds: Store<BBox>,
|
||||
zoom: Store<number>,
|
||||
featureSwitches: FeatureSwitchState
|
||||
): OverpassFeatureSource | undefined {
|
||||
): OverpassFeatureSource<T> | undefined {
|
||||
if (osmLayers.length == 0) {
|
||||
return undefined
|
||||
}
|
||||
|
|
|
@ -4,8 +4,9 @@ import { Utils } from "../../../Utils"
|
|||
import GeoJsonSource from "../Sources/GeoJsonSource"
|
||||
import { BBox } from "../../BBox"
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
|
||||
import { Feature, Geometry } from "geojson"
|
||||
|
||||
export default class DynamicGeoJsonTileSource extends UpdatableDynamicTileSource {
|
||||
export default class DynamicGeoJsonTileSource<T extends Feature<Geometry, Record<string, any> & {id: string} > > extends UpdatableDynamicTileSource<T> {
|
||||
private static whitelistCache = new Map<string, Map<number, Set<number>>>()
|
||||
|
||||
constructor(
|
||||
|
|
|
@ -9,8 +9,10 @@ import Constants from "../../../Models/Constants"
|
|||
import { UpdatableFeatureSourceMerger } from "../Sources/FeatureSourceMerger"
|
||||
import { LineSourceMerger } from "./LineSourceMerger"
|
||||
import { PolygonSourceMerger } from "./PolygonSourceMerger"
|
||||
import { OsmFeature, OsmTags } from "../../../Models/OsmFeature"
|
||||
import { Feature, Point } from "geojson"
|
||||
|
||||
class PolygonMvtSource extends PolygonSourceMerger {
|
||||
class PolygonMvtSource<P extends Record<string, any> & { id: string }> extends PolygonSourceMerger<P> {
|
||||
constructor(
|
||||
layer: LayerConfig,
|
||||
mapProperties: {
|
||||
|
@ -44,7 +46,7 @@ class PolygonMvtSource extends PolygonSourceMerger {
|
|||
}
|
||||
}
|
||||
|
||||
class LineMvtSource extends LineSourceMerger {
|
||||
class LineMvtSource extends LineSourceMerger<OsmTags> {
|
||||
constructor(
|
||||
layer: LayerConfig,
|
||||
mapProperties: {
|
||||
|
@ -78,7 +80,7 @@ class LineMvtSource extends LineSourceMerger {
|
|||
}
|
||||
}
|
||||
|
||||
class PointMvtSource extends UpdatableDynamicTileSource {
|
||||
class PointMvtSource<T extends Feature<Point, OsmTags>> extends UpdatableDynamicTileSource<T> {
|
||||
constructor(
|
||||
layer: LayerConfig,
|
||||
mapProperties: {
|
||||
|
@ -102,7 +104,7 @@ class PointMvtSource extends UpdatableDynamicTileSource {
|
|||
layer: layer.id,
|
||||
type: "pois",
|
||||
})
|
||||
return new MvtSource(url, x, y, z)
|
||||
return new MvtSource<T>(url, x, y, z)
|
||||
},
|
||||
mapProperties,
|
||||
{
|
||||
|
@ -112,7 +114,7 @@ class PointMvtSource extends UpdatableDynamicTileSource {
|
|||
}
|
||||
}
|
||||
|
||||
export default class DynamicMvtileSource extends UpdatableFeatureSourceMerger {
|
||||
export default class DynamicMvtileSource<T extends OsmFeature> extends UpdatableFeatureSourceMerger<T> {
|
||||
constructor(
|
||||
layer: LayerConfig,
|
||||
mapProperties: {
|
||||
|
@ -124,9 +126,9 @@ export default class DynamicMvtileSource extends UpdatableFeatureSourceMerger {
|
|||
}
|
||||
) {
|
||||
super(
|
||||
new PointMvtSource(layer, mapProperties, options),
|
||||
new LineMvtSource(layer, mapProperties, options),
|
||||
new PolygonMvtSource(layer, mapProperties, options)
|
||||
<any>new PointMvtSource(layer, mapProperties, options),
|
||||
<any>new LineMvtSource(layer, mapProperties, options),
|
||||
<any>new PolygonMvtSource(layer, mapProperties, options),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,14 +3,15 @@ import { Tiles } from "../../../Models/TileRange"
|
|||
import { BBox } from "../../BBox"
|
||||
import { FeatureSource, UpdatableFeatureSource } from "../FeatureSource"
|
||||
import FeatureSourceMerger from "../Sources/FeatureSourceMerger"
|
||||
import { Feature, Geometry } from "geojson"
|
||||
|
||||
/***
|
||||
* A tiled source which dynamically loads the required tiles at a fixed zoom level.
|
||||
* A single featureSource will be initialized for every tile in view; which will later be merged into this featureSource
|
||||
*/
|
||||
export default class DynamicTileSource<
|
||||
Src extends FeatureSource = FeatureSource
|
||||
> extends FeatureSourceMerger<Src> {
|
||||
export default class DynamicTileSource<T extends Feature,
|
||||
Src extends FeatureSource<T> = FeatureSource<T>
|
||||
> extends FeatureSourceMerger<T, Src> {
|
||||
private readonly loadedTiles = new Set<number>()
|
||||
private readonly zDiff: number
|
||||
private readonly zoomlevel: Store<number>
|
||||
|
@ -97,9 +98,9 @@ export default class DynamicTileSource<
|
|||
}
|
||||
}
|
||||
|
||||
export class UpdatableDynamicTileSource<Src extends UpdatableFeatureSource = UpdatableFeatureSource>
|
||||
extends DynamicTileSource<Src>
|
||||
implements UpdatableFeatureSource
|
||||
export class UpdatableDynamicTileSource<T extends Feature<Geometry, Record<string, any> & {id: string}>, Src extends UpdatableFeatureSource<T> = UpdatableFeatureSource<T>>
|
||||
extends DynamicTileSource<T, Src>
|
||||
implements UpdatableFeatureSource<T>
|
||||
{
|
||||
constructor(
|
||||
zoomlevel: Store<number>,
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { FeatureSourceForTile, UpdatableFeatureSource } from "../FeatureSource"
|
||||
import { Store } from "../../UIEventSource"
|
||||
import { BBox } from "../../BBox"
|
||||
import { Utils } from "../../../Utils"
|
||||
import { Feature, MultiLineString, Position } from "geojson"
|
||||
import { Feature, LineString, MultiLineString, Position } from "geojson"
|
||||
import { GeoOperations } from "../../GeoOperations"
|
||||
import { UpdatableDynamicTileSource } from "./DynamicTileSource"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
@ -11,15 +10,16 @@ import { Lists } from "../../../Utils/Lists"
|
|||
* The PolygonSourceMerger receives various small pieces of bigger polygons and stitches them together.
|
||||
* This is used to reconstruct polygons of vector tiles
|
||||
*/
|
||||
export class LineSourceMerger extends UpdatableDynamicTileSource<
|
||||
FeatureSourceForTile & UpdatableFeatureSource
|
||||
export class LineSourceMerger<P extends Record<string, any> & { id: string }> extends UpdatableDynamicTileSource<
|
||||
Feature<LineString | MultiLineString, P>, FeatureSourceForTile<Feature<LineString | MultiLineString, P>> & UpdatableFeatureSource<Feature<LineString | MultiLineString, P>>
|
||||
> {
|
||||
private readonly _zoomlevel: Store<number>
|
||||
|
||||
constructor(
|
||||
zoomlevel: Store<number>,
|
||||
minzoom: number,
|
||||
constructSource: (tileIndex: number) => FeatureSourceForTile & UpdatableFeatureSource,
|
||||
constructSource: (tileIndex: number) => FeatureSourceForTile<
|
||||
Feature<LineString | MultiLineString, P>> & UpdatableFeatureSource<Feature<LineString | MultiLineString, P>>,
|
||||
mapProperties: {
|
||||
bounds: Store<BBox>
|
||||
zoom: Store<number>
|
||||
|
@ -32,9 +32,9 @@ export class LineSourceMerger extends UpdatableDynamicTileSource<
|
|||
this._zoomlevel = zoomlevel
|
||||
}
|
||||
|
||||
protected addDataFromSources(sources: FeatureSourceForTile[]) {
|
||||
protected addDataFromSources(sources: FeatureSourceForTile<Feature<LineString | MultiLineString, P>>[]) {
|
||||
sources = Lists.noNull(sources)
|
||||
const all: Map<string, Feature<MultiLineString>> = new Map()
|
||||
const all: Map<string, Feature<MultiLineString | LineString, P>> = new Map()
|
||||
const currentZoom = this._zoomlevel?.data ?? 0
|
||||
for (const source of sources) {
|
||||
if (source.z != currentZoom) {
|
||||
|
@ -48,10 +48,10 @@ export class LineSourceMerger extends UpdatableDynamicTileSource<
|
|||
} else if (f.geometry.type === "MultiLineString") {
|
||||
coordinates.push(...f.geometry.coordinates)
|
||||
} else {
|
||||
console.error("Invalid geometry type:", f.geometry.type)
|
||||
console.error("Invalid geometry type:", f.geometry["type"])
|
||||
continue
|
||||
}
|
||||
const oldV = all.get(id)
|
||||
const oldV: Feature<MultiLineString | LineString, P> = all.get(id)
|
||||
if (!oldV) {
|
||||
all.set(id, {
|
||||
type: "Feature",
|
||||
|
@ -63,7 +63,13 @@ export class LineSourceMerger extends UpdatableDynamicTileSource<
|
|||
})
|
||||
continue
|
||||
}
|
||||
oldV.geometry.coordinates.push(...coordinates)
|
||||
for (const coordinate of coordinates) {
|
||||
if (oldV.geometry.type === "LineString") {
|
||||
oldV.geometry.coordinates.push(...coordinate)
|
||||
} else {
|
||||
oldV.geometry.coordinates.push(coordinate)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,11 +2,11 @@ import DynamicTileSource from "./DynamicTileSource"
|
|||
import { ImmutableStore, Store } from "../../UIEventSource"
|
||||
import { BBox } from "../../BBox"
|
||||
import TileLocalStorage from "../Actors/TileLocalStorage"
|
||||
import { Feature } from "geojson"
|
||||
import { Feature, Geometry } from "geojson"
|
||||
import StaticFeatureSource from "../Sources/StaticFeatureSource"
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
|
||||
|
||||
export default class LocalStorageFeatureSource extends DynamicTileSource {
|
||||
export default class LocalStorageFeatureSource<T extends Feature<Geometry, { [name: string]: any }>> extends DynamicTileSource<T> {
|
||||
constructor(
|
||||
backend: string,
|
||||
layer: LayerConfig,
|
||||
|
@ -30,8 +30,8 @@ export default class LocalStorageFeatureSource extends DynamicTileSource {
|
|||
new ImmutableStore(zoomlevel),
|
||||
layer.minzoom,
|
||||
(tileIndex) =>
|
||||
new StaticFeatureSource(
|
||||
storage.getTileSource(tileIndex).mapD((features) => {
|
||||
new StaticFeatureSource<T>(
|
||||
<Store<T[]>> storage.getTileSource(tileIndex).mapD((features) => {
|
||||
if (features.length === undefined) {
|
||||
console.trace("These are not features:", features)
|
||||
storage.invalidate(tileIndex)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { FeatureSourceForTile, UpdatableFeatureSource } from "../FeatureSource"
|
||||
import { Store } from "../../UIEventSource"
|
||||
import { BBox } from "../../BBox"
|
||||
import { Feature } from "geojson"
|
||||
import { Feature, Polygon } from "geojson"
|
||||
import { GeoOperations } from "../../GeoOperations"
|
||||
import { UpdatableDynamicTileSource } from "./DynamicTileSource"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
@ -10,13 +10,14 @@ import { Lists } from "../../../Utils/Lists"
|
|||
* 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 UpdatableDynamicTileSource<
|
||||
FeatureSourceForTile & UpdatableFeatureSource
|
||||
export class PolygonSourceMerger<P extends Record<string, any> & { id: string },
|
||||
F extends Feature<Polygon, P> = Feature<Polygon, P>> extends UpdatableDynamicTileSource<
|
||||
F, FeatureSourceForTile<F> & UpdatableFeatureSource<F>
|
||||
> {
|
||||
constructor(
|
||||
zoomlevel: Store<number>,
|
||||
minzoom: number,
|
||||
constructSource: (tileIndex: number) => FeatureSourceForTile & UpdatableFeatureSource,
|
||||
constructSource: (tileIndex: number) => FeatureSourceForTile<F> & UpdatableFeatureSource<F>,
|
||||
mapProperties: {
|
||||
bounds: Store<BBox>
|
||||
zoom: Store<number>
|
||||
|
@ -28,9 +29,9 @@ export class PolygonSourceMerger extends UpdatableDynamicTileSource<
|
|||
super(zoomlevel, minzoom, constructSource, mapProperties, options)
|
||||
}
|
||||
|
||||
protected addDataFromSources(sources: FeatureSourceForTile[]) {
|
||||
protected addDataFromSources(sources: FeatureSourceForTile<F>[]) {
|
||||
sources = Lists.noNull(sources)
|
||||
const all: Map<string, Feature> = new Map()
|
||||
const all: Map<string, F> = new Map()
|
||||
const zooms: Map<string, number> = new Map()
|
||||
|
||||
for (const source of sources) {
|
||||
|
@ -60,7 +61,7 @@ export class PolygonSourceMerger extends UpdatableDynamicTileSource<
|
|||
zooms.set(id, z)
|
||||
continue
|
||||
}
|
||||
const merged = GeoOperations.union(f, oldV)
|
||||
const merged = <F> GeoOperations.union(f, oldV)
|
||||
merged.properties = oldV.properties
|
||||
all.set(id, merged)
|
||||
zooms.set(id, z)
|
||||
|
|
|
@ -54,11 +54,11 @@ export class GeoOperations {
|
|||
/**
|
||||
* Create a union between two features
|
||||
*/
|
||||
public static union(
|
||||
public static union<P >(
|
||||
f0: Feature<Polygon | MultiPolygon>,
|
||||
f1: Feature<Polygon | MultiPolygon>
|
||||
): Feature<Polygon | MultiPolygon> | null {
|
||||
return turf.union(turf.featureCollection([f0, f1]))
|
||||
): Feature<Polygon | MultiPolygon, P> | null {
|
||||
return turf.union<P>(turf.featureCollection([f0, f1]))
|
||||
}
|
||||
|
||||
public static intersect(
|
||||
|
|
|
@ -12,6 +12,7 @@ import CreateNewWayAction from "./CreateNewWayAction"
|
|||
import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig"
|
||||
import FullNodeDatabaseSource from "../../FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"
|
||||
import { Position } from "geojson"
|
||||
import type { OsmFeature } from "../../../Models/OsmFeature"
|
||||
|
||||
export interface MergePointConfig {
|
||||
withinRangeOfM: number
|
||||
|
@ -71,7 +72,7 @@ export default class CreateWayWithPointReuseAction
|
|||
private readonly _state: {
|
||||
theme: ThemeConfig
|
||||
changes: Changes
|
||||
indexedFeatures: IndexedFeatureSource
|
||||
indexedFeatures: IndexedFeatureSource<OsmFeature>
|
||||
fullNodeDatabase?: FullNodeDatabaseSource
|
||||
}
|
||||
private readonly _config: MergePointConfig[]
|
||||
|
@ -82,7 +83,7 @@ export default class CreateWayWithPointReuseAction
|
|||
state: {
|
||||
theme: ThemeConfig
|
||||
changes: Changes
|
||||
indexedFeatures: IndexedFeatureSource
|
||||
indexedFeatures: IndexedFeatureSource<OsmFeature>
|
||||
fullNodeDatabase?: FullNodeDatabaseSource
|
||||
},
|
||||
config: MergePointConfig[]
|
||||
|
@ -199,7 +200,7 @@ export default class CreateWayWithPointReuseAction
|
|||
}
|
||||
features.push(newGeometry)
|
||||
}
|
||||
return StaticFeatureSource.fromGeojson(features)
|
||||
return new StaticFeatureSource(features)
|
||||
}
|
||||
|
||||
public async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> {
|
||||
|
|
|
@ -14,6 +14,7 @@ import { OsmConnection } from "../OsmConnection"
|
|||
import { Feature, Geometry, LineString, Point } from "geojson"
|
||||
import FullNodeDatabaseSource from "../../FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
import { OsmFeature } from "../../../Models/OsmFeature"
|
||||
|
||||
export default class ReplaceGeometryAction extends OsmChangeAction implements PreviewableAction {
|
||||
/**
|
||||
|
@ -90,13 +91,13 @@ export default class ReplaceGeometryAction extends OsmChangeAction implements Pr
|
|||
public async getPreview(): Promise<FeatureSource> {
|
||||
const { closestIds, allNodesById, detachedNodes, reprojectedNodes } =
|
||||
await this.GetClosestIds()
|
||||
const preview: Feature<Geometry>[] = closestIds.map((newId, i) => {
|
||||
const preview: Feature<Geometry, {id: string}>[] = closestIds.map((newId, i) => {
|
||||
if (this.identicalTo[i] !== undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (newId === undefined) {
|
||||
return {
|
||||
return <OsmFeature> {
|
||||
type: "Feature",
|
||||
properties: {
|
||||
newpoint: "yes",
|
||||
|
@ -127,7 +128,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction implements Pr
|
|||
|
||||
reprojectedNodes.forEach(({ newLat, newLon, nodeId }) => {
|
||||
const origNode = allNodesById.get(nodeId)
|
||||
const feature: Feature<LineString> = {
|
||||
const feature: Feature<LineString, {id: string} & Record<string, any>> = {
|
||||
type: "Feature",
|
||||
properties: {
|
||||
move: "yes",
|
||||
|
@ -149,7 +150,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction implements Pr
|
|||
|
||||
detachedNodes.forEach(({ reason }, id) => {
|
||||
const origNode = allNodesById.get(id)
|
||||
const feature: Feature<Point> = {
|
||||
const feature: OsmFeature & Feature<Point> = {
|
||||
type: "Feature",
|
||||
properties: {
|
||||
detach: "yes",
|
||||
|
@ -165,7 +166,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction implements Pr
|
|||
preview.push(feature)
|
||||
})
|
||||
|
||||
return StaticFeatureSource.fromGeojson(Lists.noNull(preview))
|
||||
return new StaticFeatureSource(Lists.noNull(preview))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,9 +3,9 @@ import { Utils } from "../../Utils"
|
|||
import { ImmutableStore, Store } from "../UIEventSource"
|
||||
import { BBox } from "../BBox"
|
||||
import osmtogeojson from "osmtogeojson"
|
||||
import { FeatureCollection, Geometry } from "geojson"
|
||||
import { OsmTags } from "../../Models/OsmFeature"
|
||||
;("use strict")
|
||||
import { Feature } from "geojson"
|
||||
|
||||
("use strict")
|
||||
/**
|
||||
* Interfaces overpass to get all the latest data
|
||||
*/
|
||||
|
@ -37,7 +37,7 @@ export class Overpass {
|
|||
this._includeMeta = includeMeta
|
||||
}
|
||||
|
||||
public async queryGeoJson(bounds: BBox): Promise<[FeatureCollection<Geometry, OsmTags>, Date]> {
|
||||
public async queryGeoJson<T extends Feature>(bounds: BBox): Promise<[{features: T[]}, Date]> {
|
||||
const bbox =
|
||||
"[bbox:" +
|
||||
bounds.getSouth() +
|
||||
|
@ -49,16 +49,16 @@ export class Overpass {
|
|||
bounds.getEast() +
|
||||
"]"
|
||||
const query = this.buildScript(bbox)
|
||||
return await this.ExecuteQuery(query)
|
||||
return await this.ExecuteQuery<T>(query)
|
||||
}
|
||||
|
||||
public buildUrl(query: string) {
|
||||
return `${this._interpreterUrl}?data=${encodeURIComponent(query)}`
|
||||
}
|
||||
|
||||
private async ExecuteQuery(
|
||||
private async ExecuteQuery<T extends Feature>(
|
||||
query: string
|
||||
): Promise<[FeatureCollection<Geometry, OsmTags>, Date]> {
|
||||
): Promise<[{features: T[]}, Date]> {
|
||||
const json = await Utils.downloadJson<{
|
||||
elements: []
|
||||
remark
|
||||
|
@ -73,7 +73,7 @@ export class Overpass {
|
|||
console.warn("No features for", this.buildUrl(query))
|
||||
}
|
||||
|
||||
const geojson = <FeatureCollection<Geometry, OsmTags>>osmtogeojson(json)
|
||||
const geojson = <{features: T[]}> <any> osmtogeojson(json)
|
||||
const osmTime = new Date(json.osm3s.timestamp_osm_base)
|
||||
return [geojson, osmTime]
|
||||
}
|
||||
|
|
|
@ -357,7 +357,7 @@ export default class ShowDataLayer {
|
|||
|
||||
public static showMultipleLayers(
|
||||
mlmap: UIEventSource<MlMap>,
|
||||
features: FeatureSource,
|
||||
features: FeatureSource<Feature<Geometry, Record<string, any> & {id: string}>>,
|
||||
layers: LayerConfig[],
|
||||
options?: Partial<Omit<ShowDataLayerOptions, "features" | "layer">>
|
||||
) {
|
||||
|
|
|
@ -11,6 +11,7 @@ import { IndexedFeatureSource } from "../../Logic/FeatureSource/FeatureSource"
|
|||
import SvelteUIElement from "../Base/SvelteUIElement"
|
||||
import { Feature } from "geojson"
|
||||
import AutoApplyButton from "./AutoApplyButton.svelte"
|
||||
import { OsmFeature } from "../../Models/OsmFeature"
|
||||
|
||||
export abstract class AutoAction extends SpecialVisualization {
|
||||
supportsAutoAction: boolean
|
||||
|
@ -20,7 +21,7 @@ export abstract class AutoAction extends SpecialVisualization {
|
|||
state: {
|
||||
theme: ThemeConfig
|
||||
changes: Changes
|
||||
indexedFeatures: IndexedFeatureSource
|
||||
indexedFeatures: IndexedFeatureSource<OsmFeature>
|
||||
},
|
||||
tagSource: UIEventSource<Record<string, string>>,
|
||||
argument: string[]
|
||||
|
|
|
@ -14,6 +14,7 @@ import CreateMultiPolygonWithPointReuseAction from "../../../Logic/Osm/Actions/C
|
|||
import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig"
|
||||
import { Changes } from "../../../Logic/Osm/Changes"
|
||||
import FullNodeDatabaseSource from "../../../Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"
|
||||
import { OsmFeature } from "../../../Models/OsmFeature"
|
||||
|
||||
export interface WayImportFlowArguments extends ImportFlowArguments {
|
||||
max_snap_distance: string
|
||||
|
@ -54,7 +55,7 @@ export default class WayImportFlowState extends ImportFlow<WayImportFlowArgument
|
|||
state: {
|
||||
theme: ThemeConfig
|
||||
changes: Changes
|
||||
indexedFeatures: IndexedFeatureSource
|
||||
indexedFeatures: IndexedFeatureSource<OsmFeature>
|
||||
fullNodeDatabase?: FullNodeDatabaseSource
|
||||
},
|
||||
tagsToApply: Store<Tag[]>,
|
||||
|
@ -82,7 +83,7 @@ export default class WayImportFlowState extends ImportFlow<WayImportFlowArgument
|
|||
const coors = feature.geometry.coordinates
|
||||
return new CreateWayWithPointReuseAction(tagsToApply.data, coors, state, mergeConfigs)
|
||||
} else {
|
||||
throw "Unsupported type"
|
||||
throw "Unsupported type: cannot import something of type "+feature.geometry["type"]+", only Polygon and LineString are supported"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue