I have Leaflet map with leaflet-routing-machine. I set few waypoints and all work fine.
But now I want alter the way. I drag it, map set additional waypoint and now route goes through this extra point.
How can I understand that this waypoint is 'extra' one (not includes in predefined set of points) in instructions properties of route . Maybe I can set special label for it?
I have lat, lng for my predefined points, but I want have distance between all MY points.
Related
I've been tasked with creating a Google Earth Web link programmatically when given coordinates. I have the street address as well, where I'd ideally like to drop a pin.
For example, I can get a link to the white house using its lat/lon at a distance of 150 meters like this:
https://earth.google.com/web/#38.8976633,-77.0365739,150d
If I search using the google earth web app I can generate a link with a pin, where a few of the parameters in the link change slightly:
https://earth.google.com/web/#38.8976763,-77.0365298,18.0497095a,800.41606338d,35y,0h,45t,0r/data=ChIaEAoIL20vMDgxc3EYAiABKAIoAg
Am I able to dynamically generate the data element, or whichever element creates the pin, at my desired location? I've also had trouble finding the correct distance d and elevation a parameters in my links.
As you found, you can generate links to specific views in the Google Earth web client by adding the correct parameters to the URL, including the latitude, longitude and altitude (a) of the view target, and the distance (d) of the camera from that target. Note that altitude and distance are both in meters, and altitude is above sea level, not above ground elevation. If you look at the a and d parameters that Earth puts in the URL as you fly around, often altitude will be the terrain (or builing-top) elevation at the target lat/lon, and the distance will be how far the camera is from that altitude. The other available parameters include heading (h) and roll (r).
So long as your tilt (t) remains zero, then altitude and distance should be interchangeable, or if both are >0, then they will be summed together for the final camera height above sea level. But if you add a tilt (zero degrees is looking straight down), then the altitude determines the elevation of the view target (above the lat & lon location), and the distance determines how far the camera is from that point. If you make d=0, then altitude will define both the view target and camera height above sea level. If you make a=0, then the distance will be from the lat,lon at sea level (even if that's underground).
Unfortunately there's no way to manually construct the data parameter, as it can contain many different things. To do that right would require an API, which Earth for Web currently does not provide. Hopefully that kind of functionality will come after Earth finishes its work to become cross-browser compatible via Web Assembly. Until then, there's not a way to add a point the map via just a URL.
So in this model I have several hospital-agents that are placed randomly in an area. These hospitals contains a process flow and at some point in this process flow a new agent 'Bones' is generated, using a split block. The location of these Bones-agents is correctly specified by setting it equal to the (x,y) coordinates of the hospital.
Now I want to make the model more realistic by placing the hospitals in actual location in a GIS map. I did this with success. However, now I need to re-specify the location of the Bones-agents. At the moment of generating the first Bones-agent, I get the follow error:
root.Hospital1.splitblock:
Error when trying to initialize new agent
Caused by: root.Hospital2:
This agent is already defined as agent living in space 'Continuous, based on
GIS map' and can't have behaviour for space 'Continuous'This agent is already
defined as agent living in space 'Continuous, based on GIS map' and can't
have behaviour for space 'Continuous'
What do I need to do to make this work? I have tried setting the location of the Bones-agent equal to the longitude and latitude of the hospital agent with a function:
double longitude = getLongitude();
return longitude;
I did the same for het latitude. I then inputted these functions in the 'latitude' fields of the split block.
When you develop a model, you have to choose what kind of space you will use. Remember that all the canvas in which you put agents, and the map and stuff is based on a scale, so you can't mix a map with elements that are created with the space markup (with space markup i mean nodes, paths, rectangular nodes etc).
So the bones agents should also be placed in the map... It seems that you are not doing that, and you are probably placing the bones agent using the space markup.
But it's possible to do this of course, but you have to do it in another agent. Create a new agent called continuousSpace for instance, and place your bones agents there.
After that you will have to create a navigation button using viewAreas (from the presentation palette) to move from one agent to the other (meaning from the gis space to the markup space).
Otherwise, you can also place the bonesagents in the gis space (in the map) and it will also work.
Good luck :)
Background:
I am working on a web based mapping application for hiking. So the map based on leaflet offers routes on hiking trails that are labeled. As any hiking trail can be part of multiple routes, routes - respectively the corresponding polylines representing the routes - can overlap.
Problem:
Each route has its tooltip (triggered by mouseover, {sticky:true}) showing its label which works as expected for non-overlapping polylines but as soon as two or more routes overlap only the polyline "on top" gets its tooltip opened. This behaviour is not bad per se but as all routes are equally important I would like to show all labels of the routes at the pointer's location (or something like a maximum of 5 labels + x more). I weren't able to find any issue related to this topic.
What I tried:
- Create a feature group for all routes, bind the tooltip to the group, hoping that the tooltip function provides an array of all polylines crossing the pointer's position. As it turned out, I only get information of the polyline on top
- I tried the same with a mousemove event on the map, no success
- Comparing pointer's layerPoint coordinates with all routes' _rings & _parts layPoint arrays to find matching layerPoints, but the success rate is only about 5% as these layerPoints only cover actual points of the polyline but not the connection between two points. Additionally, there is a margin around each polyline that triggers the tolltip before the pointer even touches the polyline (too improve touch action, I guess)
- A solution to the margin problem is to add positive and negative margins to each polyline point before comparing it to the pointer coordinates which improves the outcome but doesn't solve the main problem.
Sidenote:
- All routes are drawn into a single canvas
Long story short, I need external help to accomplish the goal. Maybe some of you have an idea or can provide a solution. Any input is appreciated.
** UPDATE: **
A working but pretty inefficient solution is as follows
Approach:
Calculate the shortest distance from the pointer to all routes in viewport. If distance from the pointer to a route is under a certain threshold, add them to the array of route labels that should be displayed.
Steps:
1.) bind a blank tooltip to the a feature group containing all routes
2.) bind mousemove event to the feature group with the follwing function
var routesFeatureGroup = L.featureGroup(routesGroup)
.bindTooltip('', {sticky: true})
.on('mousemove', function(e){
var routeLabels = [e.layer.options.label]; // add triggering route's label by default
var mouseCoordAbs = el.$map.project(e.latlng);
$.each(vars.objectsInViewport.routes, function(i, v){
if (e.layer.options.id != el.$routes[i].options.id && el.$routes[i]._pxBounds.contains(e.layerPoint)){
var nearestLatlngOnPolyline = getNearestPolylinePoint(e.latlng, el.$routes[i]);
var polyPointCoordAbs = el.$map.project(nearestLatlngOnPolyline);
var distToMouseX = polyPointCoordAbs.x - mouseCoordAbs.x;
var distToMouseY = polyPointCoordAbs.y - mouseCoordAbs.y;
var distToMouse = Math.sqrt(distToMouseX*distToMouseX + distToMouseY*distToMouseY);
if (distToMouse < 15) {
routeLabels.push(el.$routes[i].options.label);
}
}
})
var routesFeatureGroup.setTooltipContent(routeLabels.join('<br>'));
})
Explanation:
I already gather all objects (routes and markers) in the current viewport for another part of the app. All routes currently visible are stored in vars.objectsInViewport.routes (respectively their ids), so I dont have to go through all routes. The layer that triggered the mousemove event is added by default. I then check for each of the routes currently visible if:
- their id is different to the layer that trigger the mousemove event (as this label is added by default)
- if their bounds (in cartesian coordinates: "_pxBounds") contain the cartesian layerPoint of the mousemove event (for a rough approch to exclude routes that don't intersect)
If these conditions are met for a route, calculate the closest latlng point from the pointer to the route. I do this with a custom function, which is a bit to long to post it in this context. (I will if someone asks for it)
The mouse position and the latlng point on the polyline / route are then converted to absolute coordinates using the map-project method
http://leafletjs.com/reference.html#map-project
At last, the distance between these to points is calculated using pythagoras. It is pixel based, so that the zoom level isn't a factor. If the distance is below a certain threshold (15px) they are close enough to the pointer to be considered as being hovered (with the default margins around a polyline), so the label of the route is added to the label array.
Finally the tooltip for the feature group is filled with all labels.
Results are pretty promising even though the operation is pretty expensive. I added a timeout of 50ms to reduce the function call a bit:
var tooltipTimeout;
var routesFeatureGroup = L.featureGroup(routesGroup)
.bindTooltip('', {sticky: true})
.on('mousemove', function(e){
clearTimeout(tooltipTimeout);
tooltipTimeout = setTimeout(function(){
// collect labels
// ...
},50);
.on('mouseout', function(){
clearTimeout(tooltipTimeout);
})
I can give you an idea of how to do this, but I am not 100% sure that it will do the job. There is a plugin for Leaflet (Mapbox) that can tell you if a point is within a Polygon and it returns all the Polygons that contain that point.
If this plugin doesn't work for polylines you can create a polygon from a polyline by just going back from the last point to the first and closing the line (I am not sure if this suits you solution). For example if you have a polyline of connected points of [0, 1, 2, .... n-1, n] you then go back with connecting [n with n-1, n-1 with n-2, ... 1 with 0]. This way you will have the same shape of the polyline but it will be a polygon. This isn't the most optimized solution, it is a quick fix that uses a known and available plugin.
Once you get all the tooltips, you can open all of them at once for each polygon/polyline. Or maybe open some helper tooltip where the user can select which one he wants to open.
I hope this helps! If you figure out a better solution (or find a plugin that does the job) please post it here.
Is there any way to adjust the bounds of the map so that the right-edge of Russia doesn't appear over to the left? You can see in the image I have a MultiPolygon area overlaying Russia but the map and the overlay are split. I'd like that tiny piece of the country to be on the right if possible!
Edge of Russia on the wrong side of the map:
A workaround I can think of is using the maxBounds property, where you would shift the default bounds slightly to the right, along with minZoom: 1. This won't prevent the user from seeing the world several times for a short time if zoomed out far / panning outside, as it says there:
... bouncing the user back when he tries to pan outside the view
var map = L.map('map',{
maxBounds:[ [-90, -160], [90, 200] ],
minZoom: 1
}).setView([66.058, 189.459], 4);
Demo
Welcome to SO!
If your multi polygon is the blueish area, then I am afraid you have to refactor your data in order to achieve what you want (shifting the left area onto the right, as if it were stitched back to Russia main land).
Your data (probably GeoJSON?) contains a separate polygon which longitudes are in the [-180, -120] range. Leaflet has no choice but to display it on the left of your map, independently from the noWrap option.
So you would need to dig into your data, and add 360 degrees longitude to every node of this polygon, so that they now sit in the range [180, 300].
Or somehow introduce a "detection" in your code that would perform the longitude addition automatically for shapes which bounds and/or center are far away (let's say in the [-180, -120] longitude range). Leaflet does not perform that operation automatically out-of-the-box.
Note: the noWrap option is for your Tile Layer not to load tiles outside that "central" world (in order to avoid showing multiple copies of the world). But in your case, you want to show a part of Russia / Siberia on an "adjacent copy of the world", so you might want to remove that option, or you will have your polygon not sitting over any basemap.
Say I keep getting a set of Geo coordinates from a moving vehicle.
There is a predefined route that the vehicle needs to follow from Point A to Point B.
The route will be defined by giving the user the option to select points A and B and also selecting the WayPoints in between and letting the Google API select the best route.
As the vehicle moves I need to check whether the vehicle has deviated more than 50 m from the predefined route.
My solution (not fully tested)
When the user has selected the start and the end points and the waypoints in between, I can make a call to the Google Directions API which would give me all the points on the road in between
(The polyline property inside the steps gives you the exact points on the road which the UI uses to draw the lines. You can call the following URL to see the polyline response
https://maps.googleapis.com/maps/api/directions/json?origin=Boston,MA&destination=Concord,MA&waypoints=Charlestown,MA|Lexington,MA
)
and store these points in some database (Mongo) with a route id.
When I get the geo cordinates from the vehicle I can make a query to Mongo asking for all those points with the route id and whose distance is less than 50m from the vehicle coordinates. And if there are non then the vehicle has deviated from the route.
Is there a better way of doing this?