forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			202 lines
		
	
	
	
		
			6.4 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			202 lines
		
	
	
	
		
			6.4 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| 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 = <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
 | |
|     }
 | |
| 
 | |
|     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
 | |
|         }
 | |
|     }
 | |
| }
 |