mapcomplete/Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource.ts

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

130 lines
5.8 KiB
TypeScript
Raw Normal View History

import { Changes } from "../../Osm/Changes"
import { OsmNode, OsmRelation, OsmWay } from "../../Osm/OsmObject"
import { IndexedFeatureSource, WritableFeatureSource } from "../FeatureSource"
import { UIEventSource } from "../../UIEventSource"
import { ChangeDescription } from "../../Osm/Actions/ChangeDescription"
2022-09-02 13:43:14 +02:00
import { OsmId, OsmTags } from "../../../Models/OsmFeature"
import { Feature } from "geojson"
import OsmObjectDownloader from "../../Osm/OsmObjectDownloader"
export class NewGeometryFromChangesFeatureSource implements WritableFeatureSource {
// This class name truly puts the 'Java' into 'Javascript'
2021-11-07 16:34:51 +01:00
/**
2022-06-13 01:35:50 +02:00
* 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[]> = new UIEventSource<Feature[]>([])
constructor(changes: Changes, allElementStorage: IndexedFeatureSource, backendUrl: string) {
const seenChanges = new Set<ChangeDescription>()
const features = this.features.data
const self = this
const backend = changes.backend
changes.pendingChanges.addCallbackAndRunD((changes) => {
2022-01-21 03:57:49 +01:00
if (changes.length === 0) {
return
}
2022-01-21 03:57:49 +01:00
let somethingChanged = false
2022-01-21 03:57:49 +01:00
function add(feature) {
feature.id = feature.properties.id
features.push(feature)
2022-01-21 03:57:49 +01:00
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
}
console.log("Handling pending change")
2022-01-21 03:57:49 +01:00
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.featuresById.data.has(change.type + "/" + change.id)) {
2022-03-02 16:00:02 +01:00
// The current point already exists, we don't have to do anything here
2022-01-21 03:57:49 +01:00
continue
}
console.debug("Detected a reused point")
// The 'allElementsStore' does _not_ have this point yet, so we have to create it
new OsmObjectDownloader(backend)
.DownloadObjectAsync(change.type + "/" + change.id)
.then((feat) => {
console.log("Got the reused point:", feat)
if (feat === "deleted") {
throw "Panic: snapping to a point, but this point has been deleted in the meantime"
}
for (const kv of change.tags) {
feat.tags[kv.k] = kv.v
}
const geojson = feat.asGeoJson()
self.features.data.push(geojson)
self.features.ping()
})
2022-01-21 03:57:49 +01:00
continue
} else if (change.changes === undefined) {
// The geometry is not described - not a new point or geometry change, but probably a tagchange to a newly created point
// Not something that should be handled here
2022-01-21 03:57:49 +01:00
continue
}
2022-01-21 03:57:49 +01:00
try {
2022-10-27 01:50:41 +02:00
const tags: OsmTags & { id: OsmId & string } = {
2022-09-21 02:21:20 +02:00
id: <OsmId & string>(change.type + "/" + change.id),
2022-09-02 13:43:14 +02:00
}
2022-01-21 03:57:49 +01:00
for (const kv of change.tags) {
tags[kv.k] = kv.v
}
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"]
2022-02-05 23:38:55 +01:00
w.coordinates = change.changes["coordinates"].map(([lon, lat]) => [
lat,
lon,
])
2022-01-21 03:57:49 +01:00
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)
}
2022-01-21 03:57:49 +01:00
}
if (somethingChanged) {
self.features.ping()
2022-01-21 03:57:49 +01:00
}
})
}
}