forked from MapComplete/MapComplete
		
	Further work on the road splitting feature
This commit is contained in:
		
							parent
							
								
									9348a019d6
								
							
						
					
					
						commit
						1da3f8a332
					
				
					 9 changed files with 351 additions and 274 deletions
				
			
		| 
						 | 
					@ -1,6 +1,10 @@
 | 
				
			||||||
import FeatureSource from "./FeatureSource";
 | 
					import FeatureSource from "./FeatureSource";
 | 
				
			||||||
import {UIEventSource} from "../UIEventSource";
 | 
					import {UIEventSource} from "../UIEventSource";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Merges features from different featureSources
 | 
				
			||||||
 | 
					 * Uses the freshest feature available in the case multiple sources offer data with the same identifier
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
export default class FeatureSourceMerger implements FeatureSource {
 | 
					export default class FeatureSourceMerger implements FeatureSource {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]);
 | 
					    public features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * An action is a change to the OSM-database
 | 
				
			||||||
 | 
					 * It will generate some new/modified/deleted objects, which are all bundled by the 'changes'-object
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export default interface Action {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
import {OsmNode, OsmObject, OsmWay} from "./OsmObject";
 | 
					import {OsmNode, OsmObject} from "./OsmObject";
 | 
				
			||||||
import State from "../../State";
 | 
					import State from "../../State";
 | 
				
			||||||
import {Utils} from "../../Utils";
 | 
					import {Utils} from "../../Utils";
 | 
				
			||||||
import {UIEventSource} from "../UIEventSource";
 | 
					import {UIEventSource} from "../UIEventSource";
 | 
				
			||||||
| 
						 | 
					@ -121,7 +121,7 @@ export class Changes implements FeatureSource{
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const changes = this.createTagChangeList(basicTags, properties, id);
 | 
					        const changes = Changes.createTagChangeList(basicTags, properties, id);
 | 
				
			||||||
       
 | 
					       
 | 
				
			||||||
        console.log("New feature added and pinged")
 | 
					        console.log("New feature added and pinged")
 | 
				
			||||||
        this.features.data.push({feature:geojson, freshness: new Date()});
 | 
					        this.features.data.push({feature:geojson, freshness: new Date()});
 | 
				
			||||||
| 
						 | 
					@ -133,44 +133,8 @@ export class Changes implements FeatureSource{
 | 
				
			||||||
        return geojson;
 | 
					        return geojson;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Creates a new road with given tags that consist of the points corresponding to given nodeIDs
 | 
					 | 
				
			||||||
     * @param basicTags The tags to add to the road
 | 
					 | 
				
			||||||
     * @param nodeIDs IDs of nodes of which the road consists. Those nodes must already exist in osm or already be added to the changeset.
 | 
					 | 
				
			||||||
     * @param coordinates The coordinates correspoinding to the nodeID at the same index. Each coordinate is a [lon, lat] point
 | 
					 | 
				
			||||||
     * @return geojson A geojson representation of the created road
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public createRoad(basicTags: Tag[], nodeIDs, coordinates) {
 | 
					 | 
				
			||||||
        const osmWay = new OsmWay(this.getNewID());
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const id = "way/" + osmWay.id;
 | 
					    private static createTagChangeList(basicTags: Tag[], properties: { id: string }, id: string) {
 | 
				
			||||||
        osmWay.nodes = nodeIDs;
 | 
					 | 
				
			||||||
        const properties = {id: id};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const geojson = {
 | 
					 | 
				
			||||||
            "type": "Feature",
 | 
					 | 
				
			||||||
            "properties": properties,
 | 
					 | 
				
			||||||
            "id": id,
 | 
					 | 
				
			||||||
            "geometry": {
 | 
					 | 
				
			||||||
                "type": "LineString",
 | 
					 | 
				
			||||||
                "coordinates": coordinates
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const changes = this.createTagChangeList(basicTags, properties, id);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        console.log("New feature added and pinged")
 | 
					 | 
				
			||||||
        this.features.data.push({feature:geojson, freshness: new Date()});
 | 
					 | 
				
			||||||
        this.features.ping();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        State.state.allElements.addOrGetElement(geojson).ping();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.uploadAll([osmWay], changes);
 | 
					 | 
				
			||||||
        return geojson;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private createTagChangeList(basicTags: Tag[], properties: { id: string }, id: string) {
 | 
					 | 
				
			||||||
        // The basictags are COPIED, the id is included in the properties
 | 
					        // The basictags are COPIED, the id is included in the properties
 | 
				
			||||||
        // The tags are not yet written into the OsmObject, but this is applied onto a
 | 
					        // The tags are not yet written into the OsmObject, but this is applied onto a
 | 
				
			||||||
        const changes = [];
 | 
					        const changes = [];
 | 
				
			||||||
| 
						 | 
					@ -229,13 +193,15 @@ export class Changes implements FeatureSource{
 | 
				
			||||||
        State.state.osmConnection.UploadChangeset(
 | 
					        State.state.osmConnection.UploadChangeset(
 | 
				
			||||||
            State.state.layoutToUse.data,
 | 
					            State.state.layoutToUse.data,
 | 
				
			||||||
            State.state.allElements,
 | 
					            State.state.allElements,
 | 
				
			||||||
            function (csId) {
 | 
					            (csId) =>   Changes.createChangesetFor(csId,changedElements, newElements )
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static createChangesetFor(csId: string, changedElements: OsmObject[], newElements: OsmObject[]): string {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let modifications = "";
 | 
					        let modifications = "";
 | 
				
			||||||
        for (const element of changedElements) {
 | 
					        for (const element of changedElements) {
 | 
				
			||||||
                    if (!element.changed) {
 | 
					 | 
				
			||||||
                        continue;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
            modifications += element.ChangesetXML(csId) + "\n";
 | 
					            modifications += element.ChangesetXML(csId) + "\n";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -250,14 +216,14 @@ export class Changes implements FeatureSource{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (creations.length > 0) {
 | 
					        if (creations.length > 0) {
 | 
				
			||||||
            changes +=
 | 
					            changes +=
 | 
				
			||||||
                        "<create>" +
 | 
					                "\n<create>\n" +
 | 
				
			||||||
                creations +
 | 
					                creations +
 | 
				
			||||||
                "</create>";
 | 
					                "</create>";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (modifications.length > 0) {
 | 
					        if (modifications.length > 0) {
 | 
				
			||||||
            changes +=
 | 
					            changes +=
 | 
				
			||||||
                        "<modify>\n" +
 | 
					                "\n<modify>\n" +
 | 
				
			||||||
                modifications +
 | 
					                modifications +
 | 
				
			||||||
                "\n</modify>";
 | 
					                "\n</modify>";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -265,9 +231,7 @@ export class Changes implements FeatureSource{
 | 
				
			||||||
        changes += "</osmChange>";
 | 
					        changes += "</osmChange>";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return changes;
 | 
					        return changes;
 | 
				
			||||||
            });
 | 
					    }
 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private uploadAll(
 | 
					    private uploadAll(
 | 
				
			||||||
        newElements: OsmObject[],
 | 
					        newElements: OsmObject[],
 | 
				
			||||||
| 
						 | 
					@ -293,13 +257,4 @@ export class Changes implements FeatureSource{
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Changes the nodes of road with given id to the given nodes
 | 
					 | 
				
			||||||
     * @param roadID The ID of the road to update
 | 
					 | 
				
			||||||
     * @param newNodes The node id's the road consists of (should already be added to the changeset or in osm)
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public updateRoadCoordinates(roadID: string, newNodes: number[]) {
 | 
					 | 
				
			||||||
        // TODO
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					export default class CreateNewNodeAction {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -60,6 +60,8 @@ export abstract class OsmObject {
 | 
				
			||||||
            case("relation"):
 | 
					            case("relation"):
 | 
				
			||||||
                new OsmRelation(idN).Download(newContinuation);
 | 
					                new OsmRelation(idN).Download(newContinuation);
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                throw "Invalid road type:" + type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return src;
 | 
					        return src;
 | 
				
			||||||
| 
						 | 
					@ -150,7 +152,7 @@ export abstract class OsmObject {
 | 
				
			||||||
        const minlat = bounds[1][0]
 | 
					        const minlat = bounds[1][0]
 | 
				
			||||||
        const maxlat = bounds[0][0];
 | 
					        const maxlat = bounds[0][0];
 | 
				
			||||||
        const url = `${OsmObject.backendURL}api/0.6/map.json?bbox=${minlon},${minlat},${maxlon},${maxlat}`
 | 
					        const url = `${OsmObject.backendURL}api/0.6/map.json?bbox=${minlon},${minlat},${maxlon},${maxlat}`
 | 
				
			||||||
        Utils.downloadJson(url).then( data => {
 | 
					        Utils.downloadJson(url).then(data => {
 | 
				
			||||||
            const elements: any[] = data.elements;
 | 
					            const elements: any[] = data.elements;
 | 
				
			||||||
            const objects = OsmObject.ParseObjects(elements)
 | 
					            const objects = OsmObject.ParseObjects(elements)
 | 
				
			||||||
            callback(objects);
 | 
					            callback(objects);
 | 
				
			||||||
| 
						 | 
					@ -401,7 +403,6 @@ export class OsmWay extends OsmObject {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(id) {
 | 
					    constructor(id) {
 | 
				
			||||||
        super("way", id);
 | 
					        super("way", id);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    centerpoint(): [number, number] {
 | 
					    centerpoint(): [number, number] {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * The logic to handle relations after a way within 
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export default class RelationSplitlHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,162 +1,222 @@
 | 
				
			||||||
import {UIEventSource} from "../UIEventSource";
 | 
					import {OsmNode, OsmObject, OsmRelation, OsmWay} from "./OsmObject";
 | 
				
			||||||
import {OsmNode, OsmObject, OsmWay} from "./OsmObject";
 | 
					 | 
				
			||||||
import State from "../../State";
 | 
					 | 
				
			||||||
import {distance} from "@turf/turf";
 | 
					 | 
				
			||||||
import {GeoOperations} from "../GeoOperations";
 | 
					import {GeoOperations} from "../GeoOperations";
 | 
				
			||||||
 | 
					import State from "../../State";
 | 
				
			||||||
 | 
					import {UIEventSource} from "../UIEventSource";
 | 
				
			||||||
import {Changes} from "./Changes";
 | 
					import {Changes} from "./Changes";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					interface SplitInfo {
 | 
				
			||||||
 * Splits a road in different segments, each splitted at one of the given points (or a point on the road close to it)
 | 
					    originalIndex?: number, // or negative for new elements
 | 
				
			||||||
 * @param roadID The id of the road you want to split
 | 
					    lngLat: [number, number],
 | 
				
			||||||
 * @param points The points on the road where you want the split to occur (geojson point list)
 | 
					    doSplit: boolean
 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
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;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class SplitAction {
 | 
				
			||||||
 | 
					    private readonly roadObject: any;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 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
 | 
					     * @param roadObject: the geojson of the road object. Properties.id must be the corresponding OSM-id
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    constructor(roadObject: any) {
 | 
				
			||||||
 | 
					        this.roadObject = roadObject;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static SegmentSplitInfo(splitInfo: SplitInfo[]): SplitInfo[][] {
 | 
				
			||||||
 | 
					        const wayParts = []
 | 
				
			||||||
 | 
					        let currentPart = []
 | 
				
			||||||
 | 
					        for (const splitInfoElement of splitInfo) {
 | 
				
			||||||
 | 
					            currentPart.push(splitInfoElement)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (splitInfoElement.doSplit) {
 | 
				
			||||||
 | 
					                // We have to do a split!
 | 
				
			||||||
 | 
					                // We add the current index to the currentParts, flush it and add it again
 | 
				
			||||||
 | 
					                wayParts.push(currentPart)
 | 
				
			||||||
 | 
					                currentPart = [splitInfoElement]
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        wayParts.push(currentPart)
 | 
				
			||||||
 | 
					        return wayParts
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public DoSplit(splitPoints: any[]) {
 | 
				
			||||||
 | 
					        // We mark the new split points with a new id
 | 
				
			||||||
 | 
					        for (const splitPoint of splitPoints) {
 | 
				
			||||||
 | 
					            splitPoint.properties["_is_split_point"] = true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const self = this;
 | 
				
			||||||
 | 
					        const id = this.roadObject.properties.id
 | 
				
			||||||
 | 
					        const osmWay = <UIEventSource<OsmWay>>OsmObject.DownloadObject(id)
 | 
				
			||||||
 | 
					        const partOf = OsmObject.DownloadReferencingRelations(id)
 | 
				
			||||||
 | 
					        osmWay.map(originalElement => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if(originalElement === undefined || partOf === undefined){
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            const changes = State.state?.changes ?? new Changes();
 | 
				
			||||||
 | 
					            // First, calculate splitpoints and remove points close to one another
 | 
				
			||||||
 | 
					            const splitInfo = self.CalculateSplitCoordinates(splitPoints)
 | 
				
			||||||
 | 
					            // Now we have a list with e.g. 
 | 
				
			||||||
 | 
					            // [ { originalIndex: 0}, {originalIndex: 1, doSplit: true}, {originalIndex: 2}, {originalIndex: undefined, doSplit: true}, {originalIndex: 3}]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Lets change 'originalIndex' to the actual node id first:
 | 
				
			||||||
 | 
					            for (const element of splitInfo) {
 | 
				
			||||||
 | 
					                if (element.originalIndex >= 0) {
 | 
				
			||||||
 | 
					                    element.originalIndex = originalElement.nodes[element.originalIndex]
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    element.originalIndex = changes.getNewID();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Next up is creating actual parts from this
 | 
				
			||||||
 | 
					            const wayParts = SplitAction.SegmentSplitInfo(splitInfo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Allright! At this point, we have our new ways!
 | 
				
			||||||
 | 
					            // Which one is the longest of them (and can keep the id)?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let longest = undefined;
 | 
				
			||||||
 | 
					            for (const wayPart of wayParts) {
 | 
				
			||||||
 | 
					                if (longest === undefined) {
 | 
				
			||||||
 | 
					                    longest = wayPart;
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (wayPart.length > longest.length) {
 | 
				
			||||||
 | 
					                    longest = wayPart
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const newOsmObjects: OsmObject[] = []
 | 
				
			||||||
 | 
					            const modifiedObjects: OsmObject[] = []
 | 
				
			||||||
 | 
					            // Let's create the new points as needed
 | 
				
			||||||
 | 
					            for (const element of splitInfo) {
 | 
				
			||||||
 | 
					                if (element.originalIndex >= 0) {
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                const node = new OsmNode(element.originalIndex)
 | 
				
			||||||
 | 
					                node.lon = element.lngLat[0]
 | 
				
			||||||
 | 
					                node.lat = element.lngLat[1]
 | 
				
			||||||
 | 
					                newOsmObjects.push(node)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const newWayIds: number[] = []
 | 
				
			||||||
 | 
					            // Lets create OsmWays based on them
 | 
				
			||||||
 | 
					            for (const wayPart of wayParts) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let isOriginal = wayPart === longest
 | 
				
			||||||
 | 
					                if(isOriginal){
 | 
				
			||||||
 | 
					                    // We change the actual element!
 | 
				
			||||||
 | 
					                    originalElement.nodes = wayPart.map(p => p.originalIndex);
 | 
				
			||||||
 | 
					                    originalElement.changed = true;
 | 
				
			||||||
 | 
					                    modifiedObjects.push(originalElement)
 | 
				
			||||||
 | 
					                }else{
 | 
				
			||||||
 | 
					                    let id = changes.getNewID();
 | 
				
			||||||
 | 
					                    const way = new OsmWay(id)
 | 
				
			||||||
 | 
					                    way.tags = originalElement.tags;
 | 
				
			||||||
 | 
					                    way.nodes = wayPart.map(p => p.originalIndex);
 | 
				
			||||||
 | 
					                    way.changed = true;
 | 
				
			||||||
 | 
					                    newOsmObjects.push(way)
 | 
				
			||||||
 | 
					                        newWayIds.push(way.id)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					             
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // At last, we still have to check that we aren't part of a relation...
 | 
				
			||||||
 | 
					            // At least, the order of the ways is identical, so we can keep the same roles
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modifiedObjects.push(...SplitAction.UpdateRelations(partOf.data, newWayIds, originalElement))
 | 
				
			||||||
 | 
					            // And we have our objects!
 | 
				
			||||||
 | 
					            // Time to upload
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            console.log(Changes.createChangesetFor("123", modifiedObjects, newOsmObjects))
 | 
				
			||||||
 | 
					        }, [partOf])
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static UpdateRelations(data: OsmRelation[], newWayIds: number[], originalElement: OsmWay):OsmRelation[]{
 | 
				
			||||||
 | 
					        // TODO
 | 
				
			||||||
 | 
					        return []
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Calculates the actual points to split
 | 
				
			||||||
 | 
					     * If another point is closer then ~5m, we reuse that point
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private CalculateSplitCoordinates(
 | 
				
			||||||
 | 
					        splitPoints: any[],
 | 
				
			||||||
 | 
					        toleranceInM = 5): SplitInfo[] {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const allPoints = [...splitPoints];
 | 
				
			||||||
 | 
					        // We have a bunch of coordinates here: [ [lat, lon], [lat, lon], ...] ...
 | 
				
			||||||
 | 
					        const originalPoints: [number, number][] = this.roadObject.geometry.coordinates
 | 
				
			||||||
 | 
					        // We project them onto the line (which should yield pretty much the same point
 | 
				
			||||||
 | 
					        for (let i = 0; i < originalPoints.length; i++) {
 | 
				
			||||||
 | 
					            let originalPoint = originalPoints[i];
 | 
				
			||||||
 | 
					            let projected = GeoOperations.nearestPoint(this.roadObject, originalPoint)
 | 
				
			||||||
 | 
					            projected.properties["_is_split_point"] = false
 | 
				
			||||||
 | 
					            projected.properties["_original_index"] = i
 | 
				
			||||||
 | 
					            allPoints.push(projected)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // At this point, we have a list of both the split point and the old points, with some properties to discriminate between them
 | 
				
			||||||
 | 
					        // We sort this list so that the new points are at the same location
 | 
				
			||||||
 | 
					        allPoints.sort((a, b) => a.properties.location - b.properties.location)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // When this is done, we check that no now point is too close to an already existing point and no very small segments get created
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (let i = allPoints.length - 1; i > 0; i--) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const point = allPoints[i];
 | 
				
			||||||
 | 
					            if (point.properties._original_index !== undefined) {
 | 
				
			||||||
 | 
					                // This point is already in OSM - we have to keep it!
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (i != allPoints.length - 1) {
 | 
				
			||||||
 | 
					                const prevPoint = allPoints[i + 1]
 | 
				
			||||||
 | 
					                const diff = Math.abs(point.properties.location - prevPoint.properties.location) * 1000
 | 
				
			||||||
 | 
					                if (diff <= toleranceInM) {
 | 
				
			||||||
 | 
					                    // To close to the previous point! We delete this point...
 | 
				
			||||||
 | 
					                    allPoints.splice(i, 1)
 | 
				
			||||||
 | 
					                    // ... and mark the previous point as a split point
 | 
				
			||||||
 | 
					                    prevPoint.properties._is_split_point = true
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (i > 0) {
 | 
				
			||||||
 | 
					                const nextPoint = allPoints[i - 1]
 | 
				
			||||||
 | 
					                const diff = Math.abs(point.properties.location - nextPoint.properties.location) * 1000
 | 
				
			||||||
 | 
					                if (diff <= toleranceInM) {
 | 
				
			||||||
 | 
					                    // To close to the next point! We delete this point...
 | 
				
			||||||
 | 
					                    allPoints.splice(i, 1)
 | 
				
			||||||
 | 
					                    // ... and mark the next point as a split point
 | 
				
			||||||
 | 
					                    nextPoint.properties._is_split_point = true
 | 
				
			||||||
 | 
					                    // noinspection UnnecessaryContinueJS
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            // We don't have to remove this point...
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const splitInfo: SplitInfo[] = []
 | 
				
			||||||
 | 
					        let nextId = -1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (const p of allPoints) {
 | 
				
			||||||
 | 
					            let index = p.properties._original_index
 | 
				
			||||||
 | 
					            if (index === undefined) {
 | 
				
			||||||
 | 
					                index = nextId;
 | 
				
			||||||
 | 
					                nextId--;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            const splitInfoElement = {
 | 
				
			||||||
 | 
					                originalIndex: index,
 | 
				
			||||||
 | 
					                lngLat: p.geometry.coordinates,
 | 
				
			||||||
 | 
					                doSplit: p.properties._is_split_point
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            splitInfo.push(splitInfoElement)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return splitInfo
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,13 +7,15 @@ import State from "../../State";
 | 
				
			||||||
import ShowDataLayer from "../ShowDataLayer";
 | 
					import ShowDataLayer from "../ShowDataLayer";
 | 
				
			||||||
import {GeoOperations} from "../../Logic/GeoOperations";
 | 
					import {GeoOperations} from "../../Logic/GeoOperations";
 | 
				
			||||||
import {LeafletMouseEvent} from "leaflet";
 | 
					import {LeafletMouseEvent} from "leaflet";
 | 
				
			||||||
import LayerConfig from "../../Customizations/JSON/LayerConfig";
 | 
					 | 
				
			||||||
import Combine from "../Base/Combine";
 | 
					import Combine from "../Base/Combine";
 | 
				
			||||||
import {Button} from "../Base/Button";
 | 
					import {Button} from "../Base/Button";
 | 
				
			||||||
import Translations from "../i18n/Translations";
 | 
					import Translations from "../i18n/Translations";
 | 
				
			||||||
import {splitRoad} from "../../Logic/Osm/SplitAction";
 | 
					import LayoutConfig from "../../Customizations/JSON/LayoutConfig";
 | 
				
			||||||
 | 
					import SplitAction from "../../Logic/Osm/SplitAction";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class SplitRoadWizard extends Toggle {
 | 
					export default class SplitRoadWizard extends Toggle {
 | 
				
			||||||
 | 
					    private static splitLayout = new UIEventSource(SplitRoadWizard.GetSplitLayout())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * A UI Element used for splitting roads
 | 
					     * A UI Element used for splitting roads
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
| 
						 | 
					@ -23,25 +25,25 @@ export default class SplitRoadWizard extends Toggle {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const t = Translations.t.split;
 | 
					        const t = Translations.t.split;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Contains the points on the road that are selected to split on
 | 
					        // Contains the points on the road that are selected to split on - contains geojson points with extra properties such as 'location' with the distance along the linestring
 | 
				
			||||||
        const splitPositions = new UIEventSource([]);
 | 
					        const splitPoints = new UIEventSource<{feature: any, freshness: Date}[]>([]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Toggle variable between show split button and map
 | 
					        // Toggle variable between show split button and map
 | 
				
			||||||
        const splitClicked = new UIEventSource<boolean>(true); // todo: -> false
 | 
					        const splitClicked = new UIEventSource<boolean>(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Minimap on which you can select the points to be splitted
 | 
					        // Minimap on which you can select the points to be splitted
 | 
				
			||||||
        const miniMap = new Minimap({background: State.state.backgroundLayer});
 | 
					        const miniMap = new Minimap({background: State.state.backgroundLayer});
 | 
				
			||||||
        miniMap.SetStyle("width: 100%; height: 50rem;");
 | 
					        miniMap.SetStyle("width: 100%; height: 50rem;");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Define how a cut is displayed on the map
 | 
					        // Define how a cut is displayed on the map
 | 
				
			||||||
        const layoutConfigJson = {id: "splitpositions", source: {osmTags: "_cutposition=yes"}, icon: "./assets/svg/plus.svg"}
 | 
					 | 
				
			||||||
        State.state.layoutToUse.data.layers.push(new LayerConfig(layoutConfigJson,undefined,"Split Road Wizard"))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Load the road with given id on the minimap
 | 
					        // Load the road with given id on the minimap
 | 
				
			||||||
        const roadElement = State.state.allElements.ContainingFeatures.get(id)
 | 
					        const roadElement = State.state.allElements.ContainingFeatures.get(id)
 | 
				
			||||||
 | 
					        const splitAction = new SplitAction(roadElement)
 | 
				
			||||||
        const roadEventSource = new UIEventSource([{feature: roadElement, freshness: new Date()}]);
 | 
					        const roadEventSource = new UIEventSource([{feature: roadElement, freshness: new Date()}]);
 | 
				
			||||||
        // Datalayer displaying the road and the cut points (if any)
 | 
					        // Datalayer displaying the road and the cut points (if any)
 | 
				
			||||||
        const dataLayer = new ShowDataLayer(roadEventSource, miniMap.leafletMap, State.state.layoutToUse, false, true);
 | 
					        new ShowDataLayer(roadEventSource, miniMap.leafletMap, State.state.layoutToUse, false, true);
 | 
				
			||||||
 | 
					        new ShowDataLayer(splitPoints, miniMap.leafletMap, SplitRoadWizard.splitLayout, false, false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * Handles a click on the overleaf map.
 | 
					         * Handles a click on the overleaf map.
 | 
				
			||||||
| 
						 | 
					@ -54,17 +56,14 @@ export default class SplitRoadWizard extends Toggle {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Update point properties to let it match the layer
 | 
					            // Update point properties to let it match the layer
 | 
				
			||||||
            pointOnRoad.properties._cutposition = "yes";
 | 
					            pointOnRoad.properties._cutposition = "yes";
 | 
				
			||||||
            pointOnRoad._matching_layer_id = "splitpositions";
 | 
					            pointOnRoad["_matching_layer_id"] = "splitpositions";
 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Add it to the list of all points and notify observers
 | 
					 | 
				
			||||||
            splitPositions.data.push(pointOnRoad);
 | 
					 | 
				
			||||||
            splitPositions.ping();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // let the state remember the point, to be able to retrieve it later by id
 | 
					            // let the state remember the point, to be able to retrieve it later by id
 | 
				
			||||||
            State.state.allElements.addOrGetElement(pointOnRoad);
 | 
					            State.state.allElements.addOrGetElement(pointOnRoad);
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            roadEventSource.data.push({feature: pointOnRoad, freshness: new Date()}); // show the point on the data layer
 | 
					            // Add it to the list of all points and notify observers
 | 
				
			||||||
            roadEventSource.ping(); // not updated using .setData, so manually ping observers
 | 
					            splitPoints.data.push({feature: pointOnRoad, freshness: new Date()}); // show the point on the data layer
 | 
				
			||||||
 | 
					            splitPoints.ping(); // not updated using .setData, so manually ping observers
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // When clicked, pass clicked location coordinates to onMapClick function
 | 
					        // When clicked, pass clicked location coordinates to onMapClick function
 | 
				
			||||||
| 
						 | 
					@ -88,19 +87,16 @@ export default class SplitRoadWizard extends Toggle {
 | 
				
			||||||
            State.state.osmConnection.isLoggedIn)
 | 
					            State.state.osmConnection.isLoggedIn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Save button
 | 
					        // Save button
 | 
				
			||||||
        const saveButton =  new Button("Split here", () => splitRoad(id, splitPositions.data));
 | 
					        const saveButton = new Button("Split here", () => splitAction.DoSplit(splitPoints.data));
 | 
				
			||||||
        saveButton.SetClass("block btn btn-primary");
 | 
					        saveButton.SetClass("block btn btn-primary");
 | 
				
			||||||
        const disabledSaveButton = new Button("Split here", undefined);
 | 
					        const disabledSaveButton = new Button("Split here", undefined);
 | 
				
			||||||
        disabledSaveButton.SetClass("block btn btn-disabled");
 | 
					        disabledSaveButton.SetClass("block btn btn-disabled");
 | 
				
			||||||
        // Only show the save button if there are split points defined
 | 
					        // Only show the save button if there are split points defined
 | 
				
			||||||
        const saveToggle = new Toggle(disabledSaveButton, saveButton, splitPositions.map((data) => data.length === 0))
 | 
					        const saveToggle = new Toggle(disabledSaveButton, saveButton, splitPoints.map((data) => data.length === 0))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const cancelButton = new Button("Cancel", () => {
 | 
					        const cancelButton = new Button("Cancel", () => {
 | 
				
			||||||
            splitClicked.setData(false);
 | 
					            splitClicked.setData(false);
 | 
				
			||||||
 | 
					            splitPoints.setData([]);
 | 
				
			||||||
            splitPositions.setData([]);
 | 
					 | 
				
			||||||
            // Only keep showing the road, the cutpoints must be removed from the map
 | 
					 | 
				
			||||||
            roadEventSource.setData([roadEventSource.data[0]])
 | 
					 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        cancelButton.SetClass("block btn btn-secondary");
 | 
					        cancelButton.SetClass("block btn btn-secondary");
 | 
				
			||||||
| 
						 | 
					@ -111,4 +107,21 @@ export default class SplitRoadWizard extends Toggle {
 | 
				
			||||||
        super(mapView, splitToggle, splitClicked);
 | 
					        super(mapView, splitToggle, splitClicked);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static GetSplitLayout(): LayoutConfig {
 | 
				
			||||||
 | 
					        return new LayoutConfig({
 | 
				
			||||||
 | 
					            maintainer: "mapcomplete",
 | 
				
			||||||
 | 
					            language: [],
 | 
				
			||||||
 | 
					            startLon: 0,
 | 
				
			||||||
 | 
					            startLat: 0,
 | 
				
			||||||
 | 
					            description: undefined,
 | 
				
			||||||
 | 
					            icon: "", startZoom: 0,
 | 
				
			||||||
 | 
					            title: "Split locations",
 | 
				
			||||||
 | 
					            version: "",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            id: "splitpositions",
 | 
				
			||||||
 | 
					            layers: [{id: "splitpositions", source: {osmTags: "_cutposition=yes"}, icon: "./assets/svg/plus.svg"}]
 | 
				
			||||||
 | 
					        }, true, "split road wizard layout")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										35
									
								
								test.ts
									
										
									
									
									
								
							
							
						
						
									
										35
									
								
								test.ts
									
										
									
									
									
								
							| 
						 | 
					@ -1,6 +1,5 @@
 | 
				
			||||||
import SplitRoadWizard from "./UI/Popup/SplitRoadWizard";
 | 
					import SplitAction from "./Logic/Osm/SplitAction";
 | 
				
			||||||
import State from "./State";
 | 
					import {GeoOperations} from "./Logic/GeoOperations";
 | 
				
			||||||
import {AllKnownLayouts} from "./Customizations/AllKnownLayouts";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const way = {
 | 
					const way = {
 | 
				
			||||||
    "type": "Feature",
 | 
					    "type": "Feature",
 | 
				
			||||||
| 
						 | 
					@ -47,7 +46,31 @@ const way = {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
State.state = new State(AllKnownLayouts.allKnownLayouts.get("fietsstraten"));
 | 
					let splitPoint = {
 | 
				
			||||||
 | 
					    "type": "Feature",
 | 
				
			||||||
 | 
					    "properties": {},
 | 
				
			||||||
 | 
					    "geometry": {
 | 
				
			||||||
 | 
					        "type": "Point",
 | 
				
			||||||
 | 
					        "coordinates": [
 | 
				
			||||||
 | 
					            4.490211009979248,
 | 
				
			||||||
 | 
					            51.2041509326002
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let splitClose = {
 | 
				
			||||||
 | 
					    "type": "Feature",
 | 
				
			||||||
 | 
					    "properties": {},
 | 
				
			||||||
 | 
					    "geometry": {
 | 
				
			||||||
 | 
					        "type": "Point",
 | 
				
			||||||
 | 
					        "coordinates": [
 | 
				
			||||||
 | 
					            4.489563927054405,
 | 
				
			||||||
 | 
					            51.2047546593862
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					// State.state = new State(AllKnownLayouts.allKnownLayouts.get("fietsstraten"));
 | 
				
			||||||
// add road to state
 | 
					// add road to state
 | 
				
			||||||
State.state.allElements.addOrGetElement(way);
 | 
					// State.state.allElements.addOrGetElement(way);
 | 
				
			||||||
new SplitRoadWizard("way/23583625").AttachTo("maindiv")
 | 
					new SplitAction(way).DoSplit([splitPoint, splitClose].map(p => GeoOperations.nearestPoint(way,<[number, number]> p.geometry.coordinates)))
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue