forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			196 lines
		
	
	
		
			No EOL
		
	
	
		
			6.3 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			196 lines
		
	
	
		
			No EOL
		
	
	
		
			6.3 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
 | 
						|
        }
 | 
						|
    }
 | 
						|
} |