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

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

View file

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

View file

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

View file

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

View file

@ -5,6 +5,7 @@ import { UIEventSource } from "../../UIEventSource"
import { ChangeDescription } from "../../Osm/Actions/ChangeDescription"
import { ElementStorage } from "../../ElementStorage"
import { OsmId, OsmTags } from "../../../Models/OsmFeature"
import { Feature } from "geojson"
export class NewGeometryFromChangesFeatureSource implements FeatureSource {
// 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.
* Other sources of new points are e.g. imports from nodes
*/
public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> =
new UIEventSource<{ feature: any; freshness: Date }[]>([])
public readonly name: string = "newFeatures"
public readonly features: UIEventSource<Feature[]> = new UIEventSource<Feature[]>([])
constructor(changes: Changes, allElementStorage: ElementStorage, backendUrl: string) {
const seenChanges = new Set<ChangeDescription>()
@ -29,15 +28,11 @@ export class NewGeometryFromChangesFeatureSource implements FeatureSource {
return
}
const now = new Date()
let somethingChanged = false
function add(feature) {
feature.id = feature.properties.id
features.push({
feature: feature,
freshness: now,
})
features.push(feature)
somethingChanged = true
}
@ -71,7 +66,7 @@ export class NewGeometryFromChangesFeatureSource implements FeatureSource {
}
const geojson = feat.asGeoJson()
allElementStorage.addOrGetElement(geojson)
self.features.data.push({ feature: geojson, freshness: new Date() })
self.features.data.push(geojson)
self.features.ping()
})
continue

View file

@ -5,28 +5,25 @@
import FeatureSource, { Tiled } from "../FeatureSource"
import { Store, UIEventSource } from "../../UIEventSource"
import { BBox } from "../../BBox"
import { Feature } from "geojson"
export default class RememberingSource implements FeatureSource, Tiled {
public readonly features: Store<{ feature: any; freshness: Date }[]>
public readonly name
public readonly features: Store<Feature[]>
public readonly tileIndex: number
public readonly bbox: BBox
constructor(source: FeatureSource & Tiled) {
const self = this
this.name = "RememberingSource of " + source.name
this.tileIndex = source.tileIndex
this.bbox = source.bbox
const empty = []
const featureSource = new UIEventSource<{ feature: any; freshness: Date }[]>(empty)
const featureSource = new UIEventSource<Feature[]>(empty)
this.features = featureSource
source.features.addCallbackAndRunD((features) => {
const oldFeatures = self.features?.data ?? empty
// Then new ids
const ids = new Set<string>(
features.map((f) => f.feature.properties.id + f.feature.geometry.type)
)
const ids = new Set<string>(features.map((f) => f.properties.id + f.geometry.type))
// the old data
const oldData = oldFeatures.filter(
(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 { GeoOperations } from "../../GeoOperations"
import FeatureSource from "../FeatureSource"
import PointRenderingConfig from "../../../Models/ThemeConfig/PointRenderingConfig"
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
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 {
public readonly features: Store<
(any & {
@ -64,8 +63,7 @@ export default class RenderingMultiPlexerFeatureSource {
withIndex.push(patched)
}
for (const f of features) {
const feat = f.feature
for (const feat of features) {
if (feat === undefined) {
continue
}

View file

@ -2,23 +2,18 @@ import { UIEventSource } from "../../UIEventSource"
import FilteredLayer from "../../../Models/FilteredLayer"
import { FeatureSourceForLayer, Tiled } from "../FeatureSource"
import { BBox } from "../../BBox"
import { Feature } from "geojson"
export default class SimpleFeatureSource implements FeatureSourceForLayer, Tiled {
public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>
public readonly name: string = "SimpleFeatureSource"
public readonly features: UIEventSource<Feature[]>
public readonly layer: FilteredLayer
public readonly bbox: BBox = BBox.global
public readonly tileIndex: number
constructor(
layer: FilteredLayer,
tileIndex: number,
featureSource?: UIEventSource<{ feature: any; freshness: Date }[]>
) {
this.name = "SimpleFeatureSource(" + layer.layerDef.id + ")"
constructor(layer: FilteredLayer, tileIndex: number, featureSource?: UIEventSource<Feature[]>) {
this.layer = layer
this.tileIndex = tileIndex ?? 0
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.
*/
export default class StaticFeatureSource implements FeatureSource {
public readonly features: Store<{ feature: any; freshness: Date }[]>
public readonly name: string
public readonly features: Store<Feature[]>
constructor(
features: Store<{ feature: Feature; freshness: Date }[]>,
name = "StaticFeatureSource"
features:
| Store<Feature[]>
| Feature[]
| { features: Feature[] }
| { features: Store<Feature[]> }
) {
if (features === undefined) {
throw "Static feature source received undefined as source"
}
this.name = name
this.features = features
let feats: Feature[] | Store<Feature[]>
if (features["features"]) {
feats = features["features"]
} else {
feats = <Feature[] | Store<Feature[]>>features
}
if (Array.isArray(feats)) {
this.features = new ImmutableStore(feats)
} else {
this.features = feats
}
}
public static fromGeojsonAndDate(
features: { feature: Feature; freshness: Date }[],
name = "StaticFeatureSourceFromGeojsonAndDate"
): StaticFeatureSource {
return new StaticFeatureSource(new ImmutableStore(features), name)
}
public static fromGeojson(
geojson: Feature[],
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
)
public static fromGeojson(geojson: Feature[]): StaticFeatureSource {
return new StaticFeatureSource(geojson)
}
}
@ -76,11 +47,7 @@ export class TiledStaticFeatureSource
public readonly tileIndex: number
public readonly layer: FilteredLayer
constructor(
features: Store<{ feature: any; freshness: Date }[]>,
layer: FilteredLayer,
tileIndex: number = 0
) {
constructor(features: Store<Feature[]>, layer: FilteredLayer, tileIndex: number = 0) {
super(features)
this.tileIndex = tileIndex
this.layer = layer