forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			112 lines
		
	
	
	
		
			3.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			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)
 | 
						|
    }
 | 
						|
}
 |