Refactoring: fix delete indication, fix splitroad, fix addition of multiple new points snapped onto the same way (all will properly attach now)

This commit is contained in:
Pieter Vander Vennet 2023-04-20 17:42:07 +02:00
parent 1f9aacfb29
commit 4172af6a72
118 changed files with 1422 additions and 1357 deletions

View file

@ -20,12 +20,12 @@ export default abstract class OsmChangeAction {
this.mainObjectId = mainObjectId
}
public Perform(changes: Changes) {
public async Perform(changes: Changes) {
if (this.isUsed) {
throw "This ChangeAction is already used"
}
this.isUsed = true
return this.CreateChangeDescriptions(changes)
return await this.CreateChangeDescriptions(changes)
}
protected abstract CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]>

View file

@ -34,50 +34,40 @@ export default class OsmObjectDownloader {
}
async DownloadObjectAsync(id: NodeId, maxCacheAgeInSecs?: number): Promise<OsmNode | "deleted">
async DownloadObjectAsync(id: WayId, maxCacheAgeInSecs?: number): Promise<OsmWay | "deleted">
async DownloadObjectAsync(
id: RelationId,
maxCacheAgeInSecs?: number
): Promise<OsmRelation | undefined>
async DownloadObjectAsync(id: OsmId, maxCacheAgeInSecs?: number): Promise<OsmObject | "deleted">
async DownloadObjectAsync(
id: string,
maxCacheAgeInSecs?: number
): Promise<OsmObject | "deleted">
async DownloadObjectAsync(
id: string,
maxCacheAgeInSecs?: number
): Promise<OsmObject | "deleted"> {
async DownloadObjectAsync(id: string, maxCacheAgeInSecs?: number) {
// Wait until uploading is done
if (this._changes) {
await this._changes.isUploading.AsPromise((o) => o === false)
}
const splitted = id.split("/")
const type = splitted[0]
const idN = Number(splitted[1])
let obj: OsmObject | "deleted"
if (idN < 0) {
throw "Invalid request: cannot download OsmObject " + id + ", it has a negative id"
obj = this.constructObject(<"node" | "way" | "relation">type, idN)
} else {
obj = await this.RawDownloadObjectAsync(type, idN, maxCacheAgeInSecs)
}
const full = !id.startsWith("node") ? "/full" : ""
const url = `${this.backend}api/0.6/${id}${full}`
const rawData = await Utils.downloadJsonCachedAdvanced(
url,
(maxCacheAgeInSecs ?? 10) * 1000
)
if (rawData["error"] !== undefined && rawData["statuscode"] === 410) {
return "deleted"
if (obj === "deleted") {
return obj
}
// A full query might contain more then just the requested object (e.g. nodes that are part of a way, where we only want the way)
const parsed = OsmObject.ParseObjects(rawData["content"].elements)
// Lets fetch the object we need
for (const osmObject of parsed) {
if (osmObject.type !== type) {
continue
}
if (osmObject.id !== idN) {
continue
}
// Found the one!
return osmObject
}
throw "PANIC: requested object is not part of the response"
return await this.applyPendingChanges(obj)
}
public DownloadHistory(id: NodeId): UIEventSource<OsmNode[]>
@ -149,4 +139,105 @@ export default class OsmObjectDownloader {
return rel
})
}
private applyNodeChange(object: OsmNode, change: { lat: number; lon: number }) {
object.lat = change.lat
object.lon = change.lon
}
private applyWayChange(object: OsmWay, change: { nodes: number[]; coordinates }) {
object.nodes = change.nodes
object.coordinates = change.coordinates.map(([lat, lon]) => [lon, lat])
}
private applyRelationChange(
object: OsmRelation,
change: { members: { type: "node" | "way" | "relation"; ref: number; role: string }[] }
) {
object.members = change.members
}
private async applyPendingChanges(object: OsmObject): Promise<OsmObject | "deleted"> {
if (!this._changes) {
return object
}
const pendingChanges = this._changes.pendingChanges.data
for (const pendingChange of pendingChanges) {
if (object.id !== pendingChange.id || object.type !== pendingChange.type) {
continue
}
if (pendingChange.doDelete) {
return "deleted"
}
if (pendingChange.tags) {
for (const { k, v } of pendingChange.tags) {
if (v === undefined) {
delete object.tags[k]
} else {
object.tags[k] = v
}
}
}
if (pendingChange.changes) {
switch (pendingChange.type) {
case "node":
this.applyNodeChange(<OsmNode>object, <any>pendingChange.changes)
break
case "way":
this.applyWayChange(<OsmWay>object, <any>pendingChange.changes)
break
case "relation":
this.applyRelationChange(<OsmRelation>object, <any>pendingChange.changes)
break
}
}
}
return object
}
/**
* Creates an empty object of the specified type with the specified id.
* We assume that the pending changes will be applied on them, filling in details such as coordinates, tags, ...
*/
private constructObject(type: "node" | "way" | "relation", id: number): OsmObject {
switch (type) {
case "node":
return new OsmNode(id)
case "way":
return new OsmWay(id)
case "relation":
return new OsmRelation(id)
}
}
private async RawDownloadObjectAsync(
type: string,
idN: number,
maxCacheAgeInSecs?: number
): Promise<OsmObject | "deleted"> {
const full = type !== "node" ? "/full" : ""
const url = `${this.backend}api/0.6/${type}/${idN}${full}`
const rawData = await Utils.downloadJsonCachedAdvanced(
url,
(maxCacheAgeInSecs ?? 10) * 1000
)
if (rawData["error"] !== undefined && rawData["statuscode"] === 410) {
return "deleted"
}
// A full query might contain more then just the requested object (e.g. nodes that are part of a way, where we only want the way)
const parsed = OsmObject.ParseObjects(rawData["content"].elements)
// Lets fetch the object we need
for (const osmObject of parsed) {
if (osmObject.type !== type) {
continue
}
if (osmObject.id !== idN) {
continue
}
// Found the one!
return osmObject
}
throw "PANIC: requested object is not part of the response"
}
}