Open antonioOrtiz opened 2 years ago
If you want the markers only for the waypoints you set yourself, then the plan takes a createMarker
option where you can return a custom marker
const control = L.Routing.Control({
plan = L.Routing.Plan({
createMarker = () => {
return L.Marker(...)
},
}),
});
The coordinates are all the points the routing engine found. So let's say you want to go from A to Z, then the routing engine might suggest you to travel via coordinates B, C, etc. What is returned there is entirely up to the routing engine and plugin implementation. The OSRM plugin for example, does it this way. If you want to work with these more than just your waypoints, it's probably a good idea to implement some form of custom routing backend, where you extend the existing one and modify the result to your liking.
An example of how extending could work (if you're not using typescript) can be found in the mapbox implementation
Hi there, Thanks for replying! I appreciate it! Sorry for the tardy reply! I tried what you recommended, but it caused nothing (regarding Markers) to render. This is my Routing Machine:
const instance = L.Routing.control({
createMarker: function (i, wp, nWps) {
if (i === 0) {
return L.marker(wp.latLng, {
icon: startIcon,
draggable: true,
keyboard: true,
alt: 'current location'
})
}
if (i === nWps - 1) {
return L.marker(wp.latLng, {
icon: finishIcon,
draggable: true,
alt: 'current destination'
})
}
},
plan: new L.Routing.plan({
createMarker: function (i, wp, nWps) {
if (i !== 0 || i !== nWps - 1) {
console.log('wp', wp)
}
},
}),
waypoints: [
L.latLng(
startingPoints[0]?.highestEl?.latlng?.lat,
startingPoints[0]?.highestEl?.latlng?.lng),
L.latLng(
startingPoints[0]?.lowestEl?.latlng?.lat,
startingPoints[0]?.lowestEl?.latlng?.lng
),
],
});
I figured I do a conditional in the plan to see all the Markers which are not the first or last, and then what I would do is push in those coordinates which make the route.
But now that I think about it, won't this create markers using the plan prop? What I would like is to change the lat and lng of the points in between the starting and ending point thereby augmenting the route.
This is my repo if you'd like to see it all the code!
But now that I think about it, won't this create markers using the plan prop
You're absolutely right, not sure how I got to the createMarker function. Sorry about that!
What I would like is to change the lat and lng of the points in between the starting and ending point thereby augmenting the route
Then I believe your only option would be a custom router implementation. In general it could look something like this
const customCallback = (callback) => (context, error, routes) => {
for (const route of routes) {
route.coordinates = [...route.coordinates, L.latLng(12.33, 49.6753)];
}
callback(context, error, routes);
};
const customRouter = OSRMv1.extend({
initialize: function(options) {
L.Routing.OSRMv1.prototype.initialize.call(this, options);
},
route: function(waypoints, callback, context, options) {
const originalCallback = options.callback;
L.Routing.OSRMv1.prototype.route.call(waypoints, customCallback(originalCallback), this, options);
}
});
Hey thanks for posting this my friend. I am going to give a whirl now!
Hi there again. I tried to integrate your example like so:
const routingControl = L.Routing.control({
addWaypoints: false,
collapsible: true,
draggableWaypoints: true,
lineOptions: {
styles: [{ color: 'chartreuse', opacity: 1, weight: 5 }]
},
position: 'bottomright',
router: L.Routing.OSRMv1.extend({
initialize: function (options) {
L.Routing.OSRMv1.prototype.initialize.call(this, options);
},
route: function (waypoints, callback, context, options) {
const originalCallback = options.callback;
L.Routing.OSRMv1.prototype.route.call(waypoints, customCallback(originalCallback), this, options);
}
}),
routeWhileDragging: true,
show: true,
showAlternatives: false,
waypoints
}).addTo(map)
But I am getting :
TypeError: this._router.route is not a function
I am trying to debug it but wondering if there is documentation for adding this?
You're on the right track. Unfortunately, there isn't any great example other than the existing implementations.
The trick to all of them is that they extend Leaflet's L.Class
interface. This is more or less equivalent to plain JS classes where you need to initialize them by calling new ...
.
So instead of
router: L.Routing.OSRMv1.extend({
initialize: function (options) {
L.Routing.OSRMv1.prototype.initialize.call(this, options);
},
route: function (waypoints, callback, context, options) {
const originalCallback = options.callback;
L.Routing.OSRMv1.prototype.route.call(waypoints, customCallback(originalCallback), this, options);
}
}),
you'll have to do
const router = L.Routing.OSRMv1.extend({
initialize: function (options) {
L.Routing.OSRMv1.prototype.initialize.call(this, options);
},
route: function (waypoints, callback, context, options) {
const originalCallback = options.callback;
L.Routing.OSRMv1.prototype.route.call(waypoints, customCallback(originalCallback), this, options);
}
});
const routingControl = L.Routing.control({
addWaypoints: false,
collapsible: true,
draggableWaypoints: true,
lineOptions: {
styles: [{ color: 'chartreuse', opacity: 1, weight: 5 }]
},
position: 'bottomright',
router: new router(),
routeWhileDragging: true,
show: true,
showAlternatives: false,
waypoints
}).addTo(map)
Hi there thanks for the information! And the help with this. So it is fair to say, this is just adding props to a function to override the L.Routing.OSRMv1
So when you call .extend
and pass that object, you are re-writing props?
Like if you console logged L.Routing.OSRMv1
it would return...
{
initialize: function (options) {
L.Routing.OSRMv1.prototype.initialize.call(this, options);
},
route: function (waypoints, callback, context, options) {
const originalCallback = options.callback;
L.Routing.OSRMv1.prototype.route.call(waypoints, customCallback(originalCallback), this, options);
}
}
However using what you recommended I am getting this error.
leaflet-routing-machine.js?0f18:17955 Uncaught TypeError: Cannot read properties of undefined (reading 'routingOptions')
at Array.route (leaflet-routing-machine.js?0f18:17955:25)
at NewClass.route (index.js?3b09:31:7)
at NewClass.route (leaflet-routing-machine.js?0f18:16151:1)
at NewClass.onAdd (leaflet-routing-machine.js?0f18:15919:1)
at NewClass.addTo (leaflet-src.js?af6a:4783:1)
at eval (index.js?3b09:93:8)
at invokePassiveEffectCreate (react-dom.development.js?ac89:23487:1)
at HTMLUnknownElement.callCallback (react-dom.development.js?ac89:3945:1)
at Object.invokeGuardedCallbackDev (react-dom.development.js?ac89:3994:1)
at invokeGuardedCallback (react-dom.development.js?ac89:4056:1)
at flushPassiveEffectsImpl (react-dom.development.js?ac89:23574:1)
at unstable_runWithPriority (scheduler.development.js?bcd2:468:1)
at runWithPriority$1 (react-dom.development.js?ac89:11276:1)
at flushPassiveEffects (react-dom.development.js?ac89:23447:1)
at eval (react-dom.development.js?ac89:23324:1)
at workLoop (scheduler.development.js?bcd2:417:1)
at flushWork (scheduler.development.js?bcd2:390:1)
at MessagePort.performWorkUntilDeadline (scheduler.development.js?bcd2:157:1)
export function RoutingMachine({ startingPoints }) {
const map = useMap();
const customCallback = (callback) => (context, error, routes) => {
for (const route of routes) {
route.coordinates = [...route.coordinates, L.latLng(12.33, 49.6753)];
}
callback(context, error, routes);
};
const router = L.Routing.OSRMv1.extend({
initialize: function (options) {
L.Routing.OSRMv1.prototype.initialize.call(this, options);
},
route: function (waypoints, callback, context, options) {
console.log("waypoints ", waypoints);
console.log("callback ", callback);
console.log("context", context);
console.log("options ", options);
const originalCallback = options.callback;
console.log("originalCallback ", originalCallback);
L.Routing.OSRMv1.prototype.route.call(
waypoints,
customCallback(originalCallback),
this,
options
);
},
});
useEffect(() => {
if (!map) return;
const waypoints = [
L.latLng(
startingPoints[0]?.highestEl?.latlng?.lat,
startingPoints[0]?.highestEl?.latlng?.lng
),
L.latLng(
startingPoints[0]?.lowestEl?.latlng?.lat,
startingPoints[0]?.lowestEl?.latlng?.lng
),
];
const routingControl = L.Routing.control({
addWaypoints: false,
collapsible: true,
createMarker: function (i, wp, nWps) {
if (i === 0) {
return L.marker(wp.latLng, {
icon: startIcon,
draggable: true,
keyboard: true,
alt: "current location",
});
}
if (i === nWps - 1) {
return L.marker(wp.latLng, {
icon: finishIcon,
draggable: true,
alt: "current destination",
});
}
},
draggableWaypoints: true,
fitSelectedRoutes: true,
geocoder: L.Control.Geocoder.nominatim(),
lineOptions: {
styles: [{ color: "chartreuse", opacity: 1, weight: 5 }],
},
position: "bottomright",
routeWhileDragging: true,
router: new router(),
show: true,
showAlternatives: false,
waypoints,
}).addTo(map);
return () => map.removeControl(routingControl);
}, [map]);
return null;
}
Also and finally I thought you would be doing something like this with the new router()
const routerInstance = new router()
And then in the Routing control using it like this:
const routingControl = L.Routing.control({
...other props...
router: routerInstance.route({...pass some kind of option object}),
...other props...
}).addTo(map);
This is the repo if you need it...
Again thanks for the help!
So when you call .extend and pass that object, you are re-writing props?
You could say so. In React terms, extending would probably be like writing a higher order component (HOC).
However using what you recommended I am getting this error.
My bad, I forgot Leaflet requires context as the first argument (unless you bind it to a specific instance?).
L.Routing.OSRMv1.prototype.route.call(
this, // this one is important. See https://leafletjs.com/examples/extending/extending-1-classes.html
waypoints,
customCallback(originalCallback),
this,
options
);
That should work. Also running your project, I noticed you'll have to swap the arguments in the custom callback
const customCallback = (callback) => (context, routes, error) => {
if(!routes) return
for (const route of routes) {
route.coordinates = [...route.coordinates, L.latLng(12.33, 49.6753)];
}
callback(context, error, routes);
};
Also and finally I thought you would be doing something like this with the new router() const routerInstance = new router() And then in the Routing control using it like this: const routingControl = L.Routing.control({ ...other props... router: routerInstance.route({...pass some kind of option object}), ...other props... }).addTo(map);
const routerInstance = new router()
Would work, since it creates an instance of the custom router class
routerInstance.route({...pass some kind of option object}),
This returns nothing, so we can't just pass it to our LRM and say it's a router. LRM takes care of the routing itself, if the waypoints/coordinates change. It only needs a router with a route function to do that. You could set autoRoute
to false and call routerInstance.route()
manually in a useEffect
of some sort if you wanted to
While writing this, I realized the whole extend thing isn't really necessary anymore since JS supports native classes now. So here's a shorter, simpler version of the extend thing using ES6
const customCallback = (callback) => (context, routes, error) => {
if (!routes) {
return;
}
for (const route of routes) {
route.coordinates = [...route.coordinates, L.latLng(12.33, 49.6753)];
}
callback(context, error, routes);
};
class CustomRouter extends L.Routing.OSRMv1 {
constructor(options) {
super(options); // super is L.Routing.OSRMv1
}
route(waypoints, callback, context, options) {
console.log("waypoints ", waypoints);
console.log("callback ", callback);
console.log("context", context);
console.log("options ", options);
const originalCallback = options.callback;
super.route(
waypoints,
customCallback(originalCallback),
this,
options
);
}
}
I've created a circle on the map and in that circle I've figured a point with the highest elevation and one with the lowest. (I used this leaflet-plugin to get me that data)
Those two point are my starting and ending points in Leaflet-Routing Machine.
In the Leaflet Routing Machine documentation there is a sub-object called an interface.
When clicking one of them called IRoute it takes you to:
There is a property called coordinates
So what I would like is to add some custom points in the route, The end result would be each point from the starting point would be lower in elevation from the last until the end point.
I did a console of my L.Routing.Control instance and see this:
I see the coordinates prop but not sure if that's even the right prop?
So essentially I want to add custom markers/latlang to the route generated.
Thanks in advance!