| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  | 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[][] | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-19 01:22:24 +02:00
										 |  |  | abstract class AbstractRelationSplitHandler extends OsmChangeAction { | 
					
						
							|  |  |  |     protected readonly _input: RelationSplitInput; | 
					
						
							|  |  |  |     protected readonly _theme: string; | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |     constructor(input: RelationSplitInput, theme: string) { | 
					
						
							| 
									
										
										
										
											2022-01-26 21:40:38 +01:00
										 |  |  |         super("relation/" + input.relation.id, false) | 
					
						
							| 
									
										
										
										
											2021-09-22 16:07:56 +02:00
										 |  |  |         this._input = input; | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |         this._theme = theme; | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-01-26 21:40:38 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Returns which node should border the member at the given index | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-10-19 01:22:24 +02:00
										 |  |  |     protected async targetNodeAt(i: number, first: boolean) { | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |         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; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-10-19 01:22:24 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * 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 AbstractRelationSplitHandler { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     constructor(input: RelationSplitInput, theme: string) { | 
					
						
							|  |  |  |         super(input, theme) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> { | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |         if (this._input.relation.tags["type"] === "restriction") { | 
					
						
							| 
									
										
										
										
											2021-10-19 01:22:24 +02:00
										 |  |  |             // This is a turn restriction
 | 
					
						
							|  |  |  |             return new TurnRestrictionRSH(this._input, this._theme).CreateChangeDescriptions(changes) | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |         return new InPlaceReplacedmentRTSH(this._input, this._theme).CreateChangeDescriptions(changes) | 
					
						
							| 
									
										
										
										
											2021-10-19 01:22:24 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export class TurnRestrictionRSH extends AbstractRelationSplitHandler { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     constructor(input: RelationSplitInput, theme: string) { | 
					
						
							|  |  |  |         super(input, theme); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-19 01:22:24 +02:00
										 |  |  |     public async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const relation = this._input.relation | 
					
						
							|  |  |  |         const members = relation.members | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-19 01:22:24 +02:00
										 |  |  |         const selfMembers = members.filter(m => m.type === "way" && m.ref === this._input.originalWayId) | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (selfMembers.length > 1) { | 
					
						
							| 
									
										
										
										
											2021-10-19 01:22:24 +02:00
										 |  |  |             console.warn("Detected a turn restriction where this way has multiple occurances. This is an error") | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         const selfMember = selfMembers[0] | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (selfMember.role === "via") { | 
					
						
							| 
									
										
										
										
											2021-10-19 01:22:24 +02:00
										 |  |  |             // A via way can be replaced in place
 | 
					
						
							|  |  |  |             return new InPlaceReplacedmentRTSH(this._input, this._theme).CreateChangeDescriptions(changes); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-19 01:22:24 +02:00
										 |  |  |         // We have to keep only the way with a common point with the rest of the relation
 | 
					
						
							|  |  |  |         // Let's figure out which member is neighbouring our way
 | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         let commonStartPoint: number = await this.targetNodeAt(members.indexOf(selfMember), true) | 
					
						
							|  |  |  |         let commonEndPoint: number = await this.targetNodeAt(members.indexOf(selfMember), false) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-19 01:22:24 +02:00
										 |  |  |         // In normal circumstances, only one of those should be defined
 | 
					
						
							|  |  |  |         let commonPoint = commonStartPoint ?? commonEndPoint | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-19 01:22:24 +02:00
										 |  |  |         // Let's select the way to keep
 | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |         const idToKeep: { id: number } = this._input.allWaysNodesInOrder.map((nodes, i) => ({ | 
					
						
							|  |  |  |             nodes: nodes, | 
					
						
							|  |  |  |             id: this._input.allWayIdsInOrder[i] | 
					
						
							|  |  |  |         })) | 
					
						
							| 
									
										
										
										
											2021-10-19 01:22:24 +02:00
										 |  |  |             .filter(nodesId => { | 
					
						
							|  |  |  |                 const nds = nodesId.nodes | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |                 return nds[0] == commonPoint || nds[nds.length - 1] == commonPoint | 
					
						
							| 
									
										
										
										
											2021-10-19 01:22:24 +02:00
										 |  |  |             })[0] | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (idToKeep === undefined) { | 
					
						
							| 
									
										
										
										
											2021-10-19 01:22:24 +02:00
										 |  |  |             console.error("No common point found, this was a broken turn restriction!", relation.id) | 
					
						
							|  |  |  |             return [] | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-19 01:22:24 +02:00
										 |  |  |         const originalWayId = this._input.originalWayId | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |         if (idToKeep.id === originalWayId) { | 
					
						
							| 
									
										
										
										
											2021-10-19 01:22:24 +02:00
										 |  |  |             console.log("Turn_restriction fixer: the original ID can be kept, nothing to do") | 
					
						
							|  |  |  |             return [] | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         const newMembers: { | 
					
						
							|  |  |  |             ref: number, | 
					
						
							|  |  |  |             type: "way" | "node" | "relation", | 
					
						
							|  |  |  |             role: string | 
					
						
							| 
									
										
										
										
											2021-10-19 01:22:24 +02:00
										 |  |  |         } [] = relation.members.map(m => { | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |             if (m.type === "way" && m.ref === originalWayId) { | 
					
						
							| 
									
										
										
										
											2021-10-19 01:22:24 +02:00
										 |  |  |                 return { | 
					
						
							|  |  |  |                     ref: idToKeep.id, | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |                     type: "way", | 
					
						
							| 
									
										
										
										
											2021-10-19 01:22:24 +02:00
										 |  |  |                     role: m.role | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             return m | 
					
						
							|  |  |  |         }) | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-19 01:22:24 +02:00
										 |  |  |         return [ | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 type: "relation", | 
					
						
							|  |  |  |                 id: relation.id, | 
					
						
							|  |  |  |                 changes: { | 
					
						
							|  |  |  |                     members: newMembers | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |                 meta: { | 
					
						
							|  |  |  |                     theme: this._theme, | 
					
						
							|  |  |  |                     changeType: "relation-fix:turn_restriction" | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         ]; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-19 01:22:24 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * 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 AbstractRelationSplitHandler { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     constructor(input: RelationSplitInput, theme: string) { | 
					
						
							|  |  |  |         super(input, theme); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |             const firstNodeMatches = nodeIdBefore === undefined || nodeIdBefore === firstNode | 
					
						
							|  |  |  |             const lastNodeMatches = nodeIdAfter === undefined || nodeIdAfter === lastNode | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             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 | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |             const lastNodeMatchesRev = nodeIdAfter === undefined || nodeIdAfter === firstNode | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |             if (firstNodeMatchesRev || lastNodeMatchesRev) { | 
					
						
							|  |  |  |                 // We (probably) have a reversed situation, backward situation
 | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |                 for (let i1 = this._input.allWayIdsInOrder.length - 1; i1 >= 0; i1--) { | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |                     // Iterate BACKWARDS
 | 
					
						
							|  |  |  |                     const wId = this._input.allWayIdsInOrder[i1]; | 
					
						
							|  |  |  |                     newMembers.push({ | 
					
						
							|  |  |  |                         ref: wId, | 
					
						
							|  |  |  |                         type: "way", | 
					
						
							|  |  |  |                         role: member.role | 
					
						
							|  |  |  |                     }) | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |             // 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", | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |             changes: {members: newMembers}, | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |             meta: { | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |                 changeType: "relation-fix", | 
					
						
							|  |  |  |                 theme: this._theme | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |         }]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } |