128 lines
No EOL
5.2 KiB
TypeScript
128 lines
No EOL
5.2 KiB
TypeScript
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
|
|
*/
|
|
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<ChangeDescription>();
|
|
const features = this.features.data;
|
|
const self = this;
|
|
|
|
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
|
|
}
|
|
|
|
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(([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()
|
|
}
|
|
})
|
|
}
|
|
|
|
} |