mapcomplete/Logic/FeatureSource/Actors/FeaturePropertiesStore.ts

112 lines
3.6 KiB
TypeScript

import { FeatureSource } from "../FeatureSource"
import { UIEventSource } from "../../UIEventSource"
/**
* Constructs a UIEventStore for the properties of every Feature, indexed by id
*/
export default class FeaturePropertiesStore {
private readonly _elements = new Map<string, UIEventSource<Record<string, string>>>()
constructor(...sources: FeatureSource[]) {
for (const source of sources) {
this.trackFeatureSource(source)
}
}
public getStore(id: string): UIEventSource<Record<string, string>> {
return this._elements.get(id)
}
public trackFeatureSource(source: FeatureSource) {
const self = this
source.features.addCallbackAndRunD((features) => {
for (const feature of features) {
const id = feature.properties.id
if (id === undefined) {
console.trace("Error: feature without ID:", feature)
throw "Error: feature without ID"
}
const source = self._elements.get(id)
if (source === undefined) {
self._elements.set(id, new UIEventSource<any>(feature.properties))
continue
}
if (source.data === feature.properties) {
continue
}
// Update the tags in the old store and link them
const changeMade = FeaturePropertiesStore.mergeTags(source.data, feature.properties)
feature.properties = source.data
if (changeMade) {
source.ping()
}
}
})
}
/**
* Overwrites the tags of the old properties object, returns true if a change was made.
* Metatags are overriden if they are in the new properties, but not removed
* @param oldProperties
* @param newProperties
* @private
*/
private static mergeTags(
oldProperties: Record<string, any>,
newProperties: Record<string, any>
): boolean {
let changeMade = false
for (const oldPropertiesKey in oldProperties) {
// Delete properties from the old record if it is not in the new store anymore
if (oldPropertiesKey.startsWith("_")) {
continue
}
if (newProperties[oldPropertiesKey] === undefined) {
changeMade = true
delete oldProperties[oldPropertiesKey]
}
}
// Copy all properties from the new record into the old
for (const newPropertiesKey in newProperties) {
const v = newProperties[newPropertiesKey]
if (oldProperties[newPropertiesKey] !== v) {
oldProperties[newPropertiesKey] = v
changeMade = true
}
}
return changeMade
}
// noinspection JSUnusedGlobalSymbols
public addAlias(oldId: string, newId: string): void {
if (newId === undefined) {
// We removed the node/way/relation with type 'type' and id 'oldId' on openstreetmap!
const element = this._elements.get(oldId)
element.data._deleted = "yes"
element.ping()
return
}
if (oldId == newId) {
return
}
const element = this._elements.get(oldId)
if (element === undefined) {
// Element to rewrite not found, probably a node or relation that is not rendered
return
}
element.data.id = newId
this._elements.set(newId, element)
element.ping()
}
has(id: string) {
return this._elements.has(id)
}
}