From 1e3206120c7ad7aa3e0f4c003099785f7f60fc0d Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 21 Jan 2022 03:57:49 +0100 Subject: [PATCH] Fix #596 --- Logic/FeatureSource/FeaturePipeline.ts | 7 +- .../PerLayerFeatureSourceSplitter.ts | 3 +- .../Sources/FilteringFeatureSource.ts | 1 - .../NewGeometryFromChangesFeatureSource.ts | 161 +++++++++++------- .../Conversion/CreateNoteImportLayer.ts | 3 +- Models/ThemeConfig/LayerConfig.ts | 2 +- 6 files changed, 108 insertions(+), 69 deletions(-) diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index fd17de4e6..3b6bf4572 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -311,7 +311,7 @@ export default class FeaturePipeline { // Also load points/lines that are newly added. - const newGeometry = new NewGeometryFromChangesFeatureSource(state.changes, state.osmConnection._oauth_config.url) + const newGeometry = new NewGeometryFromChangesFeatureSource(state.changes, state.allElements, state.osmConnection._oauth_config.url) newGeometry.features.addCallbackAndRun(geometries => { console.debug("New geometries are:", geometries) }) @@ -323,7 +323,10 @@ export default class FeaturePipeline { // We don't bother to split them over tiles as it'll contain little features by default, so we simply add them like this perLayerHierarchy.get(perLayer.layer.layerDef.id).registerTile(perLayer) // AT last, we always apply the metatags whenever possible - perLayer.features.addCallbackAndRunD(_ => self.onNewDataLoaded(perLayer)) + perLayer.features.addCallbackAndRunD(feats => { + console.log("New feature for layer ", perLayer.layer.layerDef.id, ":", feats) + self.onNewDataLoaded(perLayer); + }) }, newGeometry, diff --git a/Logic/FeatureSource/PerLayerFeatureSourceSplitter.ts b/Logic/FeatureSource/PerLayerFeatureSourceSplitter.ts index 3b18e2144..3bd710478 100644 --- a/Logic/FeatureSource/PerLayerFeatureSourceSplitter.ts +++ b/Logic/FeatureSource/PerLayerFeatureSourceSplitter.ts @@ -41,6 +41,7 @@ export default class PerLayerFeatureSourceSplitter { } for (const f of features) { + console.log("Classifying ", f.feature) for (const layer of layers.data) { if (layer.layerDef.source.osmTags.matchesProperties(f.feature.properties)) { // We have found our matching layer! @@ -50,8 +51,8 @@ export default class PerLayerFeatureSourceSplitter { break; } } - noLayerFound.push(f) } + noLayerFound.push(f) } // At this point, we have our features per layer as a list diff --git a/Logic/FeatureSource/Sources/FilteringFeatureSource.ts b/Logic/FeatureSource/Sources/FilteringFeatureSource.ts index 5a7a272b9..1300d42d7 100644 --- a/Logic/FeatureSource/Sources/FilteringFeatureSource.ts +++ b/Logic/FeatureSource/Sources/FilteringFeatureSource.ts @@ -63,7 +63,6 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti } private update() { - console.log("FIltering", this.upstream.name) const self = this; const layer = this.upstream.layer; const features: { feature: any; freshness: Date }[] = (this.upstream.features.data ?? []); diff --git a/Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource.ts b/Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource.ts index 1c7ab3f39..2fa47c2b3 100644 --- a/Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource.ts +++ b/Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource.ts @@ -1,8 +1,9 @@ import {Changes} from "../../Osm/Changes"; -import {OsmNode, OsmRelation, OsmWay} from "../../Osm/OsmObject"; +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' @@ -13,79 +14,115 @@ export class NewGeometryFromChangesFeatureSource implements FeatureSource { public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); public readonly name: string = "newFeatures"; - constructor(changes: Changes, backendUrl: string) { + constructor(changes: Changes, allElementStorage: ElementStorage, backendUrl: string) { const seenChanges = new Set(); const features = this.features.data; const self = this; - changes.pendingChanges - .map(changes => changes.filter(ch => - // only new objects allowed - ch.id < 0 && - // The change is an update to the object (e.g. tags or geometry) - not the actual create - ch.changes !== undefined && - // If tags is undefined, this is probably a new point that is part of a split road - ch.tags !== undefined && - // Already handled - !seenChanges.has(ch))) - .addCallbackAndRunD(changes => { - if (changes.length === 0) { - return; + changes.pendingChanges.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 } - const now = new Date(); - - function add(feature) { - feature.id = feature.properties.id - features.push({ - feature: feature, - freshness: now - }) - } - - for (const change of changes) { - seenChanges.add(change) - 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(coor => [coor[1], coor[0]]) - 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 (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.id)) { + // const currentTags = allElementStorage.getEventSourceById(change.id).data + 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(coor => [coor[1], coor[0]]) + 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() - }) + } + }) } } \ No newline at end of file diff --git a/Models/ThemeConfig/Conversion/CreateNoteImportLayer.ts b/Models/ThemeConfig/Conversion/CreateNoteImportLayer.ts index bb5c3ddee..77f2f1945 100644 --- a/Models/ThemeConfig/Conversion/CreateNoteImportLayer.ts +++ b/Models/ThemeConfig/Conversion/CreateNoteImportLayer.ts @@ -153,8 +153,7 @@ export default class CreateNoteImportLayer extends Conversion