leaflet routing machine when waypoints are the same - leaflet

I am using this plugin : https://github.com/perliedman/leaflet-routing-machine.
My code looks like this:
for(let i=0; i<markers.length; i++){
points.push(L.latLng(markers[i].latitude, markers[i].longitude))
}
this.routingControl = L.Routing.control({
waypoints: points
}).addTo(this.map);
When I pass points filled with different latitude/longitude, it draws the route fine. But let's imagine the following scenario. let's say that points array contains 3 items. each item contains latitude/longitude and let's say that those latitude/longitude are the same. So something like this:
34.72581233927868 -80.71105957031251
34.72581233927868 -80.71105957031251
34.72581233927868 -80.71105957031251
Now what Routing control does is as it can't draw the route, it automatically zooms in in the maximum way and in the console, it shows the errors. {"message":"Zoom level must be between 0-20."}
Workaround 1: after drawing routes, i decided to use settimeout after 1 second and there I zoom at 11 by myseslf. this way it zooms out, but in the console, errors still stay. How do I fix this?

If you know your input can be invalid, then the cleanest way would be to filter it on the way in to remove duplicates. If that's not possible for some reason and you want to let LRM determine if there's an error, then you can catch the error event with:
L.Routing.control({
...
})
.addTo(this.map)
.on('routingerror', function(e) {
// do your error action here - e.g. zoom out
})
For more information on handling errors in LRM, see https://www.liedman.net/leaflet-routing-machine/api/#eventobjects

Related

HERE API:Swift. Wierd behaviour : Loops in a route - Creating a route based on GPS trace creates loops. / passthrough waypoints

im trying to create a route that follows a gps trace i provide.
The gps trace is cleaned up and it has no loops in it and is in correct order.
I checked it with other services.
It has 1920 points.
You can find the trace here GPX Files
Sadly if i create a route based on provided sdk example (github) i get loops in my path.
I was hoping you could help me to solve following problems:
how do i avoid loops while creating route by using HERE ios Swift SDK
how do i set route options is such way to follow provided point array and not create a fastest or balanced route.
Since i could not find those functions in Ios sdk i used additional REST API to filter the route a bit to remove all points that were not matched correctly according to here maps... before drawing the route.. ie everything with low probability, warnings, big distance to the road... yet the result is still not good. Here is a cleaned up file.. the file is being created after the original was maped / run once through HERE Maps. In this file all points that have low confidence or produce warnings or have big distance to original points .. are removed. This is the one i use to create a route and it still have the same issues like loops and weird turns.
Thank you very much in advance!
BR.
So far i have this code:
private lazy var router = NMACoreRouter()
#objc func do_routing_stuff( gps_trace :[NMAWaypoint]) {
var stops = [Any]()
stops = gps_trace
let routingMode = NMARoutingMode(routingType: .fastest,
transportMode: .car,
routingOptions: .avoidHighway)
// Trigger the route calculation
router.calculateRoute(withStops: stops ,
routingMode: routingMode)
{ [weak self] routeResult, error in
guard error == .none else {
self?.showMessage("Error:route calculation returned error code \(error.rawValue)")
return
}
guard let result = routeResult, let routes = result.routes, routes.count > 0 else {
self?.showMessage("Error:route result returned is not valid")
return
}
// Let's add the 1st result onto the map
self?.route = routes[0]
self?.updateMapRoute(with: self?.route)
// self?.startNavigation()
}
}
private func updateMapRoute(with route: NMARoute?) {
// remove previously created map route from map
if let previousMapRoute = mapRoute {
mapView.remove(mapObject:previousMapRoute)
}
guard let unwrappedRoute = route else {
return
}
mapRoute = NMAMapRoute(unwrappedRoute)
mapRoute?.traveledColor = .clear
_ = mapRoute.map{ mapView?.add(mapObject: $0) }
// In order to see the entire route, we orientate the
// map view accordingly
if let boundingBox = unwrappedRoute.boundingBox {
geoBoundingBox = boundingBox
mapView.set(boundingBox: boundingBox, animation: .linear)
}
}
in comparison same route presented with leaflet maps.
I believe the problem you have is that you are feeding the Routing API a large number of waypoints, all of which are in close proximity to each other.
You have almost 2000 waypoints in your GPX file (and ~1300 in your cleaned one). Each of these waypoints is less than 10 meters distance from their closest neighbors. This is not the type of data that the Routing API is really designed to work with.
I've experimented with your GPX Trace and I have come up with the following solution: simply skip a bunch of coordinates in your trace.
First, clean up your trace using the Route Matching API (which I believe you have been doing).
Second, pick the first trkpt in the GPX file as your first waypoint for the Routing call. Then skip the next 20 points. Pick the following trkpoint as the second waypoint. Repeat this until you are at the end of the file. Then add the last trkpt in the trace as the final waypoint.
Then call the Routing API and you should get a good match between your trace and your route, without any loops or other weird routing artefacts.
Some notes:
I have picked 20 as the number of traces to skip, because this would put about 200m in between each waypoint. That should be close enough to ensure that the Routing API does not deviate too much from the traced route. For larger traces you may wish to increase that number. For traces in urban environments with lots alternate routes, you may want to use a smaller number.
It's important to clean the data with the Route Matching API first, to avoid picking outliers as waypoints.
Also, you may not wish to use the "avoidHighways" option. Given your use case, there doesn't seem to be a benefit and I could see it causing additional problems.
By now you probably worked it out, but your waypoints are likely landing on bridges or tunnels that are along your route but not on the road you want. I.e. the waypoint is intended to be on the road under the bridge but the routing engine perceives that you want to drive on the bridge.
The routing engine is looping around those roads to drive you on that waypoint on the bridge or in the tunnel.
There is no simple solution to this that I have found.

MRTK V2 - Enable/Disable Spatial Mapping at Runtime

I know that this question has already been asked here twice, but the answers did not fix my problem. I need to enable spatial mapping on runtime. After scanning my environment I want to disable it, or hide at least the visualization of polygons, so I can save some fps. But by disabling spatial mapping I still want to have the colliders of my environment.
What I tried:
1. This example from this post did nothing.
if (disable){
// disable
MixedRealityToolkit.SpatialAwarenessSystem.Disable();
}
else
{
// enable
MixedRealityToolkit.SpatialAwarenessSystem.Enable()
}
2. Trying to disable the visualization gives me every time a nullreference. I guess GetObservers is giving null back or maybe meshOserver is null:
foreach(var observer in MixedRealityToolkit.SpatialAwarenessSystem.GetObservers())
{
var meshObserver = observer as IMixedRealitySpatialAwarenessMeshObserver;
if (meshObserver != null)
{
meshObserver.DisplayOption = SpatialAwarenessMeshDisplayOptions.None;
}
}
3. The example given by mrtk in there SpatialAwarenessMeshDemo scene, shows how to start and stop the observer. By starting everything starts fine but after suspending and clearing the observers the whole spatial map disappears, so my cursor does not align to my environment. So this is not what I need.
SpatialAwarenessSystem.ResumeObservers(); //start
SpatialAwarenessSystem.SuspendObservers();//stop
SpatialAwarenessSystem.ClearObservations();
What I have right now:
My Spatial Awareness Profile looks like this:
My code starts the spatial mapping with ResumeObservers, the foreach-loop gives me a nullreference and SuspendObserver is comment out, because it disables the whole spatial map thing:
if (_isObserverRunning)
{
foreach (var observer in SpatialAwarenessSystem.GetObservers())
{
var meshObserver = observer as IMixedRealitySpatialAwarenessMeshObserver;
if (meshObserver != null)
{
meshObserver.DisplayOption = SpatialAwarenessMeshDisplayOptions.None;
}
}
//SpatialAwarenessSystem.SuspendObservers();
//SpatialAwarenessSystem.ClearObservations();
_isObserverRunning = false;
}
else
{
SpatialAwarenessSystem.ResumeObservers();
_isObserverRunning = true;
}
Question: How do I start and stop spatial mapping the right way, so that I can save some performance and still have the colliders of the spatial map to interact with.
My specs:
MRTK v2.0.0
Unity 2019.2.0f1
Visual Studio 2017
!--Edit--inlcuding-Solution--!
1. With option #1 I was wrong. It does what its meant for, but I used it the wrong way. If you disable for example SpatialAwarenessSystem while running the spatial mapping process, it disables the whole process including the created spatial map. So after that you cant interact with the invironment.
2. What worked for me was using for the start ResumeObservers() in combination with setting display option to visible and for stopping spatial mapping the method SuspendObservers() in combination with display option none.
3. The Nullreference if fixed by rewritting and casting to IMixedRealityDataProviderAccess:
if (CoreServices.SpatialAwarenessSystem is IMixedRealityDataProviderAccess provider)
{
foreach (var observer in provider.GetDataProviders())
{
if (observer is IMixedRealitySpatialAwarenessMeshObserver meshObs)
{
meshObs.DisplayOption = option;
}
}
}
4. Performance: To get your fps back after starting an observer, you really need to disable the system via MixedRealityToolkit.SpatialAwarenessSystem.Disable();, but this will of course disable also the spatial map, so you cant interactive with it anymore.
#Perazim,
The recommendation is based on your option #3. Call ResumeObservers() to start and SuspendObservers() to stop. There is no need to call ClearObservations() unless you wish to have them removed from your scene.
The example calls ClearObservations() to illustrate what was, at the time, a new feature added to the Spatial Awareness system.
Please file an issue on GitHub (https://github.com/microsoft/MixedRealityToolkit-Unity/issues) for #1 (failure of Enable() and Disable() to impact the system). Those methods should behave as advertised.
Thank you!
David

When does mapquest route finish rendering?

I am using MapQuest map with leaflet. The routes are being injected dynamically. When we add routes to the map, it takes time to finish the rendering.
I want to block the screen while the map is being rendered.
Is there any way(mapquest API or leaflet event) to know the map is ready or finished rendering so that I can stop the blocking of the screen.
I'm using Leaflet to add the routes. Something like this:
function (locations) {
const dir = MQ.routing.directions();
dir.route({ locations: locations });
comp.mqroute = MQ.routing.routeLayer({
directions: dir,
fitBounds: true,
draggable: false,
});
comp.map.addLayer(comp.mqroute);
}
This is a case of RTFM.
The fact that you're using MQ.routing.directions in your code tells me that you're using the Mapquest routing plugin for Leaflet, which has complete API documentation.
By reading that API documentation, one can notice a success event, I quote:
success
Fired when route data has been retrieved from the server and shapePoints have been decompressed. [...]
I have a heavy suspicion that you don't need to know when the route is rendered (meaning: when the lines have been displayed on the screen) but rather when then network requests for the route are finished (which is what takes most time). The time to actually render lines on the screen is usually negligible unless one is working with thousands of segments (after the automatic douglas-peucker simplification).
I also have a heavy suspicion that this is a XY problem, and that the root problem you're trying to solve is race conditions when the user is (re-)requesting routes too fast, to which the solution is just throttling the requests rather than blocking the UI.
How to use this success event is illustrated in MapQuest's Route Narrative example:
dir = MQ.routing.directions();
dir.on('success', function(data) {
// ...
// the code here will run once the route is ready
// ...
}

Creating timed markers on a leaflet map?

I have a leaflet project where I am delivering a continuous stream of 'coordinates' via WebSockets to the client browser. I need to be able to display markers corresponding to those locations for a set period of time (something like 1 or 2 seconds) and then remove them (to make room for more locations).
Can anyone help me out or point me in the direction of resources wherein I could find some help?
Thanks!
Edit : Why the downvote? Its a legitimate and common question and one without a lot of solutions online.
Here is some code from the documentation (http://leafletjs.com/reference-1.0.3.html#layer):
var layer = L.Marker(latLng).addTo(map);
layer.addTo(map);
layer.remove();
So in order to remove it after 2 seconds, I think you could try this one:
var layer = L.Marker(latLng).addTo(map);
layer.addTo(map);
setTimeout(function() {
layer.remove();
}, 2000);
Example

map.fitBounds() with JSON data in a featureGroup

I cannot get the fitBounds command to work on a project where I am bringing in data to a featureLayer using GeoJSON.
This:
map.fitBounds(pointsFeatureGroup.getBounds()); // Uncaught Error: Bounds are not valid. at e.fitBounds (leaflet.js:5)
and this:
var bounds = L.latLngBounds(pointsFeatureGroup); // Uncaught Error: Bounds are not valid. at e.fitBounds
map.fitBounds(bounds);
don't work
My simplified code is here:
https://github.com/DPB61/leafletjs_problem2
If I just bring in markers individually into a normal Layer then a command like this works:
map.fitBounds(PointsLayer.getBounds());
but that raises other issues with other functionality for which a features layer is required.
Am I doing something wrong?
The root cause is a classic asynchronous issue.
See How do I return the response from an asynchronous call?
At the time you call map.fitBounds(vesselsFeatureGroup.getBounds()), the vesselsFeatureGroup is still empty (no child layer), therefore Leaflet cannot compute bounds.
Simply call the getBounds and fitBounds within the callback of your asynchronous task ($.getJSON)