Preciseness of leaflet measurement conversions - javascript

map.layerPointToLatLng(map.latLngToLayerPoint(L.latLng(40.687, -73.9035)))
results in
{lat: 40.686886382151116, lng: -73.90228271484375}
The imprecizeness seems to be unreasonably high, especially for the longitude.
map.getZoom()
results in
9
map.getSize()
results in
{x: 1335, y: 430}
Is there a way to increase preciseness?

If I understand correctly, you first convert your LatLng to a pixel position on your current map view (zoom 9, whatever view port dimensions).
Then you convert that pixel position back to a LatLng, and notice a discrepancy with your original LatLng.
It seems to me that you simply observe the effect of pixel rounding: map.latLngToLayerPoint will give you integer pixel coordinates, hence your precision will be no better than what a pixel covers in terms of actual distance.
At zoom 9 and New York latitude (40.687), the scale is about 20km for 86px, so about 230m per pixel.
The distance between the 2 LatLng coordinates you report is about 103m. Therefore it is well within a single pixel.
Live demo: https://jsfiddle.net/3v7hd2vx/149/ (results are printed in the console)
You can play with map zoom to see the precision improving, as each pixel will cover a smaller distance (area actually).

Related

Converting Latitude and Longitude to XY Coordinates

I am doing a Javascript application that requires the movement of a certain element in a real world map, in frames.
For each frame, i have the following positions in latitude and longitude for the element, for example for frame 0:
- Latitude: 49.011213
- Longitude: 8.422885
For frame 1:
- Latitude: 49.01121
- Longitude: 8.422887
Frame (Frame 0) needs to be point (0,0) and I want the following ones to be converted to XY Coordinates as well.
Basically, I need a Javascript script that receives the latitude and longitude of a frame and returns the position (x,y) for that frame (in relation to frame 0 with position (0,0)).
I've tried the following but it doesn't work:
function convertSphericalToCartesian(latitude, longitude)
{
// Convert from Degrees to Radians
let latRad = latitude * (Math.PI)/180;
let lonRad = longitude * (Math.PI)/180;
let earthRadius = 6367; // Radius in km
let posX = earthRadius * Math.cos(latRad) * Math.cos(lonRad);
let posY = earthRadius * Math.cos(latRad) * Math.sin(lonRad);
return {x: posX, y: posY};
}
Do you know a better formula for this? Thank you very much.
Any conversion you do will introduce error, since you're trying to map the surface of a sphere to rectangular coordinates. You haven't mentioned what you're doing with this information - the mention of "frames" makes me think of animation or a game involving moving things on a map. If that's the case, then your calculations are going to be closely tied to the map you're using.
In general, the question is: how far apart are your coordinates likely to be?
For small distances, up to a few miles or kilometers, you're probably just fine treating the lat/lon as x/y coordinates. For example, the coordinates you've given in your example are literally only feet apart - don't bother with complicated calculations. And again, how you draw something on a given map at those coordinates depends very much on the map you're using.
For larger distances, the curvature of the Earth becomes important and the map projection you're working with will make a big difference in how those coordinates are calculated. For example, this question discusses the calculations for a Mercator projection.

Google Maps API setBounds to exact coordinates

In the Google Maps API it allows you to set the bounds of a map given a list of coordinates. That's awesome. My issue is that it gives a little bit of breathing room on the sides. I'm trying to get the bounding box I'm looking at to be barely containing the bounds.
For example, I want to view California so I set the bounds to be the Northeast and Southwest corners. Instead of showing just California though, I get all of Oregon and half of Mexico.
Here's what I'm currently doing:
var geo_region = {
northeast: {
lat: 42.0095169
lng: -114.131211
}
southwest: {
lat: 32.528832
lng: -124.482003
}
}
var map_bounds = new google.maps.LatLngBounds();
map_bounds.extend(new google.maps.LatLng(geo_region.northeast.lat, geo_region.northeast.lng));
map_bounds.extend(new google.maps.LatLng(geo_region.southwest.lat, geo_region.southwest.lng));
var plot_map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
plot_map.fitBounds(map_bounds);
EDIT:
A clearer example might be Wyoming since it's such a nice rectangle. If my map dimensions are the same ratio as Wyoming, I only want it to show me Wyoming.
EDIT:
Somebody suggested that I offset the bounds manually so I grabbed some data on the offsets that Google is using but I can't figure out what their formula is for deciding those offsets so I'm a long ways away from being able to do that. I even used the viewport coordinates from Google's Geocoding API but those didn't help much either.
Here's my data: https://docs.google.com/a/dovidev.com/spreadsheets/d/1HZLdDt5uiGwEtY0NbX0pfkmYVuUDndptm_-kzq0vh_w/edit?usp=sharing
This cannot be done EXACTLY because of the way google's zoom level's work. Google sets the bounds of the area but zooms in as closely as possible without cutting anything out. Because the zoom levels are incremental and their increments are so large, this often means that you'll end up with a lot of extra space.
When I tried to zoom in even once from what I thought was grossly oversized, I found that parts of the bounds had been cut off.
And thus we see that Google is already getting it as tight as it can be.

How to convert radius in metres to pixels in mapbox leaflet?

I am working on an application where I am trying to run some algorithms on a map and calculate coords but they are slightly off because I am running the calculations on latitude and longitudes and the final results are getting distorted.
Now I am trying to convert all coordinates to EPSG3857 Web Mercator coordinates like so:
var crs = L.CRS.EPSG3857;
var zoom = terrainAnalysisMap.getZoom();
markerToPoint = crs.latLngToPoint(marker.getLatLng(), zoom);
markerOnRadiusToPoint = crs.latLngToPoint(markerOnRadius.getLatLng(), zoom);
Now I also have a radius which I will have to convert from metres to pixels but I cannot figure out how. How much is 1 metre in pixels? I mean it would depend on the zoom level too, right? So how to go about converting radius to pixels in mapbox and leaflet?
If you happen to have the latitude of the place where you are looking to convert the radius to pixels, you can make use of the "Meters per pixel" function described at http://wiki.openstreetmap.org/wiki/Zoom_levels. Here's my javascript implementation:
var metersPerPixel = function(latitude, zoomLevel) {
var earthCircumference = 40075017;
var latitudeRadians = latitude * (Math.PI/180);
return earthCircumference * Math.cos(latitudeRadians) / Math.pow(2, zoomLevel + 8);
};
var pixelValue = function(latitude, meters, zoomLevel) {
return meters / metersPerPixel(latitude, zoomLevel);
};
A meter is a meter, no matter what your zoom level is. What you need is to convert a meter into degrees since long/lat is a polar coordinate system. With the circumference of Earth being 40,075 km, you get 0.00000898315 deg/m which you need to multiply with the size of the object (1 m) to get the degrees which you have to add to your coordinate to get a point which intersects with the radius of the circle that you want to draw.
But usually, it's easier to just draw a circle with a radius of 10 px around the center coordinate (after you transformed it from world to screen) making sure that the circle is always the same size, no matter of the zoom level. That way, people won't have a problem to see and/or click it.
[EDIT] Your question is related to Parametric equation to place a leaflet marker on the circumference of a circle is not precise?
My suggestion is to forget about world coordinates for the drag/drop problem. Obviously, you already have a circle (which means you must know the center point and the radius in pixels - otherwise, you couldn't draw it).
What you need is to implement the dragging of the marker only in screen coordinates. That should be pretty simple to implement.
When the user releases the mouse, all you have to do is to take the screen coordinate and convert it into long/lat once.
One problem to keep in mind: If you're using something like Mercator projection, the coordinates will be off as you get closer to the poles. To solve this, you need to work with an ellipse (wider than tall) instead of a circle.
I have done this once using this array for pixel/realworld-meter translation
pixelMeterSizes: {
10: 1 / 10,
11: 1 / 9,
12: 1 / 8,
13: 1 / 7,
14: 1/ 5,
15: 1 / 4.773,
16: 1 / 2.387,
17: 1 / 1.193,
18: 1 / 0.596,
19: 1 / 0.298
}
Notice, that above a zoomlevel of 15, i simplified things, because the symbols were getting too small and would not be visible anymore.
I used this as a basic reference http://wiki.openstreetmap.org/wiki/Zoom_levels.
This worked quite good for me, but i am not sure what will happen when dealing with high res displays and such. Guess it could fail in those scenarios.

Exclude overlaid element from Google Maps viewport bounds

I am using Google Maps API v3 to create an inline map on a website. In its container element, I also have an absolute positioned overlay which shows some detail information, visually hovering over the map. Determining on context this element may grow up to the size of the entire map element.
All this is working fine, however the Maps instance of course still considers the overlaid part of the map a valid usable part of the map. This means that, especially if the overlay is at maximum height, setCenter doesn't focus on the visible center, and routes drawn with DirectionsRenderer are partially underneath the overlay.
See this image:
Is there a way to limit the actual viewport to the blueish area, so that setCenter centers on the arrow tip and setBounds fits to the blue part?
I have managed to implement an acceptably functional workaround for the time being.
Some general notes which are good to know:
Every Map object has a Projection, which can convert between LatLng points to map points.
The map points a Projection uses for calculation are in 'world' coordinates, meaning they are pixels on the world map at zoom level 0.
Every zoom level exactly doubles the number of pixels shown. This means that the number of pixels in a given map point equals 2 ^ zoom.
The samples below assume a 300px wide sidebar on the right - adapting to other borders should be easy.
Centering
Using this knowledge, it becomes trivial to write a custom function for off-center centering:
function setCenter(latlng)
{
var z = Math.pow(2, map.getZoom());
var pnt = map.getProjection().fromLatLngToPoint(latlng);
map.setCenter(map.getProjection().fromPointToLatLng(
new google.maps.Point(pnt.x + 150/z, pnt.y)));
}
The crucial bits here are the z variable, and the pnt.x + 150/z calculation in the final line. Because of the above assumptions, this moves the point to center on 150 pixels to the left for the current zoom level, and as such compensates for the missing 300 pixels on the right sidebar.
Bounding
The bounds issue is far less trivial. The reason for this is that to offset the points correctly, you need to know the zoom level. For recentering this doesn't change, but for fitting to previously unknown bounds it nearly always will. Since Google Maps uses unknown margins itself internally when fitting to bounds, there is no reliable way to predict the required zoom level.
Thus a possible solution is to invoke a two-step rocket. First off, call fitBounds with the entire map. This should make the bounds and zoom level at least nearly correct. Then right after that, do a second call to fitBounds corrected for the sidebar.
The following sample implementation should be called with a LatLngBounds object as parameter, or no parameters to default to the current bounds.
function setBounds(bnd, cb)
{
var prj = map.getProjection();
if(!bnd) bnd = map.getBounds();
var ne = prj.fromLatLngToPoint(bnd.getNorthEast()),
sw = prj.fromLatLngToPoint(bnd.getSouthWest());
if(cb) ne.x += (300 / Math.pow(2, map.getZoom()));
else google.maps.event.addListenerOnce(map,'bounds_changed',
function(){setBounds(bnd,1)});
map.fitBounds(new google.maps.LatLngBounds(
prj.fromPointToLatLng(sw), prj.fromPointToLatLng(ne)));
}
What we do here at first is get the actual points of the bounds, and since cb isn't set we install a once-only event on bounds_changed, which is then fired after the fitBounds is completed. This means that the function is automatically called a second time, after the zoom has been corrected. The second invocation, with cb=1, then offsets the box to correct for the 300 pixel wide sidebar.
In certain cases, this can lead to a slight off-animation, but in practice I've only seen this occur when really spamclicking on buttons causing a fit operation. It's running perfectly well otherwise.
Hope this helps someone :)
You can use the map panBy() method which allows you to change the center of the map by a given distance in pixels.
Hope this helps!
I had a similar need and ended up just forcing some "padding" to the east of a LatLngBounds object.
On the upside, it's simple and it works. On the downside it's not really versatile. Just a quick little hack.
// start with a standard LatLngBounds object, extending as you usually would...
bounds = new google.maps.LatLngBounds();
// ...
ne = bounds.getNorthEast();
sw = bounds.getSouthWest();
// the multiplier used to add space; positive for east, negative for west
lngPadding = 1.5
extendedLng = ne.lng() + (ne.lng() - sw.lng()) * lngPadding;
// copy original and extend with the new Lng
extendedBounds = bounds;
extendedBounds.extend(new google.maps.LatLng(ne.lat(), extendedLng));
map.fitBounds(extendedBounds);

Javascript find furthest coordinate from a certain coordinate in an array of coordinates

I know the center coordinates and an array of all coordinates of a polygon.
How can I find out which coordinate is the furthest coordinate for the center point with Javascript?
Center of bounds:
lat: -13.647141573542923lng: 109.75651876851946
Coordinates of the 4 polygon paths:
"lat:-9.10209673872643 , lng: 108.10546875"
"lat:-16.97274101999901 , lng: 91.58203125"
"lat:-17.644022027872722 , lng: 120.234375"
"lat:-8.407168163601074 , lng: 120.41015625"
The canonical way of doing things:
Figure out how to calculate distances with longitutdes/latitudes. A quick Google search, for example, brings up this.
Iterate over all points p_i and calculate the distance d_i to the center point.
The point p_k with d_k = max_j d_j will be the point you are looking for.
For distance calculation keep in mind that you can save yourself any operation that won't change the information "further away than", i.e. in euclidic coordinates you could save yourself taking the square root. Also multiplications by any constant can be omitted for this (as long as you don't need to actually know the distance, but only the information which point is the furthest away).

Categories

Resources