-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Clamp latitude value to valid Web Mercator range when projecting to y #6918
Conversation
That should take care of latitude (as long as clamp works correctly with Infnity as well). Longitude on the other hand is tricky:
Actually, fitBounds([0,-40,10000,40]) is wrong for an other reason: it zooms out the map, thus breaking the lat constraints. I believe it should do a mod 360 for exampe. My idea is that for longitude, there should be some kind of reasonable clamping so that Infinity works, as well as a mod 360 operation. |
src/geo/transform.js
Outdated
@@ -295,6 +295,12 @@ class Transform { | |||
* @returns {number} pixel coordinate | |||
*/ | |||
latY(lat: number) { | |||
const validLatitude = 85.05113; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Declare as static constant outside this method, maybe in util.js
or on LngLat
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, there is this.latRange = [-85.05113, 85.05113];
in the same file in an other place as well, as well as a couple of -85.0511
values in style-spec/reference/v8.json.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. I'll refactor these references to a single place.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You probably cannot refactor the json one, but maybe it's a good idea to have the same precision level in all 3 places (that is, probably stick with 85.0511
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it should be declared in LngLat because the way I understand it is that the LngLat is based around EPSG84 coordinates which are valid up to +-90° so the issue is more in projecting from those coordinates to valid Web Mercator ones (EDIT: well in projecting from y to to Web Mercator lat anyway). I'm leaning towards using the latRange
property in Transform.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using latRange
would prevent general transform/projection calls from using otherwise valid calls with LatLng
outside the bounds specified in Map#setMaxBound
. Im not sure that would be clear/expected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahhhhh, yeah I was misunderstanding what latRange
was. Thanks for the clarification.
I recently tried to tackle the same longitude issue described by @hyperknot in Supercluster — perhaps you could reuse some of that logic: https://github.com/mapbox/supercluster/blob/master/index.js#L72-L84 |
Ah, I looked into this, the silly negative modulo of JS really makes it complicated. Picking @mourner's solution verbatim is probably the best idea. (BTW, why the 180 special case? This way 180 maps to 180 and 540 maps to -180. Was the original aim to make min between -180 .. 179.99 and max between -179.99 .. 180?) |
src/geo/transform.js
Outdated
@@ -295,6 +297,9 @@ class Transform { | |||
* @returns {number} pixel coordinate | |||
*/ | |||
latY(lat: number) { | |||
if (lat > this.maxValidLatitude || lat < this.maxValidLatitude * -1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any point in using an if
here? Why not just call clamp directly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's probably fine. Originally, I had a console warning inside the if statement but I think we've decided to favor documentation over a warning so this can be removed.
Is there anything blocking this? I thought copying @mourner's snippet from supercluster would solve longitude. We just need to fix the 540 bug there (mapbox/supercluster#96). |
No, I just have been pushing to get some other stuff finished and this was put on the back burner. I can finish up this PR for just the latitude fix if you're interested in putting together a PR for the longitude fix separately @hyperknot. Otherwise, it will probably be next week before I finish this. |
I'm happy to do a PR for lon fix from the supercluster snippet. |
e477c1c
to
eb58382
Compare
@mourner Can you take a look at my latest implementation, particularly the change to |
@ryanhamley I think @mourner's code is having two cases to make sure that -180 gets mapped to -180 when it's the min side and and +180 to +180 when it's the max side. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm now hesitating about the longitude wrapping — it can bring some unintended consequences. Maybe let's leave that for a different PR?
src/geo/transform.js
Outdated
|
||
this._renderWorldCopies = renderWorldCopies === undefined ? true : renderWorldCopies; | ||
this._minZoom = minZoom || 0; | ||
this._maxZoom = maxZoom || 22; | ||
|
||
this.setMaxBounds(); | ||
this.latRange = [this.maxValidLatitude * -1, this.maxValidLatitude]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's move this back to setMaxBounds
to make sure the behavior is consistent (and setMaxBounds(null)
resets the map to the state it was by default).
Also nit: -this.maxValidLatitude
is more readable than this.maxValidLatitude * -1
.
@mourner Agreed. I'm reverting that change and will just close out the original latitude clamping issue with this PR. I think there's potential to combine longitude wrapping with #6985 in a single PR. |
@mourner I think this PR is good to go from my standpoint if you can look it over and give it a 👍 if it looks good. |
@@ -503,7 +503,7 @@ test('camera', (t) => { | |||
const camera = createCamera({ zoom: 0 }); | |||
camera.rotateTo(90, { around: [5, 0], duration: 0 }); | |||
t.equal(camera.getBearing(), 90); | |||
t.deepEqual(fixedLngLat(camera.getCenter()), fixedLngLat({ lng: 4.999999999999972, lat: 0.000014144426558004852 })); | |||
t.deepEqual(fixedLngLat(camera.getCenter()), fixedLngLat({ lng: 4.999999999999972, lat: 0.000002552471840999715 })); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quick question — why is value here different? Latitude is close to 0 so isn't supposed to be affected by clamping here...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is caused by a slight change in the maxValidLatitude
value from 85.05113
to 85.051129
. Should I revert to the older value?
fb1de16
to
cfd24b6
Compare
Fixes #6906
Launch Checklist