forked from MapComplete/MapComplete
		
	
		
			
	
	
		
			108 lines
		
	
	
	
		
			3.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
		
		
			
		
	
	
			108 lines
		
	
	
	
		
			3.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| 
								 | 
							
								import FeatureSource, { IndexedFeatureSource } from "../FeatureSource"
							 | 
						||
| 
								 | 
							
								import { UIEventSource } from "../../UIEventSource"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Constructs a UIEventStore for the properties of every Feature, indexed by id
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								export default class FeaturePropertiesStore {
							 | 
						||
| 
								 | 
							
								    private readonly _source: FeatureSource & IndexedFeatureSource
							 | 
						||
| 
								 | 
							
								    private readonly _elements = new Map<string, UIEventSource<any>>()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    constructor(source: FeatureSource & IndexedFeatureSource) {
							 | 
						||
| 
								 | 
							
								        this._source = source
							 | 
						||
| 
								 | 
							
								        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()
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        })
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public getStore(id: string): UIEventSource<Record<string, string>> {
							 | 
						||
| 
								 | 
							
								        return this._elements.get(id)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * 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
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    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)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 |