import { OsmNode, OsmRelation, OsmWay } from "../OsmObject" /** * Represents a single change to an object */ export interface ChangeDescription { /** * Metadata to be included in the changeset */ meta: { /* * The theme with which this changeset was made */ theme: string /** * The type of the change */ changeType: "answer" | "create" | "split" | "delete" | "move" | "import" | string | null /** * THe motivation for the change, e.g. 'deleted because does not exist anymore' */ specialMotivation?: string /** * Added by Changes.ts */ distanceToObject?: number } /** * Identifier of the object */ type: "node" | "way" | "relation" /** * Identifier of the object * Negative for new objects */ id: number /** * All changes to tags * v = "" or v = undefined to erase this tag * * Note that this list will only contain the _changes_ to the tags, not the full set of tags */ tags?: { k: string; v: string }[] /** * A change to the geometry: * 1) Change of node location * 2) Change of way geometry * 3) Change of relation members (untested) */ changes?: | { lat: number lon: number } | { /* Coordinates are only used for rendering. They should be LON, LAT * */ coordinates: [number, number][] nodes: number[] } | { members: { type: "node" | "way" | "relation"; ref: number; role: string }[] } /* Set to delete the object */ doDelete?: boolean } export class ChangeDescriptionTools { /** * Rewrites all the ids in a changeDescription * * // should rewrite the id of the changed object * const change = { * id: -1234, * type: "node", * meta:{ * theme:"test", * changeType: "answer" * }, * tags:[ * { * k: "key", * v: "value" * } * ] * } * } * const mapping = new Map([["node/-1234", "node/42"]]) * const rewritten = ChangeDescriptionTools.rewriteIds(change, mapping) * rewritten.id // => 42 * * // should rewrite ids in nodes of a way * const change = { * type: "way", * id: 789, * changes: { * nodes: [-1, -2, -3, 68453], * coordinates: [] * }, * meta:{ * theme:"test", * changeType: "create" * } * } * const mapping = new Map([["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 = { * 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([["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 ): 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 } public static getGeojsonGeometry(change: ChangeDescription): any { 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"] w.coordinates = change.changes["coordinates"].map(([lon, lat]) => [lat, lon]) return w.asGeoJson().geometry case "relation": const r = new OsmRelation(change.id) r.members = change.changes["members"] return r.asGeoJson().geometry } } }