forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			144 lines
		
	
	
		
			No EOL
		
	
	
		
			4.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			144 lines
		
	
	
		
			No EOL
		
	
	
		
			4.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import OsmChangeAction from "./OsmChangeAction";
 | 
						|
import {Changes} from "../Changes";
 | 
						|
import {ChangeDescription} from "./ChangeDescription";
 | 
						|
import {OsmObject, OsmRelation, OsmWay} from "../OsmObject";
 | 
						|
 | 
						|
export interface RelationSplitInput {
 | 
						|
    relation: OsmRelation,
 | 
						|
    originalWayId: number,
 | 
						|
    allWayIdsInOrder: number[],
 | 
						|
    originalNodes: number[],
 | 
						|
    allWaysNodesInOrder: number[][]
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * When a way is split and this way is part of a relation, the relation should be updated too to have the new segment if relevant.
 | 
						|
 */
 | 
						|
export default class RelationSplitHandler extends OsmChangeAction {
 | 
						|
    private readonly _input: RelationSplitInput;
 | 
						|
 | 
						|
    constructor(input: RelationSplitInput) {
 | 
						|
        super()
 | 
						|
        this._input = input;
 | 
						|
    }
 | 
						|
 | 
						|
    async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> {
 | 
						|
       return new InPlaceReplacedmentRTSH(this._input).CreateChangeDescriptions(changes)
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * A simple strategy to split relations:
 | 
						|
 * -> Download the way members just before and just after the original way
 | 
						|
 * -> Make sure they are still aligned
 | 
						|
 *
 | 
						|
 * Note that the feature might appear multiple times.
 | 
						|
 */
 | 
						|
export class InPlaceReplacedmentRTSH extends OsmChangeAction {
 | 
						|
    private readonly _input: RelationSplitInput;
 | 
						|
 | 
						|
    constructor(input: RelationSplitInput) {
 | 
						|
        super();
 | 
						|
        this._input = input;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns which node should border the member at the given index
 | 
						|
     */
 | 
						|
    private async targetNodeAt(i: number, first: boolean) {
 | 
						|
        const member = this._input.relation.members[i]
 | 
						|
        if (member === undefined) {
 | 
						|
            return undefined
 | 
						|
        }
 | 
						|
        if (member.type === "node") {
 | 
						|
            return member.ref
 | 
						|
        }
 | 
						|
        if (member.type === "way") {
 | 
						|
            const osmWay = <OsmWay>await OsmObject.DownloadObjectAsync("way/" + member.ref)
 | 
						|
            const nodes = osmWay.nodes
 | 
						|
            if (first) {
 | 
						|
                return nodes[0]
 | 
						|
            } else {
 | 
						|
                return nodes[nodes.length - 1]
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if (member.type === "relation") {
 | 
						|
            return undefined
 | 
						|
        }
 | 
						|
        return undefined;
 | 
						|
    }
 | 
						|
 | 
						|
    async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> {
 | 
						|
 | 
						|
        const wayId = this._input.originalWayId
 | 
						|
        const relation = this._input.relation
 | 
						|
        const members = relation.members
 | 
						|
        const originalNodes = this._input.originalNodes;
 | 
						|
        const firstNode = originalNodes[0]
 | 
						|
        const lastNode = originalNodes[originalNodes.length - 1]
 | 
						|
        const newMembers: { type: "node" | "way" | "relation", ref: number, role: string }[] = []
 | 
						|
 | 
						|
        for (let i = 0; i < members.length; i++) {
 | 
						|
            const member = members[i];
 | 
						|
            if (member.type !== "way" || member.ref !== wayId) {
 | 
						|
                newMembers.push(member)
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
 | 
						|
            const nodeIdBefore = await this.targetNodeAt(i - 1, false)
 | 
						|
            const nodeIdAfter = await this.targetNodeAt(i + 1, true)
 | 
						|
 | 
						|
            const firstNodeMatches = nodeIdBefore === undefined ||  nodeIdBefore === firstNode
 | 
						|
            const lastNodeMatches =nodeIdAfter === undefined ||  nodeIdAfter === lastNode
 | 
						|
 | 
						|
            if (firstNodeMatches && lastNodeMatches) {
 | 
						|
                // We have a classic situation, forward situation
 | 
						|
                for (const wId of this._input.allWayIdsInOrder) {
 | 
						|
                    newMembers.push({
 | 
						|
                        ref: wId,
 | 
						|
                        type: "way",
 | 
						|
                        role: member.role
 | 
						|
                    })
 | 
						|
                }
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
 | 
						|
            const firstNodeMatchesRev = nodeIdBefore === undefined || nodeIdBefore === lastNode
 | 
						|
            const lastNodeMatchesRev =nodeIdAfter === undefined || nodeIdAfter === firstNode
 | 
						|
            if (firstNodeMatchesRev || lastNodeMatchesRev) {
 | 
						|
                // We (probably) have a reversed situation, backward situation
 | 
						|
                for (let i1 =  this._input.allWayIdsInOrder.length - 1; i1 >= 0; i1--){
 | 
						|
                    // Iterate BACKWARDS
 | 
						|
                    const wId = this._input.allWayIdsInOrder[i1];
 | 
						|
                    newMembers.push({
 | 
						|
                        ref: wId,
 | 
						|
                        type: "way",
 | 
						|
                        role: member.role
 | 
						|
                    })
 | 
						|
                }
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            
 | 
						|
            // Euhm, allright... Something weird is going on, but let's not care too much
 | 
						|
            // Lets pretend this is forward going
 | 
						|
            for (const wId of this._input.allWayIdsInOrder) {
 | 
						|
                newMembers.push({
 | 
						|
                    ref: wId,
 | 
						|
                    type: "way",
 | 
						|
                    role: member.role
 | 
						|
                })
 | 
						|
            }
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
        return [{
 | 
						|
            id: relation.id,
 | 
						|
            type: "relation",
 | 
						|
            changes: {members: newMembers}
 | 
						|
        }];
 | 
						|
    }
 | 
						|
 | 
						|
} |