import {Changes} from "../../Osm/Changes"; import {OsmNode, OsmObject, OsmRelation, OsmWay} from "../../Osm/OsmObject"; import FeatureSource from "../FeatureSource"; import {UIEventSource} from "../../UIEventSource"; import {ChangeDescription} from "../../Osm/Actions/ChangeDescription"; import {ElementStorage} from "../../ElementStorage"; export class NewGeometryFromChangesFeatureSource implements FeatureSource { // This class name truly puts the 'Java' into 'Javascript' /** * A feature source containing exclusively new elements. * * 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"; constructor(changes: Changes, allElementStorage: ElementStorage, backendUrl: string) { const seenChanges = new Set(); const features = this.features.data; const self = this; changes.pendingChanges.stabilized(100).addCallbackAndRunD(changes => { if (changes.length === 0) { return; } const now = new Date(); let somethingChanged = false; function add(feature) { feature.id = feature.properties.id features.push({ feature: feature, freshness: now }) somethingChanged = true; } for (const change of changes) { if (seenChanges.has(change)) { // Already handled continue; } seenChanges.add(change) if (change.tags === undefined) { // If tags is undefined, this is probably a new point that is part of a split road continue } if (change.id > 0) { // This is an already existing object // In _most_ of the cases, this means that this _isn't_ a new object // However, when a point is snapped to an already existing point, we have to create a representation for this point! // For this, we introspect the change if (allElementStorage.has(change.type + "/" + change.id)) { // The current point already exists, we don't have to do anything here continue; } console.debug("Detected a reused point") // The 'allElementsStore' does _not_ have this point yet, so we have to create it OsmObject.DownloadObjectAsync(change.type + "/" + change.id).then(feat => { console.log("Got the reused point:", feat) for (const kv of change.tags) { feat.tags[kv.k] = kv.v } const geojson = feat.asGeoJson(); allElementStorage.addOrGetElement(geojson) self.features.data.push({feature: geojson, freshness: new Date()}) self.features.ping() }) continue } else if (change.id < 0 && change.changes === undefined) { // The geometry is not described - not a new point if (change.id < 0) { console.error("WARNING: got a new point without geometry!") } continue; } try { const tags = {} for (const kv of change.tags) { tags[kv.k] = kv.v } tags["id"] = change.type + "/" + change.id tags["_backend"] = backendUrl switch (change.type) { case "node": const n = new OsmNode(change.id) n.tags = tags n.lat = change.changes["lat"] n.lon = change.changes["lon"] const geojson = n.asGeoJson() add(geojson) break; case "way": const w = new OsmWay(change.id) w.tags = tags w.nodes = change.changes["nodes"] w.coordinates = change.changes["coordinates"].map(([lon, lat]) => [lat, lon]) add(w.asGeoJson()) break; case "relation": const r = new OsmRelation(change.id) r.tags = tags r.members = change.changes["members"] add(r.asGeoJson()) break; } } catch (e) { console.error("Could not generate a new geometry to render on screen for:", e) } } if (somethingChanged) { self.features.ping() } }) } }