Skip to content

Commit

Permalink
Improve globe constraints and panning precision (#12114)
Browse files Browse the repository at this point in the history
* fix globe constraints and improve panning precision

* fix flow

* reintroduce tracking ellipsoid

* fix panning through anti-meridian

* fixup

* fix query test

* update query geometry globe test
  • Loading branch information
mourner committed Jul 27, 2022
1 parent 2ae7305 commit 3d14aeb
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 47 deletions.
5 changes: 3 additions & 2 deletions src/geo/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -1617,12 +1617,13 @@ class Transform {
if (!this.center || !this.width || !this.height || this._constraining) return;

this._constraining = true;
const isGlobe = this.projection.name === 'globe';

// alternate constraining for non-Mercator projections
if (this.projection.isReprojectedInTileSpace) {
if (this.projection.isReprojectedInTileSpace || isGlobe) {
const center = this.center;
center.lat = clamp(center.lat, this.minLat, this.maxLat);
if (this.maxBounds || !this.renderWorldCopies) center.lng = clamp(center.lng, this.minLng, this.maxLng);
if (this.maxBounds || !(this.renderWorldCopies || isGlobe)) center.lng = clamp(center.lng, this.minLng, this.maxLng);
this.center = center;
this._constraining = false;
return;
Expand Down
45 changes: 24 additions & 21 deletions src/ui/handler_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import window from '../util/window.js';
import Point from '@mapbox/point-geometry';
import assert from 'assert';
import {vec3} from 'gl-matrix';
import MercatorCoordinate, {latFromMercatorY, mercatorZfromAltitude} from '../geo/mercator_coordinate.js';
import MercatorCoordinate, {latFromMercatorY, mercatorScale} from '../geo/mercator_coordinate.js';

import type {Vec3} from 'gl-matrix';

Expand Down Expand Up @@ -522,28 +522,31 @@ class HandlerManager {
// Compute Mercator 3D camera offset based on screenspace panDelta
const panVec = [0, 0, 0];
if (panDelta) {
assert(this._dragOrigin, '_dragOrigin should have been setup with a previous dragstart');
if (tr.projection.name === 'mercator') {
assert(this._dragOrigin, '_dragOrigin should have been setup with a previous dragstart');
const startPoint = this._trackingEllipsoid.projectRay(tr.screenPointToMercatorRay(around).dir);
const endPoint = this._trackingEllipsoid.projectRay(tr.screenPointToMercatorRay(around.sub(panDelta)).dir);
panVec[0] = endPoint[0] - startPoint[0];
panVec[1] = endPoint[1] - startPoint[1];

const startPoint = tr.pointCoordinate(around);
if (tr.projection.name === 'globe') {
const startLat = latFromMercatorY(startPoint.y);
const centerLat = tr.center.lat;

// Compute pan vector directly in pixel coordinates for the globe.
// Rotate the globe a bit faster when dragging near poles to compensate
// different pixel-per-meter ratios (ie. pixel-to-physical-rotation is lower)
const scale = Math.min(mercatorZfromAltitude(1, startLat) / mercatorZfromAltitude(1, centerLat), 2);

panDelta = panDelta.rotate(-tr.angle);

panVec[0] = -panDelta.x / tr.worldSize * scale;
panVec[1] = -panDelta.y / tr.worldSize * scale;
} else {
const endPoint = tr.pointCoordinate(around.sub(panDelta));

if (startPoint && endPoint) {
panVec[0] = endPoint.x - startPoint.x;
panVec[1] = endPoint.y - startPoint.y;
const startPoint = tr.pointCoordinate(around);
if (tr.projection.name === 'globe') {
// Compute pan vector directly in pixel coordinates for the globe.
// Rotate the globe a bit faster when dragging near poles to compensate
// different pixel-per-meter ratios (ie. pixel-to-physical-rotation is lower)
panDelta = panDelta.rotate(-tr.angle);
const scale = tr._pixelsPerMercatorPixel / tr.worldSize;
panVec[0] = -panDelta.x * mercatorScale(latFromMercatorY(startPoint.y)) * scale;
panVec[1] = -panDelta.y * mercatorScale(tr.center.lat) * scale;

} else {
const endPoint = tr.pointCoordinate(around.sub(panDelta));

if (startPoint && endPoint) {
panVec[0] = endPoint.x - startPoint.x;
panVec[1] = endPoint.y - startPoint.y;
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
},
"center": [
0,
-89
-48.56
],
"zoom": 0.5,
"pitch": 0,
Expand Down
45 changes: 22 additions & 23 deletions test/unit/style/query_geometry.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,36 +31,35 @@ test('Query geometry', (t) => {

const expected = [
// begin top edge
{x:0.38086, y:0.22541},
{x:0.3261, y:0.19212},
{x:0.28999, y:0.18054},
{x:0.25, y:0.17622},
{x:0.21001, y:0.18054},
{x:0.1739, y:0.19212},
{x:0.11914, y:0.22541},
{x: 0.3668, y: 0.24073},
{x: 0.31562, y: 0.21521},
{x: 0.28401, y: 0.20695},
{x: 0.25, y: 0.20395},
{x: 0.18438, y: 0.21521},
{x: 0.1332, y: 0.24073},
// end top edge

// the right edge is crossing the antimeridian so it's split at the location of the intersection.
// To wrap the position to the other side of the line we'll do a round trip by covering the whole
// polar area
{x:0, y:0.22349},
{x:0, y:0},
{x:1, y:0},
{x:1, y:0.22349},
{x: 0, y: 0.2213},
{x: 0, y: 0},
{x: 1, y: 0},
{x: 1, y: 0.2213},

// bottom and left edges
{x:0.95863, y:0.22283},
{x:0.86343, y:0.16406},
{x:0.80903, y:0.14555},
{x:0.75, y:0.1387},
{x:0.69097, y:0.14555},
{x:0.63657, y:0.16406},
{x:0.54137, y:0.22283},
{x:0.48864, y:0.22646},
{x:0.41885, y:0.26177},
{x:0.41244, y:0.25477},
{x:0.40068, y:0.24287},
{x:0.38086, y:0.22541}
{x: 0.98003, y: 0.21839},
{x: 0.88765, y: 0.13929},
{x: 0.82536, y: 0.10832},
{x: 0.75, y: 0.09541},
{x: 0.67464, y: 0.10832},
{x: 0.61235, y: 0.13929},
{x: 0.51997, y: 0.21839},
{x: 0.46807, y: 0.22947},
{x: 0.40528, y: 0.27109},
{x: 0.3986, y: 0.26507},
{x: 0.38656, y: 0.25503},
{x: 0.3668, y: 0.24073}
];

const actual = projected.polygon.map(p => { return {x: fixedNum(p.x, 5), y: fixedNum(p.y, 5)}; });
Expand Down

0 comments on commit 3d14aeb

Please sign in to comment.