forked from MapComplete/MapComplete
		
	SplitAction logic, not yet pushing changes to osm, pieter will take over
This commit is contained in:
		
							parent
							
								
									159e4d3350
								
							
						
					
					
						commit
						f77c1efdf5
					
				
					 6 changed files with 262 additions and 29 deletions
				
			
		
							
								
								
									
										162
									
								
								Logic/Osm/SplitAction.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								Logic/Osm/SplitAction.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,162 @@ | |||
| import {UIEventSource} from "../UIEventSource"; | ||||
| import {OsmNode, OsmObject, OsmWay} from "./OsmObject"; | ||||
| import State from "../../State"; | ||||
| import {distance} from "@turf/turf"; | ||||
| import {GeoOperations} from "../GeoOperations"; | ||||
| import {Changes} from "./Changes"; | ||||
| 
 | ||||
| /** | ||||
|  * Splits a road in different segments, each splitted at one of the given points (or a point on the road close to it) | ||||
|  * @param roadID The id of the road you want to split | ||||
|  * @param points The points on the road where you want the split to occur (geojson point list) | ||||
|  */ | ||||
| export async function splitRoad(roadID, points) { | ||||
|     if (points.length != 1) { | ||||
|         // TODO: more than one point
 | ||||
|         console.log(points) | ||||
|         window.alert("Warning, currently only tested on one point, you selected " + points.length + " points") | ||||
|     } | ||||
| 
 | ||||
|     let road = State.state.allElements.ContainingFeatures.get(roadID); | ||||
| 
 | ||||
|     /** | ||||
|      * Compares two points based on the starting point of the road, can be used in sort function | ||||
|      * @param point1 [lon, lat] point | ||||
|      * @param point2 [lon, lat] point | ||||
|      */ | ||||
|     function comparePointDistance(point1, point2) { | ||||
|         let distFromStart1 = GeoOperations.nearestPoint(road, point1).properties.location; | ||||
|         let distFromStart2 = GeoOperations.nearestPoint(road, point2).properties.location; | ||||
|         return distFromStart1 - distFromStart2; // Sort requires a number to return instead of a bool
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Eliminates split points close (<4m) to existing points on the road, so you can split on these points instead | ||||
|      * @param road The road geojson object | ||||
|      * @param points The points on the road where you want the split to occur (geojson point list) | ||||
|      * @return realSplitPoints List containing all new locations where you should split | ||||
|      */ | ||||
|     function getSplitPoints(road, points) { | ||||
|         // Copy the list
 | ||||
|         let roadPoints = [...road.geometry.coordinates]; | ||||
| 
 | ||||
|         // Get the coordinates of all geojson points
 | ||||
|         let splitPointsCoordinates = points.map((point) => point.geometry.coordinates); | ||||
| 
 | ||||
|         roadPoints.push(...splitPointsCoordinates); | ||||
| 
 | ||||
|         // Sort all points on the road based on the distance from the start
 | ||||
|         roadPoints.sort(comparePointDistance) | ||||
| 
 | ||||
|         // Remove points close to existing points on road
 | ||||
|         let realSplitPoints = [...splitPointsCoordinates]; | ||||
|         for (let index = roadPoints.length - 1; index > 0; index--) { | ||||
|             // Iterate backwards to prevent problems when removing elements
 | ||||
|             let dist = distance(roadPoints[index - 1], roadPoints[index], {units: "kilometers"}); | ||||
|             // Remove all cutpoints closer than 4m to their previous point
 | ||||
|             if ((dist < 0.004) && (splitPointsCoordinates.includes(roadPoints[index]))) { | ||||
|                 console.log("Removed a splitpoint, using a closer point to the road instead") | ||||
|                 realSplitPoints.splice(index, 1) | ||||
|                 realSplitPoints.push(roadPoints[index - 1]) | ||||
|             } | ||||
|         } | ||||
|         return realSplitPoints; | ||||
|     } | ||||
| 
 | ||||
|     let realSplitPoints = getSplitPoints(road, points); | ||||
| 
 | ||||
|     // Create a sorted list containing all points
 | ||||
|     let allPoints = [...road.geometry.coordinates]; | ||||
|     allPoints.push(...realSplitPoints); | ||||
|     allPoints.sort(comparePointDistance); | ||||
| 
 | ||||
|     // The changeset that will contain the operations to split the road
 | ||||
|     let changes = new Changes(); | ||||
| 
 | ||||
|     // Download the data of the current road from Osm to get the ID's of the coordinates
 | ||||
|     let osmRoad: UIEventSource<OsmWay> = OsmObject.DownloadObject(roadID); | ||||
| 
 | ||||
|     // TODO: Remove delay, use a callback on odmRoad instead and execute all code below in callback function
 | ||||
|     function delay(ms: number) { | ||||
|         return new Promise(resolve => setTimeout(resolve, ms)); | ||||
|     } | ||||
|     await delay(3000); | ||||
| 
 | ||||
|     // Dict to quickly convert a coordinate to a nodeID
 | ||||
|     let coordToIDMap = {}; | ||||
| 
 | ||||
|     /** | ||||
|      * Converts a coordinate to a string, so it's hashable (e.g. for using it in a dict) | ||||
|      * @param coord [lon, lat] point | ||||
|      */ | ||||
|     function getCoordKey(coord: [number, number]) { | ||||
|         return coord[0] + "," + coord[1]; | ||||
|     } | ||||
| 
 | ||||
|     osmRoad.data.coordinates.forEach((coord, i) => coordToIDMap[getCoordKey([coord[1], coord[0]])] = osmRoad.data.nodes[i]); | ||||
| 
 | ||||
|     let currentRoadPoints: number[] = []; | ||||
|     let currentRoadCoordinates: [number, number][] = [] | ||||
| 
 | ||||
|     /** | ||||
|      * Given a coordinate, check whether there is already a node in osm created (on the road or cutpoints) or create | ||||
|      * such point if it doesn't exist yet and return the id of this coordinate | ||||
|      * @param coord [lon, lat] point | ||||
|      * @return pointID The ID of the existing/created node on given coordinates | ||||
|      */ | ||||
|     function getOrCreateNodeID(coord) { | ||||
|         console.log(coordToIDMap) | ||||
|         let poinID = coordToIDMap[getCoordKey(coord)]; | ||||
|         if (poinID == undefined) { | ||||
|             console.log(getCoordKey(coord) + " not in map") | ||||
|             // TODO: Check if lat, lon is correct
 | ||||
|             let newNode = changes.createElement([], coord[1], coord[0]); | ||||
| 
 | ||||
|             coordToIDMap[coord] = newNode.id; | ||||
|             poinID = newNode.id; | ||||
| 
 | ||||
|             console.log("New point created "); | ||||
|         } | ||||
|         return poinID; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Creates a new road in OSM, while copying the tags from osmRoad and using currentRoadPoints as points | ||||
|      * @param currentRoadPoints List of id's of nodes the road should exist of | ||||
|      * @param osmRoad The road to copy the tags from | ||||
|      */ | ||||
|     function createNewRoadSegment(currentRoadPoints, osmRoad) { | ||||
|         changes.createRoad(osmRoad.data.tags, currentRoadPoints, currentRoadCoordinates); | ||||
|     } | ||||
| 
 | ||||
|     for (let coord of allPoints) { | ||||
|         console.log("Handling coord") | ||||
|         let pointID = getOrCreateNodeID(coord); | ||||
|         currentRoadPoints.push(pointID); | ||||
|         currentRoadCoordinates.push(coord); | ||||
|         if (realSplitPoints.includes(coord)) { | ||||
|             console.log("Handling split") | ||||
|             // Should split here
 | ||||
|             // currentRoadPoints contains a list containing all points for this road segment
 | ||||
|             createNewRoadSegment(currentRoadPoints, osmRoad); | ||||
| 
 | ||||
|             // Cleanup for next split
 | ||||
|             currentRoadPoints = [pointID]; | ||||
|             currentRoadCoordinates = [coord]; | ||||
|             console.log("Splitting here...") | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Update the road to contain only the points of the last segment
 | ||||
|     // changes.updateRoadCoordinates(roadID, currentRoadPoints);
 | ||||
| 
 | ||||
|     // push the applied changes
 | ||||
|     changes.flushChanges(); | ||||
| 
 | ||||
|     return; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // TODO: Vlakbij bestaand punt geklikt? Bestaand punt hergebruiken
 | ||||
| //  Nieuw wegobject aanmaken, en oude hergebruiken voor andere helft van de weg
 | ||||
| // TODO: CHeck if relation exist to the road -> Delete them when splitted, because they might be outdated after the split
 | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue