Refactoring(maplibre): remove 'freshness' and 'name' from FeatureSource to simplify the code

This commit is contained in:
Pieter Vander Vennet 2023-03-23 01:42:47 +01:00
parent 1b3609b13f
commit 231d67361e
30 changed files with 161 additions and 269 deletions

View file

@ -13,15 +13,15 @@ import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import Constants from "../../Models/Constants" import Constants from "../../Models/Constants"
import TileFreshnessCalculator from "../FeatureSource/TileFreshnessCalculator" import TileFreshnessCalculator from "../FeatureSource/TileFreshnessCalculator"
import { Tiles } from "../../Models/TileRange" import { Tiles } from "../../Models/TileRange"
import { Feature } from "geojson"
export default class OverpassFeatureSource implements FeatureSource { export default class OverpassFeatureSource implements FeatureSource {
public readonly name = "OverpassFeatureSource" public readonly name = "OverpassFeatureSource"
/** /**
* The last loaded features of the geojson * The last loaded features, as geojson
*/ */
public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = public readonly features: UIEventSource<Feature[]> = new UIEventSource(undefined)
new UIEventSource<any[]>(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)
@ -243,7 +243,6 @@ export default class OverpassFeatureSource implements FeatureSource {
data.features.forEach((feature) => data.features.forEach((feature) =>
SimpleMetaTagger.objectMetaInfo.applyMetaTagsOnFeature( SimpleMetaTagger.objectMetaInfo.applyMetaTagsOnFeature(
feature, feature,
date,
undefined, undefined,
this.state this.state
) )

View file

@ -101,7 +101,7 @@ export default class MetaTagRecalculator {
} }
} }
public registerSource(source: FeatureSourceForLayer & Tiled, recalculateOnEveryChange = false) { public registerSource(source: FeatureSourceForLayer & Tiled) {
if (source === undefined) { if (source === undefined) {
return return
} }

View file

@ -1,20 +1,19 @@
import FeatureSource from "../FeatureSource" import FeatureSource from "../FeatureSource";
import { Store } from "../../UIEventSource" import { Store } from "../../UIEventSource";
import { ElementStorage } from "../../ElementStorage" import { ElementStorage } from "../../ElementStorage";
import { Feature } from "geojson";
/** /**
* Makes sure that every feature is added to the ElementsStorage, so that the tags-eventsource can be retrieved * Makes sure that every feature is added to the ElementsStorage, so that the tags-eventsource can be retrieved
*/ */
export default class RegisteringAllFromFeatureSourceActor { export default class RegisteringAllFromFeatureSourceActor {
public readonly features: Store<{ feature: any; freshness: Date }[]> public readonly features: Store<Feature[]>
public readonly name
constructor(source: FeatureSource, allElements: ElementStorage) { constructor(source: FeatureSource, allElements: ElementStorage) {
this.features = source.features this.features = source.features
this.name = "RegisteringSource of " + source.name
this.features.addCallbackAndRunD((features) => { this.features.addCallbackAndRunD((features) => {
for (const feature of features) { for (const feature of features) {
allElements.addOrGetElement(feature.feature) allElements.addOrGetElement(<any> feature)
} }
}) })
} }

View file

@ -7,6 +7,7 @@ import { BBox } from "../../BBox"
import SimpleFeatureSource from "../Sources/SimpleFeatureSource" import SimpleFeatureSource from "../Sources/SimpleFeatureSource"
import FilteredLayer from "../../../Models/FilteredLayer" import FilteredLayer from "../../../Models/FilteredLayer"
import Loc from "../../../Models/Loc" import Loc from "../../../Models/Loc"
import { Feature } from "geojson"
/*** /***
* Saves all the features that are passed in to localstorage, so they can be retrieved on the next run * Saves all the features that are passed in to localstorage, so they can be retrieved on the next run
@ -82,7 +83,7 @@ export default class SaveTileToLocalStorageActor {
continue continue
} }
loadedTiles.add(key) loadedTiles.add(key)
this.GetIdb(key).then((features: { feature: any; freshness: Date }[]) => { this.GetIdb(key).then((features: Feature[]) => {
if (features === undefined) { if (features === undefined) {
return return
} }
@ -90,7 +91,7 @@ export default class SaveTileToLocalStorageActor {
const src = new SimpleFeatureSource( const src = new SimpleFeatureSource(
self._flayer, self._flayer,
key, key,
new UIEventSource<{ feature: any; freshness: Date }[]>(features) new UIEventSource<Feature[]>(features)
) )
registerTile(src) registerTile(src)
}) })

View file

@ -136,7 +136,7 @@ export default class FeaturePipeline {
// Passthrough to passed function, except that it registers as well // Passthrough to passed function, except that it registers as well
handleFeatureSource(src) handleFeatureSource(src)
src.features.addCallbackAndRunD((fs) => { src.features.addCallbackAndRunD((fs) => {
fs.forEach((ff) => state.allElements.addOrGetElement(<any>ff.feature)) fs.forEach((ff) => state.allElements.addOrGetElement(<any>ff))
}) })
} }
@ -422,7 +422,7 @@ export default class FeaturePipeline {
} }
return TileHierarchyTools.getTiles(requestedHierarchy, bbox) return TileHierarchyTools.getTiles(requestedHierarchy, bbox)
.filter((featureSource) => featureSource.features?.data !== undefined) .filter((featureSource) => featureSource.features?.data !== undefined)
.map((featureSource) => featureSource.features.data.map((fs) => fs.feature)) .map((featureSource) => <OsmFeature[]>featureSource.features.data)
} }
public GetTilesPerLayerWithin( public GetTilesPerLayerWithin(
@ -639,10 +639,7 @@ export default class FeaturePipeline {
* Inject a new point * Inject a new point
*/ */
InjectNewPoint(geojson) { InjectNewPoint(geojson) {
this.newGeometryHandler.features.data.push({ this.newGeometryHandler.features.data.push(geojson)
feature: geojson,
freshness: new Date(),
})
this.newGeometryHandler.features.ping() this.newGeometryHandler.features.ping()
} }
} }

View file

@ -1,15 +1,10 @@
import { Store, UIEventSource } from "../UIEventSource" import { Store } from "../UIEventSource"
import FilteredLayer from "../../Models/FilteredLayer" import FilteredLayer from "../../Models/FilteredLayer"
import { BBox } from "../BBox" import { BBox } from "../BBox"
import { Feature, Geometry } from "@turf/turf" import { Feature } from "geojson"
import { OsmFeature } from "../../Models/OsmFeature"
export default interface FeatureSource { export default interface FeatureSource {
features: Store<{ feature: OsmFeature; freshness: Date }[]> features: Store<Feature[]>
/**
* Mainly used for debuging
*/
name: string
} }
export interface Tiled { export interface Tiled {

View file

@ -2,6 +2,7 @@ import FeatureSource, { FeatureSourceForLayer, Tiled } from "./FeatureSource"
import { Store } from "../UIEventSource" import { Store } from "../UIEventSource"
import FilteredLayer from "../../Models/FilteredLayer" import FilteredLayer from "../../Models/FilteredLayer"
import SimpleFeatureSource from "./Sources/SimpleFeatureSource" import SimpleFeatureSource from "./Sources/SimpleFeatureSource"
import { Feature } from "geojson"
/** /**
* In some rare cases, some elements are shown on multiple layers (when 'passthrough' is enabled) * In some rare cases, some elements are shown on multiple layers (when 'passthrough' is enabled)
@ -32,7 +33,7 @@ export default class PerLayerFeatureSourceSplitter {
// We try to figure out (for each feature) in which feature store it should be saved. // We try to figure out (for each feature) in which feature store it should be saved.
// Note that this splitter is only run when it is invoked by the overpass feature source, so we can't be sure in which layer it should go // Note that this splitter is only run when it is invoked by the overpass feature source, so we can't be sure in which layer it should go
const featuresPerLayer = new Map<string, { feature; freshness }[]>() const featuresPerLayer = new Map<string, Feature[]>()
const noLayerFound = [] const noLayerFound = []
for (const layer of layers.data) { for (const layer of layers.data) {
@ -42,7 +43,7 @@ export default class PerLayerFeatureSourceSplitter {
for (const f of features) { for (const f of features) {
let foundALayer = false let foundALayer = false
for (const layer of layers.data) { for (const layer of layers.data) {
if (layer.layerDef.source.osmTags.matchesProperties(f.feature.properties)) { if (layer.layerDef.source.osmTags.matchesProperties(f.properties)) {
// We have found our matching layer! // We have found our matching layer!
featuresPerLayer.get(layer.layerDef.id).push(f) featuresPerLayer.get(layer.layerDef.id).push(f)
foundALayer = true foundALayer = true

View file

@ -6,11 +6,11 @@ import { UIEventSource } from "../../UIEventSource"
import { FeatureSourceForLayer, IndexedFeatureSource } from "../FeatureSource" import { FeatureSourceForLayer, IndexedFeatureSource } from "../FeatureSource"
import FilteredLayer from "../../../Models/FilteredLayer" import FilteredLayer from "../../../Models/FilteredLayer"
import { ChangeDescription, ChangeDescriptionTools } from "../../Osm/Actions/ChangeDescription" import { ChangeDescription, ChangeDescriptionTools } from "../../Osm/Actions/ChangeDescription"
import { Feature } from "geojson"
export default class ChangeGeometryApplicator implements FeatureSourceForLayer { export default class ChangeGeometryApplicator implements FeatureSourceForLayer {
public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = public readonly features: UIEventSource<Feature[]> =
new UIEventSource<{ feature: any; freshness: Date }[]>([]) new UIEventSource<Feature[]>([])
public readonly name: string
public readonly layer: FilteredLayer public readonly layer: FilteredLayer
private readonly source: IndexedFeatureSource private readonly source: IndexedFeatureSource
private readonly changes: Changes private readonly changes: Changes
@ -20,8 +20,7 @@ export default class ChangeGeometryApplicator implements FeatureSourceForLayer {
this.changes = changes this.changes = changes
this.layer = source.layer this.layer = source.layer
this.name = "ChangesApplied(" + source.name + ")" this.features = new UIEventSource<Feature[]>(undefined)
this.features = new UIEventSource<{ feature: any; freshness: Date }[]>(undefined)
const self = this const self = this
source.features.addCallbackAndRunD((_) => self.update()) source.features.addCallbackAndRunD((_) => self.update())
@ -58,9 +57,9 @@ export default class ChangeGeometryApplicator implements FeatureSourceForLayer {
changesPerId.set(key, [ch]) changesPerId.set(key, [ch])
} }
} }
const newFeatures: { feature: any; freshness: Date }[] = [] const newFeatures: Feature[] = []
for (const feature of upstreamFeatures) { for (const feature of upstreamFeatures) {
const changesForFeature = changesPerId.get(feature.feature.properties.id) const changesForFeature = changesPerId.get(feature.properties.id)
if (changesForFeature === undefined) { if (changesForFeature === undefined) {
// No changes for this element // No changes for this element
newFeatures.push(feature) newFeatures.push(feature)
@ -73,7 +72,7 @@ export default class ChangeGeometryApplicator implements FeatureSourceForLayer {
} }
// We only apply the last change as that one'll have the latest geometry // We only apply the last change as that one'll have the latest geometry
const change = changesForFeature[changesForFeature.length - 1] const change = changesForFeature[changesForFeature.length - 1]
copy.feature.geometry = ChangeDescriptionTools.getGeojsonGeometry(change) copy.geometry = ChangeDescriptionTools.getGeojsonGeometry(change)
console.log( console.log(
"Applying a geometry change onto:", "Applying a geometry change onto:",
feature, feature,

View file

@ -1,16 +1,13 @@
import { UIEventSource } from "../../UIEventSource" import { UIEventSource } from "../../UIEventSource"
import FeatureSource, { FeatureSourceForLayer, IndexedFeatureSource, Tiled } from "../FeatureSource" import FeatureSource, { FeatureSourceForLayer, IndexedFeatureSource, Tiled } from "../FeatureSource"
import FilteredLayer from "../../../Models/FilteredLayer" import FilteredLayer from "../../../Models/FilteredLayer"
import { Tiles } from "../../../Models/TileRange"
import { BBox } from "../../BBox" import { BBox } from "../../BBox"
import { Feature } from "geojson"
export default class FeatureSourceMerger export default class FeatureSourceMerger
implements FeatureSourceForLayer, Tiled, IndexedFeatureSource implements FeatureSourceForLayer, Tiled, IndexedFeatureSource
{ {
public features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource< public features: UIEventSource<Feature[]> = new UIEventSource([])
{ feature: any; freshness: Date }[]
>([])
public readonly name
public readonly layer: FilteredLayer public readonly layer: FilteredLayer
public readonly tileIndex: number public readonly tileIndex: number
public readonly bbox: BBox public readonly bbox: BBox
@ -32,12 +29,6 @@ export default class FeatureSourceMerger
this.bbox = bbox this.bbox = bbox
this._sources = sources this._sources = sources
this.layer = layer this.layer = layer
this.name =
"FeatureSourceMerger(" +
layer.layerDef.id +
", " +
Tiles.tile_from_index(tileIndex).join(",") +
")"
const self = this const self = this
const handledSources = new Set<FeatureSource>() const handledSources = new Set<FeatureSource>()
@ -63,14 +54,11 @@ export default class FeatureSourceMerger
private Update() { private Update() {
let somethingChanged = false let somethingChanged = false
const all: Map<string, { feature: any; freshness: Date }> = new Map< const all: Map<string, Feature> = new Map()
string,
{ feature: any; freshness: Date }
>()
// 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 ?? []
for (const oldValue of oldValues) { for (const oldValue of oldValues) {
all.set(oldValue.feature.id, oldValue) all.set(oldValue.properties.id, oldValue)
} }
for (const source of this._sources.data) { for (const source of this._sources.data) {
@ -78,7 +66,7 @@ export default class FeatureSourceMerger
continue continue
} }
for (const f of source.features.data) { for (const f of source.features.data) {
const id = f.feature.properties.id const id = f.properties.id
if (!all.has(id)) { if (!all.has(id)) {
// This is a new feature // This is a new feature
somethingChanged = true somethingChanged = true
@ -89,13 +77,13 @@ export default class FeatureSourceMerger
// This value has been seen already, either in a previous run or by a previous datasource // This value has been seen already, either in a previous run or by a previous datasource
// Let's figure out if something changed // Let's figure out if something changed
const oldV = all.get(id) const oldV = all.get(id)
if (oldV.freshness < f.freshness) { if (oldV === f) {
// Jup, this feature is fresher continue
}
all.set(id, f) all.set(id, f)
somethingChanged = true somethingChanged = true
} }
} }
}
if (!somethingChanged) { if (!somethingChanged) {
// We don't bother triggering an update // We don't bother triggering an update

View file

@ -4,13 +4,10 @@ import { FeatureSourceForLayer, Tiled } from "../FeatureSource"
import { BBox } from "../../BBox" import { BBox } from "../../BBox"
import { ElementStorage } from "../../ElementStorage" import { ElementStorage } from "../../ElementStorage"
import { TagsFilter } from "../../Tags/TagsFilter" import { TagsFilter } from "../../Tags/TagsFilter"
import { OsmFeature } from "../../../Models/OsmFeature" import { Feature } from "geojson"
export default class FilteringFeatureSource implements FeatureSourceForLayer, Tiled { export default class FilteringFeatureSource implements FeatureSourceForLayer, Tiled {
public features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource< public features: UIEventSource<Feature[]> = new UIEventSource([])
{ feature: any; freshness: Date }[]
>([])
public readonly name
public readonly layer: FilteredLayer public readonly layer: FilteredLayer
public readonly tileIndex: number public readonly tileIndex: number
public readonly bbox: BBox public readonly bbox: BBox
@ -36,7 +33,6 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
upstream: FeatureSourceForLayer, upstream: FeatureSourceForLayer,
metataggingUpdated?: UIEventSource<any> metataggingUpdated?: UIEventSource<any>
) { ) {
this.name = "FilteringFeatureSource(" + upstream.name + ")"
this.tileIndex = tileIndex this.tileIndex = tileIndex
this.bbox = tileIndex === undefined ? undefined : BBox.fromTileIndex(tileIndex) this.bbox = tileIndex === undefined ? undefined : BBox.fromTileIndex(tileIndex)
this.upstream = upstream this.upstream = upstream
@ -73,15 +69,14 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
private update() { private update() {
const self = this const self = this
const layer = this.upstream.layer const layer = this.upstream.layer
const features: { feature: OsmFeature; freshness: Date }[] = const features: Feature[] = this.upstream.features.data ?? []
this.upstream.features.data ?? []
const includedFeatureIds = new Set<string>() const includedFeatureIds = new Set<string>()
const globalFilters = self.state.globalFilters?.data?.map((f) => f.filter) const globalFilters = self.state.globalFilters?.data?.map((f) => f.filter)
const newFeatures = (features ?? []).filter((f) => { const newFeatures = (features ?? []).filter((f) => {
self.registerCallback(f.feature) self.registerCallback(f)
const isShown: TagsFilter = layer.layerDef.isShown const isShown: TagsFilter = layer.layerDef.isShown
const tags = f.feature.properties const tags = f.properties
if (isShown !== undefined && !isShown.matchesProperties(tags)) { if (isShown !== undefined && !isShown.matchesProperties(tags)) {
return false return false
} }
@ -92,10 +87,7 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
const tagsFilter = Array.from(layer.appliedFilters?.data?.values() ?? []) const tagsFilter = Array.from(layer.appliedFilters?.data?.values() ?? [])
for (const filter of tagsFilter) { for (const filter of tagsFilter) {
const neededTags: TagsFilter = filter?.currentFilter const neededTags: TagsFilter = filter?.currentFilter
if ( if (neededTags !== undefined && !neededTags.matchesProperties(f.properties)) {
neededTags !== undefined &&
!neededTags.matchesProperties(f.feature.properties)
) {
// Hidden by the filter on the layer itself - we want to hide it no matter what // Hidden by the filter on the layer itself - we want to hide it no matter what
return false return false
} }
@ -103,16 +95,13 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
for (const filter of globalFilters ?? []) { for (const filter of globalFilters ?? []) {
const neededTags: TagsFilter = filter?.currentFilter const neededTags: TagsFilter = filter?.currentFilter
if ( if (neededTags !== undefined && !neededTags.matchesProperties(f.properties)) {
neededTags !== undefined &&
!neededTags.matchesProperties(f.feature.properties)
) {
// Hidden by the filter on the layer itself - we want to hide it no matter what // Hidden by the filter on the layer itself - we want to hide it no matter what
return false return false
} }
} }
includedFeatureIds.add(f.feature.properties.id) includedFeatureIds.add(f.properties.id)
return true return true
}) })

View file

@ -8,9 +8,10 @@ import { FeatureSourceForLayer, Tiled } from "../FeatureSource"
import { Tiles } from "../../../Models/TileRange" import { Tiles } from "../../../Models/TileRange"
import { BBox } from "../../BBox" import { BBox } from "../../BBox"
import { GeoOperations } from "../../GeoOperations" import { GeoOperations } from "../../GeoOperations"
import { Feature } from "geojson"
export default class GeoJsonSource implements FeatureSourceForLayer, Tiled { export default class GeoJsonSource implements FeatureSourceForLayer, Tiled {
public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> public readonly features: UIEventSource<Feature[]>
public readonly state = new UIEventSource<undefined | { error: string } | "loaded">(undefined) public readonly state = new UIEventSource<undefined | { error: string } | "loaded">(undefined)
public readonly name public readonly name
public readonly isOsmCache: boolean public readonly isOsmCache: boolean
@ -69,7 +70,7 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled {
this.name = "GeoJsonSource of " + url this.name = "GeoJsonSource of " + url
this.isOsmCache = flayer.layerDef.source.isOsmCacheLayer this.isOsmCache = flayer.layerDef.source.isOsmCacheLayer
this.features = new UIEventSource<{ feature: any; freshness: Date }[]>([]) this.features = new UIEventSource<Feature[]>([])
this.LoadJSONFrom(url) this.LoadJSONFrom(url)
} }
@ -110,7 +111,7 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled {
} }
const time = new Date() const time = new Date()
const newFeatures: { feature: any; freshness: Date }[] = [] const newFeatures: Feature[] = []
let i = 0 let i = 0
let skipped = 0 let skipped = 0
for (const feature of json.features) { for (const feature of json.features) {
@ -146,7 +147,7 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled {
freshness = new Date(props["_last_edit:timestamp"]) freshness = new Date(props["_last_edit:timestamp"])
} }
newFeatures.push({ feature: feature, freshness: freshness }) newFeatures.push(feature)
} }
if (newFeatures.length == 0) { if (newFeatures.length == 0) {

View file

@ -5,6 +5,7 @@ import { UIEventSource } from "../../UIEventSource"
import { ChangeDescription } from "../../Osm/Actions/ChangeDescription" import { ChangeDescription } from "../../Osm/Actions/ChangeDescription"
import { ElementStorage } from "../../ElementStorage" import { ElementStorage } from "../../ElementStorage"
import { OsmId, OsmTags } from "../../../Models/OsmFeature" import { OsmId, OsmTags } from "../../../Models/OsmFeature"
import { Feature } from "geojson"
export class NewGeometryFromChangesFeatureSource implements FeatureSource { export class NewGeometryFromChangesFeatureSource implements FeatureSource {
// This class name truly puts the 'Java' into 'Javascript' // This class name truly puts the 'Java' into 'Javascript'
@ -15,9 +16,7 @@ export class NewGeometryFromChangesFeatureSource implements FeatureSource {
* These elements are probably created by the 'SimpleAddUi' which generates a new point, but the import functionality might create a line or polygon too. * These elements are probably created by the 'SimpleAddUi' which generates a new point, but the import functionality might create a line or polygon too.
* Other sources of new points are e.g. imports from nodes * Other sources of new points are e.g. imports from nodes
*/ */
public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = public readonly features: UIEventSource<Feature[]> = new UIEventSource<Feature[]>([])
new UIEventSource<{ feature: any; freshness: Date }[]>([])
public readonly name: string = "newFeatures"
constructor(changes: Changes, allElementStorage: ElementStorage, backendUrl: string) { constructor(changes: Changes, allElementStorage: ElementStorage, backendUrl: string) {
const seenChanges = new Set<ChangeDescription>() const seenChanges = new Set<ChangeDescription>()
@ -29,15 +28,11 @@ export class NewGeometryFromChangesFeatureSource implements FeatureSource {
return return
} }
const now = new Date()
let somethingChanged = false let somethingChanged = false
function add(feature) { function add(feature) {
feature.id = feature.properties.id feature.id = feature.properties.id
features.push({ features.push(feature)
feature: feature,
freshness: now,
})
somethingChanged = true somethingChanged = true
} }
@ -71,7 +66,7 @@ export class NewGeometryFromChangesFeatureSource implements FeatureSource {
} }
const geojson = feat.asGeoJson() const geojson = feat.asGeoJson()
allElementStorage.addOrGetElement(geojson) allElementStorage.addOrGetElement(geojson)
self.features.data.push({ feature: geojson, freshness: new Date() }) self.features.data.push(geojson)
self.features.ping() self.features.ping()
}) })
continue continue

View file

@ -5,28 +5,25 @@
import FeatureSource, { Tiled } from "../FeatureSource" import FeatureSource, { Tiled } from "../FeatureSource"
import { Store, UIEventSource } from "../../UIEventSource" import { Store, UIEventSource } from "../../UIEventSource"
import { BBox } from "../../BBox" import { BBox } from "../../BBox"
import { Feature } from "geojson"
export default class RememberingSource implements FeatureSource, Tiled { export default class RememberingSource implements FeatureSource, Tiled {
public readonly features: Store<{ feature: any; freshness: Date }[]> public readonly features: Store<Feature[]>
public readonly name
public readonly tileIndex: number public readonly tileIndex: number
public readonly bbox: BBox public readonly bbox: BBox
constructor(source: FeatureSource & Tiled) { constructor(source: FeatureSource & Tiled) {
const self = this const self = this
this.name = "RememberingSource of " + source.name
this.tileIndex = source.tileIndex this.tileIndex = source.tileIndex
this.bbox = source.bbox this.bbox = source.bbox
const empty = [] const empty = []
const featureSource = new UIEventSource<{ feature: any; freshness: Date }[]>(empty) const featureSource = new UIEventSource<Feature[]>(empty)
this.features = featureSource this.features = featureSource
source.features.addCallbackAndRunD((features) => { source.features.addCallbackAndRunD((features) => {
const oldFeatures = self.features?.data ?? empty const oldFeatures = self.features?.data ?? empty
// Then new ids // Then new ids
const ids = new Set<string>( const ids = new Set<string>(features.map((f) => f.properties.id + f.geometry.type))
features.map((f) => f.feature.properties.id + f.feature.geometry.type)
)
// the old data // the old data
const oldData = oldFeatures.filter( const oldData = oldFeatures.filter(
(old) => !ids.has(old.feature.properties.id + old.feature.geometry.type) (old) => !ids.has(old.feature.properties.id + old.feature.geometry.type)

View file

@ -1,13 +1,12 @@
/**
* This feature source helps the ShowDataLayer class: it introduces the necessary extra features and indicates with what renderConfig it should be rendered.
*/
import { Store } from "../../UIEventSource" import { Store } from "../../UIEventSource"
import { GeoOperations } from "../../GeoOperations" import { GeoOperations } from "../../GeoOperations"
import FeatureSource from "../FeatureSource" import FeatureSource from "../FeatureSource"
import PointRenderingConfig from "../../../Models/ThemeConfig/PointRenderingConfig" import PointRenderingConfig from "../../../Models/ThemeConfig/PointRenderingConfig"
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
import LineRenderingConfig from "../../../Models/ThemeConfig/LineRenderingConfig" import LineRenderingConfig from "../../../Models/ThemeConfig/LineRenderingConfig"
/**
* This feature source helps the ShowDataLayer class: it introduces the necessary extra features and indicates with what renderConfig it should be rendered.
*/
export default class RenderingMultiPlexerFeatureSource { export default class RenderingMultiPlexerFeatureSource {
public readonly features: Store< public readonly features: Store<
(any & { (any & {
@ -64,8 +63,7 @@ export default class RenderingMultiPlexerFeatureSource {
withIndex.push(patched) withIndex.push(patched)
} }
for (const f of features) { for (const feat of features) {
const feat = f.feature
if (feat === undefined) { if (feat === undefined) {
continue continue
} }

View file

@ -2,23 +2,18 @@ import { UIEventSource } from "../../UIEventSource"
import FilteredLayer from "../../../Models/FilteredLayer" import FilteredLayer from "../../../Models/FilteredLayer"
import { FeatureSourceForLayer, Tiled } from "../FeatureSource" import { FeatureSourceForLayer, Tiled } from "../FeatureSource"
import { BBox } from "../../BBox" import { BBox } from "../../BBox"
import { Feature } from "geojson"
export default class SimpleFeatureSource implements FeatureSourceForLayer, Tiled { export default class SimpleFeatureSource implements FeatureSourceForLayer, Tiled {
public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> public readonly features: UIEventSource<Feature[]>
public readonly name: string = "SimpleFeatureSource"
public readonly layer: FilteredLayer public readonly layer: FilteredLayer
public readonly bbox: BBox = BBox.global public readonly bbox: BBox = BBox.global
public readonly tileIndex: number public readonly tileIndex: number
constructor( constructor(layer: FilteredLayer, tileIndex: number, featureSource?: UIEventSource<Feature[]>) {
layer: FilteredLayer,
tileIndex: number,
featureSource?: UIEventSource<{ feature: any; freshness: Date }[]>
) {
this.name = "SimpleFeatureSource(" + layer.layerDef.id + ")"
this.layer = layer this.layer = layer
this.tileIndex = tileIndex ?? 0 this.tileIndex = tileIndex ?? 0
this.bbox = BBox.fromTileIndex(this.tileIndex) this.bbox = BBox.fromTileIndex(this.tileIndex)
this.features = featureSource ?? new UIEventSource<{ feature: any; freshness: Date }[]>([]) this.features = featureSource ?? new UIEventSource<Feature[]>([])
} }
} }

View file

@ -8,63 +8,34 @@ import { Feature } from "geojson"
* A simple, read only feature store. * A simple, read only feature store.
*/ */
export default class StaticFeatureSource implements FeatureSource { export default class StaticFeatureSource implements FeatureSource {
public readonly features: Store<{ feature: any; freshness: Date }[]> public readonly features: Store<Feature[]>
public readonly name: string
constructor( constructor(
features: Store<{ feature: Feature; freshness: Date }[]>, features:
name = "StaticFeatureSource" | Store<Feature[]>
| Feature[]
| { features: Feature[] }
| { features: Store<Feature[]> }
) { ) {
if (features === undefined) { if (features === undefined) {
throw "Static feature source received undefined as source" throw "Static feature source received undefined as source"
} }
this.name = name let feats: Feature[] | Store<Feature[]>
this.features = features if (features["features"]) {
feats = features["features"]
} else {
feats = <Feature[] | Store<Feature[]>>features
} }
public static fromGeojsonAndDate( if (Array.isArray(feats)) {
features: { feature: Feature; freshness: Date }[], this.features = new ImmutableStore(feats)
name = "StaticFeatureSourceFromGeojsonAndDate" } else {
): StaticFeatureSource { this.features = feats
return new StaticFeatureSource(new ImmutableStore(features), name) }
} }
public static fromGeojson( public static fromGeojson(geojson: Feature[]): StaticFeatureSource {
geojson: Feature[], return new StaticFeatureSource(geojson)
name = "StaticFeatureSourceFromGeojson"
): StaticFeatureSource {
const now = new Date()
return StaticFeatureSource.fromGeojsonAndDate(
geojson.map((feature) => ({ feature, freshness: now })),
name
)
}
public static fromGeojsonStore(
geojson: Store<Feature[]>,
name = "StaticFeatureSourceFromGeojson"
): StaticFeatureSource {
const now = new Date()
const mapped: Store<{ feature: Feature; freshness: Date }[]> = geojson.map((features) =>
features.map((feature) => ({ feature, freshness: now }))
)
return new StaticFeatureSource(mapped, name)
}
static fromDateless(
featureSource: Store<{ feature: Feature }[]>,
name = "StaticFeatureSourceFromDateless"
) {
const now = new Date()
return new StaticFeatureSource(
featureSource.map((features) =>
features.map((feature) => ({
feature: feature.feature,
freshness: now,
}))
),
name
)
} }
} }
@ -76,11 +47,7 @@ export class TiledStaticFeatureSource
public readonly tileIndex: number public readonly tileIndex: number
public readonly layer: FilteredLayer public readonly layer: FilteredLayer
constructor( constructor(features: Store<Feature[]>, layer: FilteredLayer, tileIndex: number = 0) {
features: Store<{ feature: any; freshness: Date }[]>,
layer: FilteredLayer,
tileIndex: number = 0
) {
super(features) super(features)
this.tileIndex = tileIndex this.tileIndex = tileIndex
this.layer = layer this.layer = layer

View file

@ -53,11 +53,9 @@ export default class FullNodeDatabaseSource implements TileHierarchy<FeatureSour
src.ping() src.ping()
} }
} }
const now = new Date() const asGeojsonFeatures = Array.from(nodesById.values()).map((osmNode) =>
const asGeojsonFeatures = Array.from(nodesById.values()).map((osmNode) => ({ osmNode.asGeoJson()
feature: osmNode.asGeoJson(), )
freshness: now,
}))
const featureSource = new SimpleFeatureSource(this.layer, tileId) const featureSource = new SimpleFeatureSource(this.layer, tileId)
featureSource.features.setData(asGeojsonFeatures) featureSource.features.setData(asGeojsonFeatures)

View file

@ -175,7 +175,7 @@ export default class OsmFeatureSource {
new PerLayerFeatureSourceSplitter( new PerLayerFeatureSourceSplitter(
this.filteredLayers, this.filteredLayers,
this.handleTile, this.handleTile,
StaticFeatureSource.fromGeojson(geojson.features), new StaticFeatureSource(geojson.features),
{ {
tileIndex: index, tileIndex: index,
} }

View file

@ -4,6 +4,7 @@ import FilteredLayer from "../../../Models/FilteredLayer"
import TileHierarchy from "./TileHierarchy" import TileHierarchy from "./TileHierarchy"
import { Tiles } from "../../../Models/TileRange" import { Tiles } from "../../../Models/TileRange"
import { BBox } from "../../BBox" import { BBox } from "../../BBox"
import { Feature } from "geojson";
/** /**
* Contains all features in a tiled fashion. * Contains all features in a tiled fashion.
@ -29,7 +30,7 @@ export default class TiledFeatureSource
public readonly maxFeatureCount: number public readonly maxFeatureCount: number
public readonly name public readonly name
public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> public readonly features: UIEventSource<Feature[]>
public readonly containedIds: Store<Set<string>> public readonly containedIds: Store<Set<string>>
public readonly bbox: BBox public readonly bbox: BBox
@ -80,7 +81,7 @@ export default class TiledFeatureSource
if (features === undefined) { if (features === undefined) {
return undefined return undefined
} }
return new Set(features.map((f) => f.feature.properties.id)) return new Set(features.map((f) => f.properties.id))
}) })
// We register this tile, but only when there is some data in it // We register this tile, but only when there is some data in it
@ -132,7 +133,7 @@ export default class TiledFeatureSource
* @param features * @param features
* @private * @private
*/ */
private addFeatures(features: { feature: any; freshness: Date }[]) { private addFeatures(features: Feature[]) {
if (features === undefined || features.length === 0) { if (features === undefined || features.length === 0) {
return return
} }
@ -180,7 +181,7 @@ export default class TiledFeatureSource
const overlapsboundary = [] const overlapsboundary = []
for (const feature of features) { for (const feature of features) {
const bbox = BBox.get(feature.feature) const bbox = BBox.get(feature)
// There are a few strategies to deal with features that cross tile boundaries // There are a few strategies to deal with features that cross tile boundaries

View file

@ -2,6 +2,7 @@ import SimpleMetaTaggers, { SimpleMetaTagger } from "./SimpleMetaTagger"
import { ExtraFuncParams, ExtraFunctions } from "./ExtraFunctions" import { ExtraFuncParams, ExtraFunctions } from "./ExtraFunctions"
import LayerConfig from "../Models/ThemeConfig/LayerConfig" import LayerConfig from "../Models/ThemeConfig/LayerConfig"
import { ElementStorage } from "./ElementStorage" import { ElementStorage } from "./ElementStorage"
import { Feature } from "geojson"
/** /**
* Metatagging adds various tags to the elements, e.g. lat, lon, surface area, ... * Metatagging adds various tags to the elements, e.g. lat, lon, surface area, ...
@ -20,7 +21,7 @@ export default class MetaTagging {
* Returns true if at least one feature has changed properties * Returns true if at least one feature has changed properties
*/ */
public static addMetatags( public static addMetatags(
features: { feature: any; freshness: Date }[], features: Feature[],
params: ExtraFuncParams, params: ExtraFuncParams,
layer: LayerConfig, layer: LayerConfig,
state?: { allElements?: ElementStorage }, state?: { allElements?: ElementStorage },
@ -55,8 +56,7 @@ export default class MetaTagging {
for (let i = 0; i < features.length; i++) { for (let i = 0; i < features.length; i++) {
const ff = features[i] const ff = features[i]
const feature = ff.feature const feature = ff
const freshness = ff.freshness
let somethingChanged = false let somethingChanged = false
let definedTags = new Set(Object.getOwnPropertyNames(feature.properties)) let definedTags = new Set(Object.getOwnPropertyNames(feature.properties))
for (const metatag of metatagsToApply) { for (const metatag of metatagsToApply) {
@ -72,19 +72,14 @@ export default class MetaTagging {
continue continue
} }
somethingChanged = true somethingChanged = true
metatag.applyMetaTagsOnFeature(feature, freshness, layer, state) metatag.applyMetaTagsOnFeature(feature, layer, state)
if (options?.evaluateStrict) { if (options?.evaluateStrict) {
for (const key of metatag.keys) { for (const key of metatag.keys) {
feature.properties[key] feature.properties[key]
} }
} }
} else { } else {
const newValueAdded = metatag.applyMetaTagsOnFeature( const newValueAdded = metatag.applyMetaTagsOnFeature(feature, layer, state)
feature,
freshness,
layer,
state
)
/* Note that the expression: /* Note that the expression:
* `somethingChanged = newValueAdded || metatag.applyMetaTagsOnFeature(feature, freshness)` * `somethingChanged = newValueAdded || metatag.applyMetaTagsOnFeature(feature, freshness)`
* Is WRONG * Is WRONG

View file

@ -17,12 +17,7 @@ export class SimpleMetaTagger {
public readonly doc: string public readonly doc: string
public readonly isLazy: boolean public readonly isLazy: boolean
public readonly includesDates: boolean public readonly includesDates: boolean
public readonly applyMetaTagsOnFeature: ( public readonly applyMetaTagsOnFeature: (feature: any, layer: LayerConfig, state) => boolean
feature: any,
freshness: Date,
layer: LayerConfig,
state
) => boolean
/*** /***
* A function that adds some extra data to a feature * A function that adds some extra data to a feature
@ -41,7 +36,7 @@ export class SimpleMetaTagger {
isLazy?: boolean isLazy?: boolean
cleanupRetagger?: boolean cleanupRetagger?: boolean
}, },
f: (feature: any, freshness: Date, layer: LayerConfig, state) => boolean f: (feature: any, layer: LayerConfig, state) => boolean
) { ) {
this.keys = docs.keys this.keys = docs.keys
this.doc = docs.doc this.doc = docs.doc
@ -71,7 +66,7 @@ export class ReferencingWaysMetaTagger extends SimpleMetaTagger {
isLazy: true, isLazy: true,
doc: "_referencing_ways contains - for a node - which ways use this this node as point in their geometry. ", doc: "_referencing_ways contains - for a node - which ways use this this node as point in their geometry. ",
}, },
(feature, _, __, state) => { (feature, _, state) => {
if (!ReferencingWaysMetaTagger.enabled) { if (!ReferencingWaysMetaTagger.enabled) {
return false return false
} }
@ -116,7 +111,7 @@ export class CountryTagger extends SimpleMetaTagger {
doc: "The country code of the property (with latlon2country)", doc: "The country code of the property (with latlon2country)",
includesDates: false, includesDates: false,
}, },
(feature, _, __, state) => { (feature, _, state) => {
let centerPoint: any = GeoOperations.centerpoint(feature) let centerPoint: any = GeoOperations.centerpoint(feature)
const lat = centerPoint.geometry.coordinates[1] const lat = centerPoint.geometry.coordinates[1]
const lon = centerPoint.geometry.coordinates[0] const lon = centerPoint.geometry.coordinates[0]
@ -236,7 +231,7 @@ export default class SimpleMetaTaggers {
keys: ["_layer"], keys: ["_layer"],
includesDates: false, includesDates: false,
}, },
(feature, freshness, layer) => { (feature, _, layer) => {
if (feature.properties._layer === layer.id) { if (feature.properties._layer === layer.id) {
return false return false
} }
@ -322,7 +317,7 @@ export default class SimpleMetaTaggers {
doc: "If 'units' is defined in the layoutConfig, then this metatagger will rewrite the specified keys to have the canonical form (e.g. `1meter` will be rewritten to `1m`; `1` will be rewritten to `1m` as well)", doc: "If 'units' is defined in the layoutConfig, then this metatagger will rewrite the specified keys to have the canonical form (e.g. `1meter` will be rewritten to `1m`; `1` will be rewritten to `1m` as well)",
keys: ["Theme-defined keys"], keys: ["Theme-defined keys"],
}, },
(feature, _, __, state) => { (feature, _, state) => {
const units = Utils.NoNull( const units = Utils.NoNull(
[].concat(...(state?.layoutToUse?.layers?.map((layer) => layer.units) ?? [])) [].concat(...(state?.layoutToUse?.layers?.map((layer) => layer.units) ?? []))
) )
@ -395,7 +390,7 @@ export default class SimpleMetaTaggers {
includesDates: true, includesDates: true,
isLazy: true, isLazy: true,
}, },
(feature, _, __, state) => { (feature, _, state) => {
if (Utils.runningFromConsole) { if (Utils.runningFromConsole) {
// We are running from console, thus probably creating a cache // We are running from console, thus probably creating a cache
// isOpen is irrelevant // isOpen is irrelevant
@ -508,17 +503,13 @@ export default class SimpleMetaTaggers {
private static currentTime = new SimpleMetaTagger( private static currentTime = new SimpleMetaTagger(
{ {
keys: ["_now:date", "_now:datetime", "_loaded:date", "_loaded:_datetime"], keys: ["_now:date", "_now:datetime"],
doc: "Adds the time that the data got loaded - pretty much the time of downloading from overpass. The format is YYYY-MM-DD hh:mm, aka 'sortable' aka ISO-8601-but-not-entirely", doc: "Adds the time that the data got loaded - pretty much the time of downloading from overpass. The format is YYYY-MM-DD hh:mm, aka 'sortable' aka ISO-8601-but-not-entirely",
includesDates: true, includesDates: true,
}, },
(feature, freshness) => { (feature) => {
const now = new Date() const now = new Date()
if (typeof freshness === "string") {
freshness = new Date(freshness)
}
function date(d: Date) { function date(d: Date) {
return d.toISOString().slice(0, 10) return d.toISOString().slice(0, 10)
} }
@ -529,8 +520,6 @@ export default class SimpleMetaTaggers {
feature.properties["_now:date"] = date(now) feature.properties["_now:date"] = date(now)
feature.properties["_now:datetime"] = datetime(now) feature.properties["_now:datetime"] = datetime(now)
feature.properties["_loaded:date"] = date(freshness)
feature.properties["_loaded:datetime"] = datetime(freshness)
return true return true
} }
) )

View file

@ -57,7 +57,7 @@ export default class FeaturePipelineState extends MapState {
function registerSource(source: FeatureSourceForLayer & Tiled) { function registerSource(source: FeatureSourceForLayer & Tiled) {
clusterCounter.addTile(source) clusterCounter.addTile(source)
const sourceBBox = source.features.map((allFeatures) => const sourceBBox = source.features.map((allFeatures) =>
BBox.bboxAroundAll(allFeatures.map((f) => BBox.get(f.feature))) BBox.bboxAroundAll(allFeatures.map(BBox.get))
) )
// Do show features indicates if the respective 'showDataLayer' should be shown. It can be hidden by e.g. clustering // Do show features indicates if the respective 'showDataLayer' should be shown. It can be hidden by e.g. clustering
@ -131,7 +131,7 @@ export default class FeaturePipelineState extends MapState {
handleRawFeatureSource: registerRaw, handleRawFeatureSource: registerRaw,
}) })
this.metatagRecalculator = new MetaTagRecalculator(this, this.featurePipeline) this.metatagRecalculator = new MetaTagRecalculator(this, this.featurePipeline)
this.metatagRecalculator.registerSource(this.currentView, true) this.metatagRecalculator.registerSource(this.currentView)
sourcesToRegister.forEach((source) => self.metatagRecalculator.registerSource(source)) sourcesToRegister.forEach((source) => self.metatagRecalculator.registerSource(source))

View file

@ -213,15 +213,12 @@ export default class MapState extends UserRelatedState {
let i = 0 let i = 0
const self = this const self = this
const features: Store<{ feature: any; freshness: Date }[]> = this.currentBounds.map( const features: Store<Feature[]> = this.currentBounds.map((bounds) => {
(bounds) => {
if (bounds === undefined) { if (bounds === undefined) {
return [] return []
} }
i++ i++
const feature = { const feature = {
freshness: new Date(),
feature: {
type: "Feature", type: "Feature",
properties: { properties: {
id: "current_view-" + i, id: "current_view-" + i,
@ -240,11 +237,9 @@ export default class MapState extends UserRelatedState {
], ],
], ],
}, },
},
} }
return [feature] return [feature]
} })
)
this.currentView = new TiledStaticFeatureSource(features, currentViewLayer) this.currentView = new TiledStaticFeatureSource(features, currentViewLayer)
} }

View file

@ -1,4 +1,4 @@
import { Feature, Geometry } from "@turf/turf" import { Feature, Geometry } from "geojson"
export type RelationId = `relation/${number}` export type RelationId = `relation/${number}`
export type WayId = `way/${number}` export type WayId = `way/${number}`

View file

@ -106,7 +106,7 @@ export class CompareToAlreadyExistingNotes
state, state,
zoomToFeatures: true, zoomToFeatures: true,
leafletMap: comparisonMap.leafletMap, leafletMap: comparisonMap.leafletMap,
features: StaticFeatureSource.fromGeojsonStore( features: new StaticFeatureSource(
partitionedImportPoints.map((p) => <Feature[]>p.hasNearby) partitionedImportPoints.map((p) => <Feature[]>p.hasNearby)
), ),
popup: (tags, layer) => new FeatureInfoBox(tags, layer, state), popup: (tags, layer) => new FeatureInfoBox(tags, layer, state),

View file

@ -166,7 +166,7 @@ export default class ConflationChecker
[osmLiveData.bounds, zoomLevel.GetValue()] [osmLiveData.bounds, zoomLevel.GetValue()]
) )
const preview = StaticFeatureSource.fromGeojsonStore(geojsonFeatures) const preview = new StaticFeatureSource(geojsonFeatures)
new ShowDataLayer({ new ShowDataLayer({
layerToShow: new LayerConfig(currentview), layerToShow: new LayerConfig(currentview),
@ -225,7 +225,7 @@ export default class ConflationChecker
}, },
[nearbyCutoff.GetValue().stabilized(500)] [nearbyCutoff.GetValue().stabilized(500)]
) )
const nearbyFeatures = StaticFeatureSource.fromGeojsonStore(geojsonMapped) const nearbyFeatures = new StaticFeatureSource(geojsonMapped)
const paritionedImport = ImportUtils.partitionFeaturesIfNearby( const paritionedImport = ImportUtils.partitionFeaturesIfNearby(
toImport, toImport,
geojson, geojson,
@ -233,7 +233,7 @@ export default class ConflationChecker
) )
// Featuresource showing OSM-features which are nearby a toImport-feature // Featuresource showing OSM-features which are nearby a toImport-feature
const toImportWithNearby = StaticFeatureSource.fromGeojsonStore( const toImportWithNearby = new StaticFeatureSource(
paritionedImport.map((els) => <Feature[]>els?.hasNearby ?? []) paritionedImport.map((els) => <Feature[]>els?.hasNearby ?? [])
) )
toImportWithNearby.features.addCallback((nearby) => toImportWithNearby.features.addCallback((nearby) =>

View file

@ -8,8 +8,6 @@ import Constants from "../../Models/Constants"
import { DropDown } from "../Input/DropDown" import { DropDown } from "../Input/DropDown"
import { Utils } from "../../Utils" import { Utils } from "../../Utils"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig" import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import BaseLayer from "../../Models/BaseLayer"
import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"
import Loc from "../../Models/Loc" import Loc from "../../Models/Loc"
import Minimap from "../Base/Minimap" import Minimap from "../Base/Minimap"
import Attribution from "../BigComponents/Attribution" import Attribution from "../BigComponents/Attribution"
@ -140,9 +138,7 @@ export class MapPreview
new ShowDataLayer({ new ShowDataLayer({
layerToShow, layerToShow,
zoomToFeatures: true, zoomToFeatures: true,
features: StaticFeatureSource.fromDateless( features: new StaticFeatureSource(matching),
matching.map((features) => features.map((feature) => ({ feature })))
),
leafletMap: map.leafletMap, leafletMap: map.leafletMap,
popup: (tag) => new PreviewPanel(tag), popup: (tag) => new PreviewPanel(tag),
}) })

View file

@ -53,7 +53,7 @@ export default class LocationInput
* Used for rendering * Used for rendering
* @private * @private
*/ */
private readonly _snapToRaw: Store<{ feature: Feature }[]> private readonly _snapToRaw: Store<Feature[]>
private readonly _value: Store<Loc> private readonly _value: Store<Loc>
private readonly _snappedPoint: Store<any> private readonly _snappedPoint: Store<any>
private readonly _maxSnapDistance: number private readonly _maxSnapDistance: number
@ -112,7 +112,7 @@ export default class LocationInput
constructor(options?: { constructor(options?: {
minZoom?: number minZoom?: number
mapBackground?: UIEventSource<BaseLayer> mapBackground?: UIEventSource<BaseLayer>
snapTo?: UIEventSource<{ feature: Feature }[]> snapTo?: UIEventSource<Feature[]>
renderLayerForSnappedPoint?: LayerConfig renderLayerForSnappedPoint?: LayerConfig
maxSnapDistance?: number maxSnapDistance?: number
snappedPointTags?: any snappedPointTags?: any
@ -276,7 +276,7 @@ export default class LocationInput
if (this._snapToRaw !== undefined) { if (this._snapToRaw !== undefined) {
// Show the lines to snap to // Show the lines to snap to
new ShowDataMultiLayer({ new ShowDataMultiLayer({
features: StaticFeatureSource.fromDateless(this._snapToRaw), features: new StaticFeatureSource(this._snapToRaw),
zoomToFeatures: false, zoomToFeatures: false,
leafletMap: this.map.leafletMap, leafletMap: this.map.leafletMap,
layers: this._state.filteredLayers, layers: this._state.filteredLayers,
@ -286,12 +286,12 @@ export default class LocationInput
if (loc === undefined) { if (loc === undefined) {
return [] return []
} }
return [{ feature: loc }] return [loc]
}) })
// The 'matchlayer' is the layer which shows the target location // The 'matchlayer' is the layer which shows the target location
new ShowDataLayer({ new ShowDataLayer({
features: StaticFeatureSource.fromDateless(matchPoint), features: new StaticFeatureSource(matchPoint),
zoomToFeatures: false, zoomToFeatures: false,
leafletMap: this.map.leafletMap, leafletMap: this.map.leafletMap,
layerToShow: this._matching_layer, layerToShow: this._matching_layer,

View file

@ -3,7 +3,6 @@ import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline" import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline"
import BaseUIElement from "../BaseUIElement" import BaseUIElement from "../BaseUIElement"
import LocationInput from "../Input/LocationInput" import LocationInput from "../Input/LocationInput"
import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"
import { BBox } from "../../Logic/BBox" import { BBox } from "../../Logic/BBox"
import { TagUtils } from "../../Logic/Tags/TagUtils" import { TagUtils } from "../../Logic/Tags/TagUtils"
import { SubtleButton } from "../Base/SubtleButton" import { SubtleButton } from "../Base/SubtleButton"
@ -12,7 +11,6 @@ import Translations from "../i18n/Translations"
import Svg from "../../Svg" import Svg from "../../Svg"
import Toggle from "../Input/Toggle" import Toggle from "../Input/Toggle"
import SimpleAddUI, { PresetInfo } from "../BigComponents/SimpleAddUI" import SimpleAddUI, { PresetInfo } from "../BigComponents/SimpleAddUI"
import BaseLayer from "../../Models/BaseLayer"
import Img from "../Base/Img" import Img from "../Base/Img"
import Title from "../Base/Title" import Title from "../Base/Title"
import { GlobalFilter } from "../../Logic/State/MapState" import { GlobalFilter } from "../../Logic/State/MapState"
@ -20,6 +18,8 @@ import { VariableUiElement } from "../Base/VariableUIElement"
import { Tag } from "../../Logic/Tags/Tag" import { Tag } from "../../Logic/Tags/Tag"
import { WayId } from "../../Models/OsmFeature" import { WayId } from "../../Models/OsmFeature"
import { Translation } from "../i18n/Translation" import { Translation } from "../i18n/Translation"
import { Feature } from "geojson";
import { AvailableRasterLayers, RasterLayerPolygon } from "../../Models/RasterLayers";
export default class ConfirmLocationOfPoint extends Combine { export default class ConfirmLocationOfPoint extends Combine {
constructor( constructor(
@ -28,7 +28,7 @@ export default class ConfirmLocationOfPoint extends Combine {
featureSwitchIsTesting: UIEventSource<boolean> featureSwitchIsTesting: UIEventSource<boolean>
osmConnection: OsmConnection osmConnection: OsmConnection
featurePipeline: FeaturePipeline featurePipeline: FeaturePipeline
backgroundLayer?: UIEventSource<BaseLayer> backgroundLayer?: UIEventSource<RasterLayerPolygon | undefined>
}, },
filterViewIsOpened: UIEventSource<boolean>, filterViewIsOpened: UIEventSource<boolean>,
preset: PresetInfo, preset: PresetInfo,
@ -55,10 +55,10 @@ export default class ConfirmLocationOfPoint extends Combine {
const locationSrc = new UIEventSource(zloc) const locationSrc = new UIEventSource(zloc)
let backgroundLayer = new UIEventSource( let backgroundLayer = new UIEventSource(
state?.backgroundLayer?.data ?? AvailableBaseLayers.osmCarto state?.backgroundLayer?.data ?? AvailableRasterLayers.osmCarto
) )
if (preset.preciseInput.preferredBackground) { if (preset.preciseInput.preferredBackground) {
const defaultBackground = AvailableBaseLayers.SelectBestLayerAccordingTo( const defaultBackground = AvailableRasterLayers.SelectBestLayerAccordingTo(
locationSrc, locationSrc,
new UIEventSource<string | string[]>(preset.preciseInput.preferredBackground) new UIEventSource<string | string[]>(preset.preciseInput.preferredBackground)
) )
@ -66,10 +66,10 @@ export default class ConfirmLocationOfPoint extends Combine {
backgroundLayer.setData(defaultBackground.data) backgroundLayer.setData(defaultBackground.data)
} }
let snapToFeatures: UIEventSource<{ feature: any }[]> = undefined let snapToFeatures: UIEventSource<Feature[]> = undefined
let mapBounds: UIEventSource<BBox> = undefined let mapBounds: UIEventSource<BBox> = undefined
if (preset.preciseInput.snapToLayers && preset.preciseInput.snapToLayers.length > 0) { if (preset.preciseInput.snapToLayers && preset.preciseInput.snapToLayers.length > 0) {
snapToFeatures = new UIEventSource<{ feature: any }[]>([]) snapToFeatures = new UIEventSource< Feature[]>([])
mapBounds = new UIEventSource<BBox>(undefined) mapBounds = new UIEventSource<BBox>(undefined)
} }
@ -105,13 +105,13 @@ export default class ConfirmLocationOfPoint extends Combine {
Math.max(preset.boundsFactor ?? 0.25, 2) Math.max(preset.boundsFactor ?? 0.25, 2)
) )
loadedBbox = bbox loadedBbox = bbox
const allFeatures: { feature: any }[] = [] const allFeatures: Feature[] = []
preset.preciseInput.snapToLayers.forEach((layerId) => { preset.preciseInput.snapToLayers.forEach((layerId) => {
console.log("Snapping to", layerId) console.log("Snapping to", layerId)
state.featurePipeline state.featurePipeline
.GetFeaturesWithin(layerId, bbox) .GetFeaturesWithin(layerId, bbox)
?.forEach((feats) => ?.forEach((feats) =>
allFeatures.push(...feats.map((f) => ({ feature: f }))) allFeatures.push(...<any[]>feats)
) )
}) })
console.log("Snapping to", allFeatures) console.log("Snapping to", allFeatures)

View file

@ -1,20 +1,17 @@
import SvelteUIElement from "./UI/Base/SvelteUIElement" import SvelteUIElement from "./UI/Base/SvelteUIElement"
import MaplibreMap from "./UI/Map/MaplibreMap.svelte" import MaplibreMap from "./UI/Map/MaplibreMap.svelte"
import { ImmutableStore, UIEventSource } from "./Logic/UIEventSource" import { UIEventSource } from "./Logic/UIEventSource"
import { MapLibreAdaptor } from "./UI/Map/MapLibreAdaptor" import { MapLibreAdaptor } from "./UI/Map/MapLibreAdaptor"
import { AvailableRasterLayers, RasterLayerPolygon } from "./Models/RasterLayers" import { AvailableRasterLayers, RasterLayerPolygon } from "./Models/RasterLayers"
import type { Map as MlMap } from "maplibre-gl" import type { Map as MlMap } from "maplibre-gl"
import RasterLayerPicker from "./UI/Map/RasterLayerPicker.svelte"
import BackgroundLayerResetter from "./Logic/Actors/BackgroundLayerResetter"
import { ShowDataLayer } from "./UI/Map/ShowDataLayer" import { ShowDataLayer } from "./UI/Map/ShowDataLayer"
import StaticFeatureSource from "./Logic/FeatureSource/Sources/StaticFeatureSource"
import { Layer } from "leaflet"
import LayerConfig from "./Models/ThemeConfig/LayerConfig" import LayerConfig from "./Models/ThemeConfig/LayerConfig"
import * as bench from "./assets/generated/layers/bench.json" import * as bench from "./assets/generated/layers/bench.json"
import { Utils } from "./Utils" import { Utils } from "./Utils"
import SimpleFeatureSource from "./Logic/FeatureSource/Sources/SimpleFeatureSource" import SimpleFeatureSource from "./Logic/FeatureSource/Sources/SimpleFeatureSource"
import { FilterState } from "./Models/FilteredLayer" import { FilterState } from "./Models/FilteredLayer"
import { FixedUiElement } from "./UI/Base/FixedUiElement" import { FixedUiElement } from "./UI/Base/FixedUiElement"
async function main() { async function main() {
const mlmap = new UIEventSource<MlMap>(undefined) const mlmap = new UIEventSource<MlMap>(undefined)
const location = new UIEventSource<{ lon: number; lat: number }>({ const location = new UIEventSource<{ lon: number; lat: number }>({