2021-09-22 05:02:09 +02:00
|
|
|
import {OsmNode, OsmRelation, OsmWay} from "../OsmObject";
|
|
|
|
|
2021-09-21 02:10:42 +02:00
|
|
|
/**
|
|
|
|
* Represents a single change to an object
|
|
|
|
*/
|
2021-07-15 20:47:28 +02:00
|
|
|
export interface ChangeDescription {
|
|
|
|
|
2021-10-04 03:12:42 +02:00
|
|
|
/**
|
|
|
|
* Metadata to be included in the changeset
|
|
|
|
*/
|
|
|
|
meta: {
|
|
|
|
/*
|
|
|
|
* The theme with which this changeset was made
|
|
|
|
*/
|
|
|
|
theme: string,
|
|
|
|
/**
|
|
|
|
* The type of the change
|
|
|
|
*/
|
2021-11-07 16:34:51 +01:00
|
|
|
changeType: "answer" | "create" | "split" | "delete" | "move" | "import" | string | null
|
2021-10-04 03:12:42 +02:00
|
|
|
/**
|
|
|
|
* THe motivation for the change, e.g. 'deleted because does not exist anymore'
|
|
|
|
*/
|
2021-11-09 01:49:07 +01:00
|
|
|
specialMotivation?: string,
|
|
|
|
/**
|
|
|
|
* Added by Changes.ts
|
|
|
|
*/
|
|
|
|
distanceToObject?: number
|
2021-10-04 03:12:42 +02:00
|
|
|
},
|
2021-11-07 16:34:51 +01:00
|
|
|
|
2021-09-21 02:10:42 +02:00
|
|
|
/**
|
|
|
|
* Identifier of the object
|
|
|
|
*/
|
2021-07-15 20:47:28 +02:00
|
|
|
type: "node" | "way" | "relation",
|
|
|
|
/**
|
2021-09-21 02:10:42 +02:00
|
|
|
* Identifier of the object
|
|
|
|
* Negative for new objects
|
2021-07-15 20:47:28 +02:00
|
|
|
*/
|
|
|
|
id: number,
|
2021-11-07 16:34:51 +01:00
|
|
|
|
2021-09-21 02:10:42 +02:00
|
|
|
/**
|
|
|
|
* All changes to tags
|
|
|
|
* v = "" or v = undefined to erase this tag
|
2021-11-07 16:34:51 +01:00
|
|
|
*
|
2021-10-10 00:53:14 +02:00
|
|
|
* Note that this list will only contain the _changes_ to the tags, not the full set of tags
|
2021-09-21 02:10:42 +02:00
|
|
|
*/
|
2021-07-15 20:47:28 +02:00
|
|
|
tags?: { k: string, v: string }[],
|
|
|
|
|
2021-09-21 02:10:42 +02:00
|
|
|
/**
|
|
|
|
* A change to the geometry:
|
|
|
|
* 1) Change of node location
|
|
|
|
* 2) Change of way geometry
|
|
|
|
* 3) Change of relation members (untested)
|
|
|
|
*/
|
2021-07-15 20:47:28 +02:00
|
|
|
changes?: {
|
|
|
|
lat: number,
|
|
|
|
lon: number
|
|
|
|
} | {
|
2021-10-29 16:38:33 +02:00
|
|
|
/* Coordinates are only used for rendering. They should be LON, LAT
|
|
|
|
* */
|
2021-09-22 05:02:09 +02:00
|
|
|
coordinates: [number, number][]
|
2021-07-15 20:47:28 +02:00
|
|
|
nodes: number[],
|
|
|
|
} | {
|
|
|
|
members: { type: "node" | "way" | "relation", ref: number, role: string }[]
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Set to delete the object
|
|
|
|
*/
|
|
|
|
doDelete?: boolean
|
2021-09-22 05:02:09 +02:00
|
|
|
}
|
2021-07-15 20:47:28 +02:00
|
|
|
|
2021-11-07 16:34:51 +01:00
|
|
|
export class ChangeDescriptionTools {
|
|
|
|
|
2022-04-08 04:18:53 +02:00
|
|
|
/**
|
|
|
|
* Rewrites all the ids in a changeDescription
|
|
|
|
*
|
|
|
|
* // should rewrite the id of the changed object
|
|
|
|
* const change = <ChangeDescription> {
|
|
|
|
* id: -1234,
|
|
|
|
* type: "node",
|
|
|
|
* meta:{
|
|
|
|
* theme:"test",
|
|
|
|
* changeType: "answer"
|
|
|
|
* },
|
|
|
|
* tags:[
|
|
|
|
* {
|
|
|
|
* k: "key",
|
|
|
|
* v: "value"
|
|
|
|
* }
|
|
|
|
* ]
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
* const mapping = new Map<string, string>([["node/-1234", "node/42"]])
|
|
|
|
* const rewritten = ChangeDescriptionTools.rewriteIds(change, mapping)
|
|
|
|
* rewritten.id // => 42
|
|
|
|
*
|
|
|
|
* // should rewrite ids in nodes of a way
|
|
|
|
* const change = <ChangeDescription> {
|
|
|
|
* type: "way",
|
|
|
|
* id: 789,
|
|
|
|
* changes: {
|
|
|
|
* nodes: [-1, -2, -3, 68453],
|
|
|
|
* coordinates: []
|
|
|
|
* },
|
|
|
|
* meta:{
|
|
|
|
* theme:"test",
|
|
|
|
* changeType: "create"
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
* const mapping = new Map<string, string>([["node/-1", "node/42"],["node/-2", "node/43"],["node/-3", "node/44"]])
|
|
|
|
* const rewritten = ChangeDescriptionTools.rewriteIds(change, mapping)
|
|
|
|
* rewritten.id // => 789
|
|
|
|
* rewritten.changes["nodes"] // => [42,43,44, 68453]
|
|
|
|
*
|
|
|
|
* // should rewrite ids in relationship members
|
|
|
|
* const change = <ChangeDescription> {
|
|
|
|
* type: "way",
|
|
|
|
* id: 789,
|
|
|
|
* changes: {
|
|
|
|
* members: [{type: "way", ref: -1, role: "outer"},{type: "way", ref: 48, role: "outer"}],
|
|
|
|
* },
|
|
|
|
* meta:{
|
|
|
|
* theme:"test",
|
|
|
|
* changeType: "create"
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
* const mapping = new Map<string, string>([["way/-1", "way/42"],["node/-2", "node/43"],["node/-3", "node/44"]])
|
|
|
|
* const rewritten = ChangeDescriptionTools.rewriteIds(change, mapping)
|
|
|
|
* rewritten.id // => 789
|
|
|
|
* rewritten.changes["members"] // => [{type: "way", ref: 42, role: "outer"},{type: "way", ref: 48, role: "outer"}]
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
public static rewriteIds(change: ChangeDescription, mappings: Map<string, string>): ChangeDescription {
|
|
|
|
const key = change.type + "/" + change.id
|
|
|
|
|
|
|
|
const wayHasChangedNode = ((change.changes ?? {})["nodes"] ?? []).some(id => mappings.has("node/" + id));
|
|
|
|
const relationHasChangedMembers = ((change.changes ?? {})["members"] ?? [])
|
|
|
|
.some((obj:{type: string, ref: number}) => mappings.has(obj.type+"/" + obj.ref));
|
|
|
|
|
|
|
|
const hasSomeChange = mappings.has(key)
|
|
|
|
|| wayHasChangedNode || relationHasChangedMembers
|
|
|
|
if(hasSomeChange){
|
|
|
|
change = {...change}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mappings.has(key)) {
|
|
|
|
const [_, newId] = mappings.get(key).split("/")
|
|
|
|
change.id = Number.parseInt(newId)
|
|
|
|
}
|
|
|
|
if(wayHasChangedNode){
|
|
|
|
change.changes = {...change.changes}
|
|
|
|
change.changes["nodes"] = change.changes["nodes"].map(id => {
|
|
|
|
const key = "node/"+id
|
|
|
|
if(!mappings.has(key)){
|
|
|
|
return id
|
|
|
|
}
|
|
|
|
const [_, newId] = mappings.get(key).split("/")
|
|
|
|
return Number.parseInt(newId)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
if(relationHasChangedMembers){
|
|
|
|
change.changes = {...change.changes}
|
|
|
|
change.changes["members"] = change.changes["members"].map(
|
|
|
|
(obj:{type: string, ref: number}) => {
|
|
|
|
const key = obj.type+"/"+obj.ref;
|
|
|
|
if(!mappings.has(key)){
|
|
|
|
return obj
|
|
|
|
}
|
|
|
|
const [_, newId] = mappings.get(key).split("/")
|
|
|
|
return {...obj, ref: Number.parseInt(newId)}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
return change
|
|
|
|
}
|
|
|
|
|
2021-11-07 16:34:51 +01:00
|
|
|
public static getGeojsonGeometry(change: ChangeDescription): any {
|
2021-09-22 05:02:09 +02:00
|
|
|
switch (change.type) {
|
|
|
|
case "node":
|
|
|
|
const n = new OsmNode(change.id)
|
|
|
|
n.lat = change.changes["lat"]
|
|
|
|
n.lon = change.changes["lon"]
|
|
|
|
return n.asGeoJson().geometry
|
|
|
|
case "way":
|
|
|
|
const w = new OsmWay(change.id)
|
|
|
|
w.nodes = change.changes["nodes"]
|
2022-02-22 14:13:41 +01:00
|
|
|
w.coordinates = change.changes["coordinates"].map(([lon, lat]) => [lat, lon])
|
2021-09-22 05:02:09 +02:00
|
|
|
return w.asGeoJson().geometry
|
|
|
|
case "relation":
|
|
|
|
const r = new OsmRelation(change.id)
|
|
|
|
r.members = change.changes["members"]
|
|
|
|
return r.asGeoJson().geometry
|
|
|
|
}
|
|
|
|
}
|
2021-07-15 20:47:28 +02:00
|
|
|
}
|