mapcomplete/Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource.ts

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

132 lines
5.6 KiB
TypeScript
Raw Normal View History

import { Changes } from "../../Osm/Changes"
2022-01-21 03:57:49 +01:00
import { OsmNode, OsmObject, OsmRelation, OsmWay } from "../../Osm/OsmObject"
import FeatureSource from "../FeatureSource"
import { UIEventSource } from "../../UIEventSource"
import { ChangeDescription } from "../../Osm/Actions/ChangeDescription"
2022-01-21 03:57:49 +01:00
import { ElementStorage } from "../../ElementStorage"
2022-09-02 13:43:14 +02:00
import { OsmId, OsmTags } from "../../../Models/OsmFeature"
export class NewGeometryFromChangesFeatureSource implements FeatureSource {
// 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: any; freshness: Date }[]> =
new UIEventSource<{ feature: any; freshness: Date }[]>([])
public readonly name: string = "newFeatures"
2022-01-21 03:57:49 +01:00
constructor(changes: Changes, allElementStorage: ElementStorage, backendUrl: string) {
const seenChanges = new Set<ChangeDescription>()
const features = this.features.data
const self = this
changes.pendingChanges.stabilized(100).addCallbackAndRunD((changes) => {
2022-01-21 03:57:49 +01:00
if (changes.length === 0) {
return
}
2022-01-21 03:57:49 +01:00
const now = new Date()
let somethingChanged = false
2022-01-21 03:57:49 +01:00
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
}
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
2022-03-02 16:00:02 +01:00
if (allElementStorage.has(change.type + "/" + change.id)) {
// 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
OsmObject.DownloadObjectAsync(change.type + "/" + change.id).then((feat) => {
console.log("Got the reused point:", feat)
for (const kv of change.tags) {
2022-01-21 03:57:49 +01:00
feat.tags[kv.k] = kv.v
}
2022-01-26 21:40:38 +01:00
const geojson = feat.asGeoJson()
2022-01-21 03:57:49 +01:00
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!")
}
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
}
})
}
}