Leaflet: L.latLngBounds swapping longitude of Northeast and Southwest point

Created on 16 Sep 2016  Â·  7Comments  Â·  Source: Leaflet/Leaflet

Consider the example below

var southWest = L.latLng(-7.12725588946924, 159.14), northEast = L.latLng(70.0415297179124, 24.855900000000005);
var bounds = L.latLngBounds(southWest, northEast);

//logs - LatLng(-7.12726, 159.14), LatLng(70.04153, 24.8559)
console.log(southWest.toString(), northEast.toString());

//logs - LatLng(-7.12726, 24.8559)
console.log('bounds SW: ', bounds.getSouthWest().toString());
//logs - LatLng(70.04153, 159.14)
console.log('bounds NE: ', bounds.getNorthEast().toString());

Longitude for SouthWest and NorthEast points are swapped in the bounds. Why? This seems like a very bad bug.
Issue reproduction - http://jsbin.com/huziqi/edit?html,console

needs discussion

Most helpful comment

Hi,

If I am not mistaken, the conventional cardinal directions and latitude / longitude ranges are:

             North (+90)
               |
(-180) West ———+——— East (+180)
               |
             South (-90)

Therefore your southWest = L.latLng(-7.12725588946924, 159.14) would actually be more East than your northEast = L.latLng(70.0415297179124, 24.855900000000005).

When creating your bounds out of these corners, Leaflet detects the correct positions for you.
Is not it great? :-)

All 7 comments

Hi,

If I am not mistaken, the conventional cardinal directions and latitude / longitude ranges are:

             North (+90)
               |
(-180) West ———+——— East (+180)
               |
             South (-90)

Therefore your southWest = L.latLng(-7.12725588946924, 159.14) would actually be more East than your northEast = L.latLng(70.0415297179124, 24.855900000000005).

When creating your bounds out of these corners, Leaflet detects the correct positions for you.
Is not it great? :-)

This is a good example of Postel's law: «an implementation should be conservative in its sending behavior, and liberal in its receiving behavior».

Perhaps it should be a good idea to change the docstrings and rename the parameters for the LatLngBounds constructor into corner1 and corner2 or something like that, to prevent confusion.

@ghybs Some more context -
image

Here is what I am expecting it to do - Region in the blue box is what I expect my bounds to be, while region is the red box is what leaflet is picking up.

@aaani Look at your blue polygon, it crosses the dateline.

A very easy workaround would have been to use a longitude of 159.14 - 360 = -200.86 to reach the left copy of the world.
Demo: https://jsfiddle.net/3v7hd2vx/80/

See https://github.com/Leaflet/Leaflet/pull/1293

@ghybs thanks for the workaround. I happen to be already aware of it 😄
I guess my bigger concern is leaflet picking the wrong bounds instead of raising an error - Hey, your bounds seem to encompass International Date Line. Please adjust them so they don't do it.. Or at least providing an option to opt out from this _auto adjustment of bounds_ behavior.

P.S. - Is there any hope for merging #1293 into master any time soon?

@aaani Thank you for your feedback!

It seems to me that what you are asking for is already somehow a controversy in Leaflet, as shown by #1293 and the associated threads, including duplicating features to all world copies (like in https://github.com/Leaflet/Leaflet/issues/2506).

I think we can say that the current Leaflet behaviour makes the _overall assumption_ that the developer is aware that if they want to cross the IDL, they should specify values _outside_ the [-180; +180] degrees range.
That way, Leaflet does not need to make additional hypothetical assumptions and leaves out the associated complexity.

Then for consistency, L.LatLngBounds should assume that the developer is mistaken this time if they pass a "West" value that is higher than the "East" one, and invert the two, as is currently implemented.
BTW, same applies for North and South, except that they have to be inverted if passed wrong, since we cannot wrap vertically as simply (Earth is ~spherical, not a toroid).

Based on SO questions, many people make that mistake indeed, and the current correction works right for them. In fact, it works so well that they are never aware of their mistake (so even SO answers do not mention it), since Leaflet correction matches their expectation.

In that sense, @IvanSanchez's proposal about changing the parameters names to corner1 and corner2 could indeed make it clearer that L.LatLngBounds does not care about what order are the coordinates, it just sticks to the actual values, under the above _overall assumption_.

As for "raising an error […] bounds seem to encompass [IDL]", why should it be an error / warning in your case for example, where you _really_ want to encompass the IDL?

This shows that whether Leaflet corrects mistakes or automatically wraps longitude to keep the "West" value to the left, it will act against some people expectation. And if a message is reported, it would also be useless in most cases, and you probably do not want a message to be continuously reported when the code behaves the way you expect, or you have already taken the appropriate workaround.

As for "an option to opt out from this _auto adjustment of bounds_ behavior", please realize that it would not only be a matter of _disabling_ something, but it would actually involve a more complex _auto adjustment_!

If you want that Leaflet really keeps whatever you specify in "West" as the _left_ side, it should either:

  • Subtract 360 degrees to your West coordinate (in case it is a higher value than the East one),
  • Add 360 degrees to your East coordinate,
  • Perform one or the other and rely on a Features Duplication functionality.

You could make up a choice and build a small plugin out of it, that would override L.LatLngBounds :-)

BTW, also realize that #1293 seems to rely on the current _map center_, so in the precise case of your JS Bin, it would not even be able to help, since there is no map…
Of course in reality you would probably have a map (but would it be instantiated and the view set before your bounds?), but this shows that such a functionality has to make additional and hypothetical assumptions.

One could even be tempted to try to determine the "correct" auto adjustment by trying to decrease the mismatch of resulting coordinates (i.e. minimizing the resulting area), but it would specifically guess wrong in your precise case (your blue polygon has bigger area than your red one).
This is another sign that every workaround is based on a specific use case situation / assumption, and is probably impossible to generalize.

Is there any hope for merging #1293 into master any time soon?

IMHO, given the time these issues / PR are around (some related threads are much older than #1293), and the fact that it involves some _guessing_ / hypothetical assumptions about developer's intention, with complex algorithm involved, which is against Leaflet spirit of remaining technically simple, lightweight and reliable for the majority of use cases, there is _very little_ to _no_ chance of having such functionality in Leaflet core.

At best, if someone comes up with a working implementation, it could be built in a _plugin_, so that the complexity and assumptions are kept optional for the use cases that really need it (and are aware of these additional assumptions).
But again, this is only my personal interpretation.

Having said all that, I agree though that it would be lovely to have such a functionality in Leaflet (whether in core or through a plugin). After all, Google Maps does have it (well, at least the features duplication), which is another sign that it is what many people expect from a mapping tool. But since the majority of use cases do not need it, it seems to me that the additional complexity still outweighs the usefulness.
I am sure Leaflet maintainers would have loved to be paid by Google to implement it! :-)

BTW, Leaflet is Open Source, so feel free to contribute! :-)

I have discovered this is not so easy for maps covering 180. If the projection for map is 3851 (proj4.defs("EPSG:3851","+title=NZ Continental Shelf +proj=lcc +lat_1=-37.5 +lat_2=-44.5 +lat_0=-41 +lon_0=173 +x_0=3000000 +y_0=7000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs");

Then when getBounds unprojects the pixel coordinate bounds, you get, say, sw =-47, 164 and
ne -30, -174. ie proj.4 has returned the negative coordinate. As creator of map, I cannot control this. proj.4 is correct and following the convention. LatLngBounds then effectively reverses them and, well yes... For say EPSG:3587, proj.4 would ne of -30,186 and so not problem.

I am very aware that map crosses antemeridian but cannot force proj.4 to return 0:360 instead of -180:180.

I am amazed that leaflet works in polar projections but somehow it does. However, EPSG:3851 is giving me grief.

Edit: Actually, I discover that some things in polar suffer this problem too. Scalebars arent great in polar regions but totally messed by this hijinks.

Was this page helpful?
0 / 5 - 0 ratings