javascriptleafletreact-leafletleaflet-routing-machine

React-Leaflet/React-Routing-Machine: Remove route and waypoints


I am using the following packages for my map:

    "leaflet-routing-machine": "^3.2.12",
    "leaflet": "^1.7.1",
    "react-leaflet": "^2.7.0",

Essentially I have a Routing machine component which I've integrated with my Map and Markers i.e. (upon clicking two points on the map you get the route) you can drag each and the route updates!

However at this point I have a button which resets everything, clears the markers, the associated LAT & LONG. But I just want the route to reset as well.

enter image description here

You can see those previous routes (in beautiful "chartreuse") stay on the map.

Right now I have a function which is supposed to control when the component is visible:

function clearMarkers(){
  setIsRoutingDone(false);
}

 {isRoutingDone &&  <Routing isRoutingDone={isRoutingDone} map={map} myMapRef={myMapRef} icon={{startIcon, endIcon}} userLocation={userLocation}  coords={{fromLat, fromLon, toLat, toLon}}  />}

This is my Routing Machine:

import { MapLayer } from "react-leaflet";
import L from "leaflet";
import "leaflet-routing-machine";
import { withLeaflet } from "react-leaflet";


class Routing extends MapLayer {
 constructor(props) {
    super(props);
  }

  createLeafletElement() {
    const { map, coords, icon,  removeFrom, removeTo } = this.props;


    var dStart = L.latLng(coords.fromLat, coords.fromLon);
    var dGoal = L.latLng(coords.toLat, coords.toLon);


    this.leafletElement = L.Routing.control({
      collapsible: true,
       lineOptions: {
      styles: [{color: 'chartreuse', opacity: 1, weight: 5}]
     },
      waypoints: [dStart, dGoal],
      createMarker: function(i, waypoints, n) {
        if (i === 0) {
         marker_icon = icon.startIcon;
        }


       var marker_icon;
        if (i === 0) {
         marker_icon = icon.startIcon;
        }
        else if (i == n - 1) {
         marker_icon = icon.endIcon
        }
        var marker = L.marker(i === 0 ? dStart : dGoal,{
         draggable: true,
         icon: marker_icon
        });
        return marker;
     }
    }).addTo(map.leafletElement);

    return this.leafletElement.getPlan();
  }

  updateLeafletElement(props) {
    if(this.leafletElement){
      if(this.props.isRoutingDone === false){
this.leafletElement.spliceWaypoints(0, 2); // <-- removes your route
      }
    }
  }

}
export default withLeaflet(Routing);

Actually I logged something in the updateLeafletElement function and zzilch.

And this is my map:

    import React, { useState, useEffect, useRef } from 'react'
    import { Map, Marker } from 'react-leaflet';
    import LocateControl from '../LocateControl/LocateControl.jsx';
    import MapboxLayer from '../MapboxLayer/MapboxLayer.jsx';
    import Routing from '../RoutingMachine/RoutingMachine.jsx'
    
    export default function MyMap({getAddressFromLatLong, hillfinderFormButtonRef, setCurrentLocation, setCurrentDestination}) {
 
    var myMapRef = useRef();
        
      useEffect(() => {
       hillfinderFormButtonRef.current = clearMarkers;
    
        return() => {
          hillfinderFormButtonRef.current = null;
        }
      }, []);
    
    
    function resetHandler (){
        return myMapRef.current();
      };
    
    
    function clearMarkers(){
      console.log("markerData ", markerData);
      setMarkerData(markerData => [], ...markerData);
      setFromLat(fromLat => null);
      setFromLon(fromLon => null);
      setToLat(toLat => null)
      setToLon(toLon => null)
      setFrom(from => 0);
      setTo(to => 0);
      setIsRoutingDone(false);
      // setRemoveFrom(removeFrom => null)
      // setRemoveTo(removeTo => null)
    }
    

  function saveMap(map){
    setMap(map);
  }

  function handleOnLocationFound(e){
   setUserLocation(e.latlng)
  }
    
  function markerClick(e){
   e.originalEvent.view.L.DomEvent.stopPropagation(e)
  }

  return (
  <Map animate={animate} center={userLocation} onClick={setMarkers} onLocationFound={handleOnLocationFound} zoom={zoom} ref={saveMap}>

     {markerData && markerData.map((element, index) => {
      return (
      <Marker
        key={index}
        marker_index={index}
        position={element}
        draggable={true}
        onClick={markerClick}
        onDragend={updateMarker}
        icon={element.id === 0 ? startIcon : endIcon}
      />
      )
     })}
    <MapboxLayer
      accessToken={MAPBOX_ACCESS_TOKEN}
      style="mapbox://styles/mapbox/streets-v9"
    />
    <LocateControl startDirectly />

     {isRoutingDone &&  <Routing isRoutingDone={isRoutingDone} map={map} myMapRef={myMapRef} icon={{startIcon, endIcon}} userLocation={userLocation}  coords={{fromLat, fromLon, toLat, toLon}}  />}
  </Map>

  )
}

I got rid of the the code that's not important to the problem at hand!

Thanks in advance!


Solution

  • I actually wound up solving it myself!

    Think the head scratcher was while react-leaflet has its lifeCycle methods, i.e. createLeaflet, updateLeafletElement you should not forget the regular React life methods!

    Not sure if they overlap, but I found adding componentDidMount:

      componentDidMount() {
        const { map } = this.props;
    
        map.addControl(this.routing);
      }
    

    with updateLeafletElement (I'm using the API for the function correctly now) it accepts a fromProps and toProps to just check the value of the prop I pass to the Routing Machine...

      updateLeafletElement(fromProps, toProps) {
        if (toProps.removeRoutingMachine !== false) {
          this.routing.setWaypoints([]);
        }
      }
    
     
    

    Finally on unmount, use the removeControl method on the map you pass into the Routing Machine to remove the Router-Machine!

    import { MapLayer } from 'react-leaflet';
    import L from 'leaflet';
    import 'leaflet-routing-machine';
    import { withLeaflet } from 'react-leaflet';
    
    class Routing extends MapLayer {
      constructor(props) {
        super(props);
      }
    
      createLeafletElement(props) {
        const { map, coords, icon } = this.props;
    
        var dStart = L.latLng(coords.fromLat, coords.fromLon);
        var dGoal = L.latLng(coords.toLat, coords.toLon);
    
        if (map && !this.routing) {
          this.routing = L.Routing.control({
            collapsible: true,
            position: 'bottomleft',
            lineOptions: {
              styles: [{ color: 'chartreuse', opacity: 1, weight: 5 }]
            },
            waypoints: [dStart, dGoal],
            createMarker: function(i, waypoints, n) {
              var marker_icon;
    
              if (i === 0) {
                marker_icon = icon.startIcon;
              } else if (i == n - 1) {
                marker_icon = icon.endIcon;
              }
              var marker = L.marker(i === 0 ? dStart : dGoal, {
                draggable: true,
                icon: marker_icon
              });
              return marker;
            }
          });
        }
    
        return this.routing.getPlan();
      }
    
      componentDidMount() {
        const { map } = this.props;
    
        console.log('map ', map);
        map.addControl(this.routing);
      }
    
      updateLeafletElement(fromProps, toProps) {
        if (toProps.removeRoutingMachine !== false) {
          this.routing.setWaypoints([]);
        }
      }
    
      componentWillUnmount() {
        this.destroyRouting();
      }
    
      destroyRouting() {
        if (this.props.map) {
          this.props.map.removeControl(this.routing);
        }
      }
    }
    
    export default withLeaflet(Routing);
    

    Hope this helps! FYI: this is the app I'm using the Routing Machine in case you'd like to browse the other integrations...