Docs: improve typing of FeatureSources

This commit is contained in:
Pieter Vander Vennet 2025-08-20 01:12:09 +02:00
parent c71036d6c6
commit 34924fd4b1
21 changed files with 140 additions and 127 deletions

View file

@ -36,6 +36,6 @@ export interface FeatureSourceForTile<T extends Feature = Feature> extends Featu
/** /**
* A feature source which is aware of the indexes it contains * 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>> readonly featuresById: Store<Map<string, Feature>>
} }

View file

@ -1,17 +1,16 @@
import { Store, UIEventSource } from "../../UIEventSource" import { Store, UIEventSource } from "../../UIEventSource"
import { FeatureSource, IndexedFeatureSource, UpdatableFeatureSource } from "../FeatureSource" import { FeatureSource, IndexedFeatureSource, UpdatableFeatureSource } from "../FeatureSource"
import { Feature } from "geojson" import { Feature } from "geojson"
import { OsmFeature } from "../../../Models/OsmFeature"
import { Lists } from "../../../Utils/Lists" import { Lists } from "../../../Utils/Lists"
/** /**
* The featureSourceMerger receives complete geometries from various sources. * 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 * 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> export default class FeatureSourceMerger<T extends Feature, Src extends FeatureSource<T> = FeatureSource<T>>
implements IndexedFeatureSource implements IndexedFeatureSource<T>
{ {
public features: UIEventSource<Feature[]> = new UIEventSource([]) public features: UIEventSource<T[]> = new UIEventSource([])
public readonly featuresById: Store<Map<string, Feature>> public readonly featuresById: Store<Map<string, Feature>>
protected readonly _featuresById: UIEventSource<Map<string, Feature>> protected readonly _featuresById: UIEventSource<Map<string, Feature>>
protected readonly _sources: Src[] 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. * Returns 'true' if this was a previously unseen item.
* If the item was already present, nothing will happen * 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 id = f.properties.id
const all = this._featuresById.data 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) sources = Lists.noNull(sources)
let somethingChanged = false let somethingChanged = false
const all: Map<string, Feature> = new Map() const all: Map<string, T> = new Map()
const unseen = new Set<string>() const unseen = new Set<string>()
// We seed the dictionary with the previously loaded features // We seed the dictionary with the previously loaded features
const oldValues = this.features.data ?? [] const oldValues = this.features.data ?? []
@ -118,10 +117,11 @@ export default class FeatureSourceMerger<Src extends FeatureSource = FeatureSour
} }
export class UpdatableFeatureSourceMerger< export class UpdatableFeatureSourceMerger<
Src extends UpdatableFeatureSource = UpdatableFeatureSource T extends Feature,
Src extends UpdatableFeatureSource<T> = UpdatableFeatureSource<T>
> >
extends FeatureSourceMerger<Src> extends FeatureSourceMerger<T, Src>
implements IndexedFeatureSource, UpdatableFeatureSource implements IndexedFeatureSource<T>, UpdatableFeatureSource<T>
{ {
constructor(...sources: Src[]) { constructor(...sources: Src[]) {
super(...sources) super(...sources)

View file

@ -3,13 +3,15 @@ import { Utils } from "../../../Utils"
import { FeatureSource } from "../FeatureSource" import { FeatureSource } from "../FeatureSource"
import { BBox } from "../../BBox" import { BBox } from "../../BBox"
import { GeoOperations } from "../../GeoOperations" import { GeoOperations } from "../../GeoOperations"
import { Feature } from "geojson" import { Feature, Geometry } from "geojson"
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
import { Tiles } from "../../../Models/TileRange" import { Tiles } from "../../../Models/TileRange"
export default class GeoJsonSource implements FeatureSource { export default class GeoJsonSource<T extends Feature<Geometry, {
private readonly _features: UIEventSource<Feature[]> = new UIEventSource<Feature[]>(undefined) id: string
public readonly features: Store<Feature[]> = this._features } & 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 seenids: Set<string>
private readonly idKey?: string private readonly idKey?: string
private readonly url: string private readonly url: string
@ -96,7 +98,7 @@ export default class GeoJsonSource implements FeatureSource {
const url = this.url const url = this.url
try { try {
const cacheAge = (options?.maxCacheAgeSec ?? 300) * 1000 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) { if (json.features === undefined || json.features === null) {
json.features = [] json.features = []
@ -106,7 +108,7 @@ export default class GeoJsonSource implements FeatureSource {
json = GeoOperations.GeoJsonToWGS84(json) json = GeoOperations.GeoJsonToWGS84(json)
} }
const newFeatures: Feature[] = [] const newFeatures: T[] = []
let i = 0 let i = 0
for (const feature of json.features) { for (const feature of json.features) {
if (feature.geometry.type === "Point") { if (feature.geometry.type === "Point") {

View file

@ -5,8 +5,8 @@ import { FeatureSourceForTile, UpdatableFeatureSource } from "../FeatureSource"
import { MvtToGeojson } from "mvt-to-geojson" import { MvtToGeojson } from "mvt-to-geojson"
import { OsmTags } from "../../../Models/OsmFeature" import { OsmTags } from "../../../Models/OsmFeature"
export default class MvtSource implements FeatureSourceForTile, UpdatableFeatureSource { export default class MvtSource<T extends Feature<Geometry, OsmTags>> implements FeatureSourceForTile<T>, UpdatableFeatureSource<T> {
public readonly features: Store<GeojsonFeature<Geometry, OsmTags>[]> public readonly features: Store<T[]>
public readonly x: number public readonly x: number
public readonly y: number public readonly y: number
public readonly z: number public readonly z: number

View file

@ -4,16 +4,16 @@ import { ImmutableStore, Store, UIEventSource } from "../../UIEventSource"
import { Tiles } from "../../../Models/TileRange" import { Tiles } from "../../../Models/TileRange"
import { BBox } from "../../BBox" import { BBox } from "../../BBox"
import { TagsFilter } from "../../Tags/TagsFilter" import { TagsFilter } from "../../Tags/TagsFilter"
import { Feature } from "geojson"
import FeatureSourceMerger from "../Sources/FeatureSourceMerger" import FeatureSourceMerger from "../Sources/FeatureSourceMerger"
import OsmObjectDownloader from "../../Osm/OsmObjectDownloader" import OsmObjectDownloader from "../../Osm/OsmObjectDownloader"
import FullNodeDatabaseSource from "../TiledFeatureSource/FullNodeDatabaseSource" import FullNodeDatabaseSource from "../TiledFeatureSource/FullNodeDatabaseSource"
import { Lists } from "../../../Utils/Lists" 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' * 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 _bounds: Store<BBox>
private readonly isActive: Store<boolean> private readonly isActive: Store<boolean>
private readonly _backend: string private readonly _backend: string
@ -33,7 +33,7 @@ export default class OsmFeatureSource extends FeatureSourceMerger {
public readonly isRunning: UIEventSource<boolean> = new UIEventSource<boolean>(false) public readonly isRunning: UIEventSource<boolean> = new UIEventSource<boolean>(false)
private readonly _downloadedTiles: Set<number> = new Set<number>() private readonly _downloadedTiles: Set<number> = new Set<number>()
private readonly _downloadedData: Feature[][] = [] private readonly _downloadedData: T[][] = []
private readonly _patchRelations: boolean private readonly _patchRelations: boolean
/** /**
* Downloads data directly from the OSM-api within the given bounds. * 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) this._downloadedData.push(features)
super.addData(this._downloadedData) super.addData(this._downloadedData)
} }
@ -160,7 +160,7 @@ export default class OsmFeatureSource extends FeatureSourceMerger {
const osmJson = await Utils.downloadJsonCached(url, 2000) const osmJson = await Utils.downloadJsonCached(url, 2000)
try { try {
this.options?.fullNodeDatabase?.handleOsmJson(osmJson, z, x, y) this.options?.fullNodeDatabase?.handleOsmJson(osmJson, z, x, y)
let features = <Feature<any, { id: string }>[]>OsmToGeoJson(osmJson, { let features = <T[]>OsmToGeoJson(osmJson, {
flatProperties: true, flatProperties: true,
}).features }).features

View file

@ -1,4 +1,3 @@
import { Feature, FeatureCollection, Geometry } from "geojson"
import { UpdatableFeatureSource } from "../FeatureSource" import { UpdatableFeatureSource } from "../FeatureSource"
import { ImmutableStore, Store, UIEventSource } from "../../UIEventSource" import { ImmutableStore, Store, UIEventSource } from "../../UIEventSource"
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
@ -7,20 +6,20 @@ import { Overpass } from "../../Osm/Overpass"
import { Utils } from "../../../Utils" import { Utils } from "../../../Utils"
import { TagsFilter } from "../../Tags/TagsFilter" import { TagsFilter } from "../../Tags/TagsFilter"
import { BBox } from "../../BBox" import { BBox } from "../../BBox"
import { OsmTags } from "../../../Models/OsmFeature" import { OsmFeature } from "../../../Models/OsmFeature"
import { Lists } from "../../../Utils/Lists" import { Lists } from "../../../Utils/Lists"
;("use strict") ("use strict")
/** /**
* A wrapper around the 'Overpass'-object. * A wrapper around the 'Overpass'-object.
* It has more logic and will automatically fetch the data for the right bbox and the active layers * 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 * 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 runningQuery: UIEventSource<boolean> = new UIEventSource<boolean>(false)
public readonly timeout: UIEventSource<number> = new UIEventSource<number>(0) public readonly timeout: UIEventSource<number> = new UIEventSource<number>(0)
@ -111,7 +110,7 @@ export default class OverpassFeatureSource implements UpdatableFeatureSource {
if (!navigator.onLine) { if (!navigator.onLine) {
return return
} }
let data: FeatureCollection<Geometry, OsmTags> = undefined let data: { features: T[] } = undefined
let lastUsed = 0 let lastUsed = 0
const start = new Date() const start = new Date()
const layersToDownload = this._layersToDownload.data const layersToDownload = this._layersToDownload.data
@ -143,7 +142,7 @@ export default class OverpassFeatureSource implements UpdatableFeatureSource {
return undefined return undefined
} }
this.runningQuery.setData(true) this.runningQuery.setData(true)
data = (await overpass.queryGeoJson(bounds))[0] data = (await overpass.queryGeoJson<T>(bounds))[0]
} catch (e) { } catch (e) {
this.retries.data++ this.retries.data++
this.retries.ping() this.retries.ping()

View file

@ -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> export class WritableStaticFeatureSource<T extends Feature = Feature>

View file

@ -12,7 +12,7 @@ import LocalStorageFeatureSource from "../TiledFeatureSource/LocalStorageFeature
import FullNodeDatabaseSource from "../TiledFeatureSource/FullNodeDatabaseSource" import FullNodeDatabaseSource from "../TiledFeatureSource/FullNodeDatabaseSource"
import DynamicMvtileSource from "../TiledFeatureSource/DynamicMvtTileSource" import DynamicMvtileSource from "../TiledFeatureSource/DynamicMvtTileSource"
import FeatureSourceMerger from "./FeatureSourceMerger" import FeatureSourceMerger from "./FeatureSourceMerger"
import { Feature } from "geojson" import { Feature, Geometry } from "geojson"
import { OsmFeature } from "../../../Models/OsmFeature" import { OsmFeature } from "../../../Models/OsmFeature"
/** /**
@ -20,7 +20,7 @@ import { OsmFeature } from "../../../Models/OsmFeature"
* *
* Note that special layers (with `source=null` will be ignored) * 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 * Indicates if a data source is loading something
*/ */
@ -28,12 +28,12 @@ export default class ThemeSource implements IndexedFeatureSource {
public static readonly fromCacheZoomLevel = 15 public static readonly fromCacheZoomLevel = 15
public features: UIEventSource<Feature[]> = new UIEventSource([]) public features: UIEventSource<T[]> = new UIEventSource([])
public readonly featuresById: Store<Map<string, Feature>> public readonly featuresById: Store<Map<string, T>>
private readonly core: Store<ThemeSourceCore> private readonly core: Store<ThemeSourceCore<T>>
private readonly addedSources: FeatureSource[] = [] private readonly addedSources: FeatureSource<T>[] = []
private readonly addedItems: OsmFeature[] = [] private readonly addedItems: T[] = []
constructor( constructor(
layers: LayerConfig[], layers: LayerConfig[],
@ -47,11 +47,11 @@ export default class ThemeSource implements IndexedFeatureSource {
const isLoading = new UIEventSource(true) const isLoading = new UIEventSource(true)
this.isLoading = isLoading 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())) const featuresById = (this.featuresById = new UIEventSource(new Map()))
this.core = mvtAvailableLayers.mapD((mvtAvailableLayers) => { this.core = mvtAvailableLayers.mapD((mvtAvailableLayers) => {
this.core?.data?.destruct() this.core?.data?.destruct()
const core = new ThemeSourceCore( const core = new ThemeSourceCore<T>(
layers, layers,
featureSwitches, featureSwitches,
mapProperties, mapProperties,
@ -73,12 +73,12 @@ export default class ThemeSource implements IndexedFeatureSource {
return this.core.data.downloadAll() return this.core.data.downloadAll()
} }
public addSource(source: FeatureSource) { public addSource(source: FeatureSource<T>) {
this.core.data?.addSource(source) this.core.data?.addSource(source)
this.addedSources.push(source) this.addedSources.push(source)
} }
public addItem(obj: OsmFeature) { public addItem(obj: T) {
this.core.data?.addItem(obj) this.core.data?.addItem(obj)
this.addedItems.push(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) * 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 * This source is _only_ triggered when the data is downloaded for CSV export
* @private * @private
@ -113,10 +113,10 @@ class ThemeSourceCore extends FeatureSourceMerger {
const geojsonlayers = layers.filter((layer) => layer.source.geojsonSource !== undefined) const geojsonlayers = layers.filter((layer) => layer.source.geojsonSource !== undefined)
const osmLayers = 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) { if (featureSwitches.featureSwitchCache.data) {
for (const layer of osmLayers) { for (const layer of osmLayers) {
const src = new LocalStorageFeatureSource( const src = new LocalStorageFeatureSource<T>(
backend, backend,
layer, layer,
ThemeSource.fromCacheZoomLevel, ThemeSource.fromCacheZoomLevel,
@ -129,13 +129,13 @@ class ThemeSourceCore extends FeatureSourceMerger {
fromCache.set(layer.id, src) fromCache.set(layer.id, src)
} }
} }
const mvtSources: UpdatableFeatureSource[] = osmLayers const mvtSources: UpdatableFeatureSource<T>[] = osmLayers
.filter((f) => mvtAvailableLayers.has(f.id)) .filter((f) => mvtAvailableLayers.has(f.id))
.map((l) => ThemeSourceCore.setupMvtSource(l, mapProperties, isDisplayed(l.id))) .map((l) => ThemeSourceCore.setupMvtSource<T>(l, mapProperties, isDisplayed(l.id)))
const nonMvtSources: FeatureSource[] = [] const nonMvtSources: FeatureSource<T>[] = []
const nonMvtLayers: LayerConfig[] = osmLayers.filter((l) => !mvtAvailableLayers.has(l.id)) const nonMvtLayers: LayerConfig[] = osmLayers.filter((l) => !mvtAvailableLayers.has(l.id))
const osmApiSource = ThemeSourceCore.setupOsmApiSource( const osmApiSource = ThemeSourceCore.setupOsmApiSource<T>(
osmLayers, osmLayers,
bounds, bounds,
zoom, zoom,
@ -145,14 +145,14 @@ class ThemeSourceCore extends FeatureSourceMerger {
) )
nonMvtSources.push(osmApiSource) nonMvtSources.push(osmApiSource)
let overpassSource: OverpassFeatureSource = undefined let overpassSource: OverpassFeatureSource<T> = undefined
if (nonMvtLayers.length > 0) { if (nonMvtLayers.length > 0) {
console.log( console.log(
"Layers ", "Layers ",
nonMvtLayers.map((l) => l.id), nonMvtLayers.map((l) => l.id),
" cannot be fetched from the cache server, defaulting to overpass/OSM-api" " 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) nonMvtSources.push(overpassSource)
} }
@ -164,11 +164,11 @@ class ThemeSourceCore extends FeatureSourceMerger {
overpassSource?.runningQuery?.addCallbackAndRun(() => setIsLoading()) overpassSource?.runningQuery?.addCallbackAndRun(() => setIsLoading())
osmApiSource?.isRunning?.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)) ThemeSourceCore.setupGeojsonSource(l, mapProperties, isDisplayed(l.id))
) )
const downloadAll = new OverpassFeatureSource( const downloadAll = new OverpassFeatureSource<T>(
{ {
layers: layers.filter((l) => l.isNormal()), layers: layers.filter((l) => l.isNormal()),
bounds: mapProperties.bounds, bounds: mapProperties.bounds,
@ -196,19 +196,19 @@ class ThemeSourceCore extends FeatureSourceMerger {
this._mapBounds = mapProperties.bounds this._mapBounds = mapProperties.bounds
} }
private static setupMvtSource( private static setupMvtSource<T extends OsmFeature>(
layer: LayerConfig, layer: LayerConfig,
mapProperties: { zoom: Store<number>; bounds: Store<BBox> }, mapProperties: { zoom: Store<number>; bounds: Store<BBox> },
isActive?: Store<boolean> isActive?: Store<boolean>
): UpdatableFeatureSource { ): UpdatableFeatureSource<T> {
return new DynamicMvtileSource(layer, mapProperties, { isActive }) return new DynamicMvtileSource<T>(layer, mapProperties, { isActive })
} }
private static setupGeojsonSource( private static setupGeojsonSource<T extends OsmFeature>(
layer: LayerConfig, layer: LayerConfig,
mapProperties: { zoom: Store<number>; bounds: Store<BBox> }, mapProperties: { zoom: Store<number>; bounds: Store<BBox> },
isActiveByFilter?: Store<boolean> isActiveByFilter?: Store<boolean>
): UpdatableFeatureSource { ): UpdatableFeatureSource<T> {
const source = layer.source const source = layer.source
const isActive = mapProperties.zoom.map( const isActive = mapProperties.zoom.map(
(z) => (isActiveByFilter?.data ?? true) && z >= layer.minzoom, (z) => (isActiveByFilter?.data ?? true) && z >= layer.minzoom,
@ -216,20 +216,20 @@ class ThemeSourceCore extends FeatureSourceMerger {
) )
if (source.geojsonZoomLevel === undefined) { if (source.geojsonZoomLevel === undefined) {
// This is a 'load everything at once' geojson layer // This is a 'load everything at once' geojson layer
return new GeoJsonSource(layer, { isActive }) return new GeoJsonSource<T>(layer, { isActive })
} else { } 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[], osmLayers: LayerConfig[],
bounds: Store<BBox>, bounds: Store<BBox>,
zoom: Store<number>, zoom: Store<number>,
backend: string, backend: string,
featureSwitches: FeatureSwitchState, featureSwitches: FeatureSwitchState,
fullNodeDatabase: FullNodeDatabaseSource fullNodeDatabase: FullNodeDatabaseSource
): OsmFeatureSource | undefined { ): OsmFeatureSource<T> | undefined {
if (osmLayers.length == 0) { if (osmLayers.length == 0) {
return undefined return undefined
} }
@ -248,7 +248,7 @@ class ThemeSourceCore extends FeatureSourceMerger {
if (typeof allowedFeatures === "boolean") { if (typeof allowedFeatures === "boolean") {
throw "Invalid filter to init OsmFeatureSource: it optimizes away to " + allowedFeatures throw "Invalid filter to init OsmFeatureSource: it optimizes away to " + allowedFeatures
} }
return new OsmFeatureSource({ return new OsmFeatureSource<T>({
allowedFeatures, allowedFeatures,
bounds, bounds,
backend, backend,
@ -258,12 +258,12 @@ class ThemeSourceCore extends FeatureSourceMerger {
}) })
} }
private static setupOverpass( private static setupOverpass<T extends OsmFeature>(
osmLayers: LayerConfig[], osmLayers: LayerConfig[],
bounds: Store<BBox>, bounds: Store<BBox>,
zoom: Store<number>, zoom: Store<number>,
featureSwitches: FeatureSwitchState featureSwitches: FeatureSwitchState
): OverpassFeatureSource | undefined { ): OverpassFeatureSource<T> | undefined {
if (osmLayers.length == 0) { if (osmLayers.length == 0) {
return undefined return undefined
} }

View file

@ -4,8 +4,9 @@ import { Utils } from "../../../Utils"
import GeoJsonSource from "../Sources/GeoJsonSource" import GeoJsonSource from "../Sources/GeoJsonSource"
import { BBox } from "../../BBox" import { BBox } from "../../BBox"
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" 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>>>() private static whitelistCache = new Map<string, Map<number, Set<number>>>()
constructor( constructor(

View file

@ -9,8 +9,10 @@ import Constants from "../../../Models/Constants"
import { UpdatableFeatureSourceMerger } from "../Sources/FeatureSourceMerger" import { UpdatableFeatureSourceMerger } from "../Sources/FeatureSourceMerger"
import { LineSourceMerger } from "./LineSourceMerger" import { LineSourceMerger } from "./LineSourceMerger"
import { PolygonSourceMerger } from "./PolygonSourceMerger" 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( constructor(
layer: LayerConfig, layer: LayerConfig,
mapProperties: { mapProperties: {
@ -44,7 +46,7 @@ class PolygonMvtSource extends PolygonSourceMerger {
} }
} }
class LineMvtSource extends LineSourceMerger { class LineMvtSource extends LineSourceMerger<OsmTags> {
constructor( constructor(
layer: LayerConfig, layer: LayerConfig,
mapProperties: { mapProperties: {
@ -78,7 +80,7 @@ class LineMvtSource extends LineSourceMerger {
} }
} }
class PointMvtSource extends UpdatableDynamicTileSource { class PointMvtSource<T extends Feature<Point, OsmTags>> extends UpdatableDynamicTileSource<T> {
constructor( constructor(
layer: LayerConfig, layer: LayerConfig,
mapProperties: { mapProperties: {
@ -102,7 +104,7 @@ class PointMvtSource extends UpdatableDynamicTileSource {
layer: layer.id, layer: layer.id,
type: "pois", type: "pois",
}) })
return new MvtSource(url, x, y, z) return new MvtSource<T>(url, x, y, z)
}, },
mapProperties, 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( constructor(
layer: LayerConfig, layer: LayerConfig,
mapProperties: { mapProperties: {
@ -124,9 +126,9 @@ export default class DynamicMvtileSource extends UpdatableFeatureSourceMerger {
} }
) { ) {
super( super(
new PointMvtSource(layer, mapProperties, options), <any>new PointMvtSource(layer, mapProperties, options),
new LineMvtSource(layer, mapProperties, options), <any>new LineMvtSource(layer, mapProperties, options),
new PolygonMvtSource(layer, mapProperties, options) <any>new PolygonMvtSource(layer, mapProperties, options),
) )
} }
} }

View file

@ -3,14 +3,15 @@ import { Tiles } from "../../../Models/TileRange"
import { BBox } from "../../BBox" import { BBox } from "../../BBox"
import { FeatureSource, UpdatableFeatureSource } from "../FeatureSource" import { FeatureSource, UpdatableFeatureSource } from "../FeatureSource"
import FeatureSourceMerger from "../Sources/FeatureSourceMerger" 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 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 * A single featureSource will be initialized for every tile in view; which will later be merged into this featureSource
*/ */
export default class DynamicTileSource< export default class DynamicTileSource<T extends Feature,
Src extends FeatureSource = FeatureSource Src extends FeatureSource<T> = FeatureSource<T>
> extends FeatureSourceMerger<Src> { > extends FeatureSourceMerger<T, Src> {
private readonly loadedTiles = new Set<number>() private readonly loadedTiles = new Set<number>()
private readonly zDiff: number private readonly zDiff: number
private readonly zoomlevel: Store<number> private readonly zoomlevel: Store<number>
@ -97,9 +98,9 @@ export default class DynamicTileSource<
} }
} }
export class UpdatableDynamicTileSource<Src extends UpdatableFeatureSource = UpdatableFeatureSource> export class UpdatableDynamicTileSource<T extends Feature<Geometry, Record<string, any> & {id: string}>, Src extends UpdatableFeatureSource<T> = UpdatableFeatureSource<T>>
extends DynamicTileSource<Src> extends DynamicTileSource<T, Src>
implements UpdatableFeatureSource implements UpdatableFeatureSource<T>
{ {
constructor( constructor(
zoomlevel: Store<number>, zoomlevel: Store<number>,

View file

@ -1,8 +1,7 @@
import { FeatureSourceForTile, UpdatableFeatureSource } from "../FeatureSource" import { FeatureSourceForTile, UpdatableFeatureSource } from "../FeatureSource"
import { Store } from "../../UIEventSource" import { Store } from "../../UIEventSource"
import { BBox } from "../../BBox" import { BBox } from "../../BBox"
import { Utils } from "../../../Utils" import { Feature, LineString, MultiLineString, Position } from "geojson"
import { Feature, MultiLineString, Position } from "geojson"
import { GeoOperations } from "../../GeoOperations" import { GeoOperations } from "../../GeoOperations"
import { UpdatableDynamicTileSource } from "./DynamicTileSource" import { UpdatableDynamicTileSource } from "./DynamicTileSource"
import { Lists } from "../../../Utils/Lists" 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. * The PolygonSourceMerger receives various small pieces of bigger polygons and stitches them together.
* This is used to reconstruct polygons of vector tiles * This is used to reconstruct polygons of vector tiles
*/ */
export class LineSourceMerger extends UpdatableDynamicTileSource< export class LineSourceMerger<P extends Record<string, any> & { id: string }> extends UpdatableDynamicTileSource<
FeatureSourceForTile & UpdatableFeatureSource Feature<LineString | MultiLineString, P>, FeatureSourceForTile<Feature<LineString | MultiLineString, P>> & UpdatableFeatureSource<Feature<LineString | MultiLineString, P>>
> { > {
private readonly _zoomlevel: Store<number> private readonly _zoomlevel: Store<number>
constructor( constructor(
zoomlevel: Store<number>, zoomlevel: Store<number>,
minzoom: number, minzoom: number,
constructSource: (tileIndex: number) => FeatureSourceForTile & UpdatableFeatureSource, constructSource: (tileIndex: number) => FeatureSourceForTile<
Feature<LineString | MultiLineString, P>> & UpdatableFeatureSource<Feature<LineString | MultiLineString, P>>,
mapProperties: { mapProperties: {
bounds: Store<BBox> bounds: Store<BBox>
zoom: Store<number> zoom: Store<number>
@ -32,9 +32,9 @@ export class LineSourceMerger extends UpdatableDynamicTileSource<
this._zoomlevel = zoomlevel this._zoomlevel = zoomlevel
} }
protected addDataFromSources(sources: FeatureSourceForTile[]) { protected addDataFromSources(sources: FeatureSourceForTile<Feature<LineString | MultiLineString, P>>[]) {
sources = Lists.noNull(sources) 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 const currentZoom = this._zoomlevel?.data ?? 0
for (const source of sources) { for (const source of sources) {
if (source.z != currentZoom) { if (source.z != currentZoom) {
@ -48,10 +48,10 @@ export class LineSourceMerger extends UpdatableDynamicTileSource<
} else if (f.geometry.type === "MultiLineString") { } else if (f.geometry.type === "MultiLineString") {
coordinates.push(...f.geometry.coordinates) coordinates.push(...f.geometry.coordinates)
} else { } else {
console.error("Invalid geometry type:", f.geometry.type) console.error("Invalid geometry type:", f.geometry["type"])
continue continue
} }
const oldV = all.get(id) const oldV: Feature<MultiLineString | LineString, P> = all.get(id)
if (!oldV) { if (!oldV) {
all.set(id, { all.set(id, {
type: "Feature", type: "Feature",
@ -63,7 +63,13 @@ export class LineSourceMerger extends UpdatableDynamicTileSource<
}) })
continue 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)
}
}
} }
} }

View file

@ -2,11 +2,11 @@ import DynamicTileSource from "./DynamicTileSource"
import { ImmutableStore, Store } from "../../UIEventSource" import { ImmutableStore, Store } from "../../UIEventSource"
import { BBox } from "../../BBox" import { BBox } from "../../BBox"
import TileLocalStorage from "../Actors/TileLocalStorage" import TileLocalStorage from "../Actors/TileLocalStorage"
import { Feature } from "geojson" import { Feature, Geometry } from "geojson"
import StaticFeatureSource from "../Sources/StaticFeatureSource" import StaticFeatureSource from "../Sources/StaticFeatureSource"
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" 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( constructor(
backend: string, backend: string,
layer: LayerConfig, layer: LayerConfig,
@ -30,8 +30,8 @@ export default class LocalStorageFeatureSource extends DynamicTileSource {
new ImmutableStore(zoomlevel), new ImmutableStore(zoomlevel),
layer.minzoom, layer.minzoom,
(tileIndex) => (tileIndex) =>
new StaticFeatureSource( new StaticFeatureSource<T>(
storage.getTileSource(tileIndex).mapD((features) => { <Store<T[]>> storage.getTileSource(tileIndex).mapD((features) => {
if (features.length === undefined) { if (features.length === undefined) {
console.trace("These are not features:", features) console.trace("These are not features:", features)
storage.invalidate(tileIndex) storage.invalidate(tileIndex)

View file

@ -1,7 +1,7 @@
import { FeatureSourceForTile, UpdatableFeatureSource } from "../FeatureSource" import { FeatureSourceForTile, UpdatableFeatureSource } from "../FeatureSource"
import { Store } from "../../UIEventSource" import { Store } from "../../UIEventSource"
import { BBox } from "../../BBox" import { BBox } from "../../BBox"
import { Feature } from "geojson" import { Feature, Polygon } from "geojson"
import { GeoOperations } from "../../GeoOperations" import { GeoOperations } from "../../GeoOperations"
import { UpdatableDynamicTileSource } from "./DynamicTileSource" import { UpdatableDynamicTileSource } from "./DynamicTileSource"
import { Lists } from "../../../Utils/Lists" 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. * The PolygonSourceMerger receives various small pieces of bigger polygons and stitches them together.
* This is used to reconstruct polygons of vector tiles * This is used to reconstruct polygons of vector tiles
*/ */
export class PolygonSourceMerger extends UpdatableDynamicTileSource< export class PolygonSourceMerger<P extends Record<string, any> & { id: string },
FeatureSourceForTile & UpdatableFeatureSource F extends Feature<Polygon, P> = Feature<Polygon, P>> extends UpdatableDynamicTileSource<
F, FeatureSourceForTile<F> & UpdatableFeatureSource<F>
> { > {
constructor( constructor(
zoomlevel: Store<number>, zoomlevel: Store<number>,
minzoom: number, minzoom: number,
constructSource: (tileIndex: number) => FeatureSourceForTile & UpdatableFeatureSource, constructSource: (tileIndex: number) => FeatureSourceForTile<F> & UpdatableFeatureSource<F>,
mapProperties: { mapProperties: {
bounds: Store<BBox> bounds: Store<BBox>
zoom: Store<number> zoom: Store<number>
@ -28,9 +29,9 @@ export class PolygonSourceMerger extends UpdatableDynamicTileSource<
super(zoomlevel, minzoom, constructSource, mapProperties, options) super(zoomlevel, minzoom, constructSource, mapProperties, options)
} }
protected addDataFromSources(sources: FeatureSourceForTile[]) { protected addDataFromSources(sources: FeatureSourceForTile<F>[]) {
sources = Lists.noNull(sources) 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() const zooms: Map<string, number> = new Map()
for (const source of sources) { for (const source of sources) {
@ -60,7 +61,7 @@ export class PolygonSourceMerger extends UpdatableDynamicTileSource<
zooms.set(id, z) zooms.set(id, z)
continue continue
} }
const merged = GeoOperations.union(f, oldV) const merged = <F> GeoOperations.union(f, oldV)
merged.properties = oldV.properties merged.properties = oldV.properties
all.set(id, merged) all.set(id, merged)
zooms.set(id, z) zooms.set(id, z)

View file

@ -54,11 +54,11 @@ export class GeoOperations {
/** /**
* Create a union between two features * Create a union between two features
*/ */
public static union( public static union<P >(
f0: Feature<Polygon | MultiPolygon>, f0: Feature<Polygon | MultiPolygon>,
f1: Feature<Polygon | MultiPolygon> f1: Feature<Polygon | MultiPolygon>
): Feature<Polygon | MultiPolygon> | null { ): Feature<Polygon | MultiPolygon, P> | null {
return turf.union(turf.featureCollection([f0, f1])) return turf.union<P>(turf.featureCollection([f0, f1]))
} }
public static intersect( public static intersect(

View file

@ -12,6 +12,7 @@ import CreateNewWayAction from "./CreateNewWayAction"
import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig" import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig"
import FullNodeDatabaseSource from "../../FeatureSource/TiledFeatureSource/FullNodeDatabaseSource" import FullNodeDatabaseSource from "../../FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"
import { Position } from "geojson" import { Position } from "geojson"
import type { OsmFeature } from "../../../Models/OsmFeature"
export interface MergePointConfig { export interface MergePointConfig {
withinRangeOfM: number withinRangeOfM: number
@ -71,7 +72,7 @@ export default class CreateWayWithPointReuseAction
private readonly _state: { private readonly _state: {
theme: ThemeConfig theme: ThemeConfig
changes: Changes changes: Changes
indexedFeatures: IndexedFeatureSource indexedFeatures: IndexedFeatureSource<OsmFeature>
fullNodeDatabase?: FullNodeDatabaseSource fullNodeDatabase?: FullNodeDatabaseSource
} }
private readonly _config: MergePointConfig[] private readonly _config: MergePointConfig[]
@ -82,7 +83,7 @@ export default class CreateWayWithPointReuseAction
state: { state: {
theme: ThemeConfig theme: ThemeConfig
changes: Changes changes: Changes
indexedFeatures: IndexedFeatureSource indexedFeatures: IndexedFeatureSource<OsmFeature>
fullNodeDatabase?: FullNodeDatabaseSource fullNodeDatabase?: FullNodeDatabaseSource
}, },
config: MergePointConfig[] config: MergePointConfig[]
@ -199,7 +200,7 @@ export default class CreateWayWithPointReuseAction
} }
features.push(newGeometry) features.push(newGeometry)
} }
return StaticFeatureSource.fromGeojson(features) return new StaticFeatureSource(features)
} }
public async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> { public async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> {

View file

@ -14,6 +14,7 @@ import { OsmConnection } from "../OsmConnection"
import { Feature, Geometry, LineString, Point } from "geojson" import { Feature, Geometry, LineString, Point } from "geojson"
import FullNodeDatabaseSource from "../../FeatureSource/TiledFeatureSource/FullNodeDatabaseSource" import FullNodeDatabaseSource from "../../FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"
import { Lists } from "../../../Utils/Lists" import { Lists } from "../../../Utils/Lists"
import { OsmFeature } from "../../../Models/OsmFeature"
export default class ReplaceGeometryAction extends OsmChangeAction implements PreviewableAction { 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> { public async getPreview(): Promise<FeatureSource> {
const { closestIds, allNodesById, detachedNodes, reprojectedNodes } = const { closestIds, allNodesById, detachedNodes, reprojectedNodes } =
await this.GetClosestIds() 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) { if (this.identicalTo[i] !== undefined) {
return undefined return undefined
} }
if (newId === undefined) { if (newId === undefined) {
return { return <OsmFeature> {
type: "Feature", type: "Feature",
properties: { properties: {
newpoint: "yes", newpoint: "yes",
@ -127,7 +128,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction implements Pr
reprojectedNodes.forEach(({ newLat, newLon, nodeId }) => { reprojectedNodes.forEach(({ newLat, newLon, nodeId }) => {
const origNode = allNodesById.get(nodeId) const origNode = allNodesById.get(nodeId)
const feature: Feature<LineString> = { const feature: Feature<LineString, {id: string} & Record<string, any>> = {
type: "Feature", type: "Feature",
properties: { properties: {
move: "yes", move: "yes",
@ -149,7 +150,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction implements Pr
detachedNodes.forEach(({ reason }, id) => { detachedNodes.forEach(({ reason }, id) => {
const origNode = allNodesById.get(id) const origNode = allNodesById.get(id)
const feature: Feature<Point> = { const feature: OsmFeature & Feature<Point> = {
type: "Feature", type: "Feature",
properties: { properties: {
detach: "yes", detach: "yes",
@ -165,7 +166,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction implements Pr
preview.push(feature) preview.push(feature)
}) })
return StaticFeatureSource.fromGeojson(Lists.noNull(preview)) return new StaticFeatureSource(Lists.noNull(preview))
} }
/** /**

View file

@ -3,9 +3,9 @@ import { Utils } from "../../Utils"
import { ImmutableStore, Store } from "../UIEventSource" import { ImmutableStore, Store } from "../UIEventSource"
import { BBox } from "../BBox" import { BBox } from "../BBox"
import osmtogeojson from "osmtogeojson" import osmtogeojson from "osmtogeojson"
import { FeatureCollection, Geometry } from "geojson" import { Feature } from "geojson"
import { OsmTags } from "../../Models/OsmFeature"
;("use strict") ("use strict")
/** /**
* Interfaces overpass to get all the latest data * Interfaces overpass to get all the latest data
*/ */
@ -37,7 +37,7 @@ export class Overpass {
this._includeMeta = includeMeta 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 = const bbox =
"[bbox:" + "[bbox:" +
bounds.getSouth() + bounds.getSouth() +
@ -49,16 +49,16 @@ export class Overpass {
bounds.getEast() + bounds.getEast() +
"]" "]"
const query = this.buildScript(bbox) const query = this.buildScript(bbox)
return await this.ExecuteQuery(query) return await this.ExecuteQuery<T>(query)
} }
public buildUrl(query: string) { public buildUrl(query: string) {
return `${this._interpreterUrl}?data=${encodeURIComponent(query)}` return `${this._interpreterUrl}?data=${encodeURIComponent(query)}`
} }
private async ExecuteQuery( private async ExecuteQuery<T extends Feature>(
query: string query: string
): Promise<[FeatureCollection<Geometry, OsmTags>, Date]> { ): Promise<[{features: T[]}, Date]> {
const json = await Utils.downloadJson<{ const json = await Utils.downloadJson<{
elements: [] elements: []
remark remark
@ -73,7 +73,7 @@ export class Overpass {
console.warn("No features for", this.buildUrl(query)) 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) const osmTime = new Date(json.osm3s.timestamp_osm_base)
return [geojson, osmTime] return [geojson, osmTime]
} }

View file

@ -357,7 +357,7 @@ export default class ShowDataLayer {
public static showMultipleLayers( public static showMultipleLayers(
mlmap: UIEventSource<MlMap>, mlmap: UIEventSource<MlMap>,
features: FeatureSource, features: FeatureSource<Feature<Geometry, Record<string, any> & {id: string}>>,
layers: LayerConfig[], layers: LayerConfig[],
options?: Partial<Omit<ShowDataLayerOptions, "features" | "layer">> options?: Partial<Omit<ShowDataLayerOptions, "features" | "layer">>
) { ) {

View file

@ -11,6 +11,7 @@ import { IndexedFeatureSource } from "../../Logic/FeatureSource/FeatureSource"
import SvelteUIElement from "../Base/SvelteUIElement" import SvelteUIElement from "../Base/SvelteUIElement"
import { Feature } from "geojson" import { Feature } from "geojson"
import AutoApplyButton from "./AutoApplyButton.svelte" import AutoApplyButton from "./AutoApplyButton.svelte"
import { OsmFeature } from "../../Models/OsmFeature"
export abstract class AutoAction extends SpecialVisualization { export abstract class AutoAction extends SpecialVisualization {
supportsAutoAction: boolean supportsAutoAction: boolean
@ -20,7 +21,7 @@ export abstract class AutoAction extends SpecialVisualization {
state: { state: {
theme: ThemeConfig theme: ThemeConfig
changes: Changes changes: Changes
indexedFeatures: IndexedFeatureSource indexedFeatures: IndexedFeatureSource<OsmFeature>
}, },
tagSource: UIEventSource<Record<string, string>>, tagSource: UIEventSource<Record<string, string>>,
argument: string[] argument: string[]

View file

@ -14,6 +14,7 @@ import CreateMultiPolygonWithPointReuseAction from "../../../Logic/Osm/Actions/C
import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig" import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig"
import { Changes } from "../../../Logic/Osm/Changes" import { Changes } from "../../../Logic/Osm/Changes"
import FullNodeDatabaseSource from "../../../Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource" import FullNodeDatabaseSource from "../../../Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"
import { OsmFeature } from "../../../Models/OsmFeature"
export interface WayImportFlowArguments extends ImportFlowArguments { export interface WayImportFlowArguments extends ImportFlowArguments {
max_snap_distance: string max_snap_distance: string
@ -54,7 +55,7 @@ export default class WayImportFlowState extends ImportFlow<WayImportFlowArgument
state: { state: {
theme: ThemeConfig theme: ThemeConfig
changes: Changes changes: Changes
indexedFeatures: IndexedFeatureSource indexedFeatures: IndexedFeatureSource<OsmFeature>
fullNodeDatabase?: FullNodeDatabaseSource fullNodeDatabase?: FullNodeDatabaseSource
}, },
tagsToApply: Store<Tag[]>, tagsToApply: Store<Tag[]>,
@ -82,7 +83,7 @@ export default class WayImportFlowState extends ImportFlow<WayImportFlowArgument
const coors = feature.geometry.coordinates const coors = feature.geometry.coordinates
return new CreateWayWithPointReuseAction(tagsToApply.data, coors, state, mergeConfigs) return new CreateWayWithPointReuseAction(tagsToApply.data, coors, state, mergeConfigs)
} else { } else {
throw "Unsupported type" throw "Unsupported type: cannot import something of type "+feature.geometry["type"]+", only Polygon and LineString are supported"
} }
} }