diff --git a/src/geo/transform.js b/src/geo/transform.js index 2e70e78fb2a..3417e20faa3 100644 --- a/src/geo/transform.js +++ b/src/geo/transform.js @@ -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; diff --git a/src/ui/handler_manager.js b/src/ui/handler_manager.js index f6c54750d44..3796f2e54fc 100644 --- a/src/ui/handler_manager.js +++ b/src/ui/handler_manager.js @@ -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'; @@ -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; + } } } } diff --git a/test/integration/query-tests/globe/poles/south-pole-box-inside/style.json b/test/integration/query-tests/globe/poles/south-pole-box-inside/style.json index 4dab934f802..c96774b6611 100644 --- a/test/integration/query-tests/globe/poles/south-pole-box-inside/style.json +++ b/test/integration/query-tests/globe/poles/south-pole-box-inside/style.json @@ -18,7 +18,7 @@ }, "center": [ 0, - -89 + -48.56 ], "zoom": 0.5, "pitch": 0, diff --git a/test/unit/style/query_geometry.test.js b/test/unit/style/query_geometry.test.js index aba0d562810..bda0781ce2f 100644 --- a/test/unit/style/query_geometry.test.js +++ b/test/unit/style/query_geometry.test.js @@ -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)}; });