Skip to content

Commit

Permalink
Constrain panning on single-world map (#3738)
Browse files Browse the repository at this point in the history
* Constrain horizontal panning if !renderWorldCopies

* Trigger map refresh in render-world-copies example

* Test horizontal constraint on single-world map

* Prevent world wrap on single-globe map

* Add tests

* Wrap marker and popup position on single-globe map

* Use optional chaining when wrapping marker/popup lng

* Add changelog

---------

Co-authored-by: Harel M <[email protected]>
  • Loading branch information
sbachinin and HarelM authored Mar 1, 2024
1 parent 8698ff9 commit 7a7cc77
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 6 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
## main

### ✨ Features and improvements
- _...Add new stuff here..._
- Add option to position popup at subpixel coordinates to allow for smooth animations ([#3710](https://github.com/maplibre/maplibre-gl-js/pull/3710))
- Constrain horizontal panning when renderWorldCopies is set to false ([3738](https://github.com/maplibre/maplibre-gl-js/pull/3738))
- _...Add new stuff here..._

### 🐞 Bug fixes
- Fix popup appearing far from marker that was moved to a side globe ([3712](https://github.com/maplibre/maplibre-gl-js/pull/3712))
Expand Down
18 changes: 13 additions & 5 deletions src/geo/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,13 @@ export class Transform {
_constrain() {
if (!this.center || !this.width || !this.height || this._constraining) return;

let lngRange = this.lngRange;

if (!this._renderWorldCopies && lngRange === null) {
const almost180 = 180 - 1e-10;
lngRange = [-almost180, almost180];
}

this._constraining = true;

let minY = -90;
Expand All @@ -731,9 +738,7 @@ export class Transform {
sy = maxY - minY < size.y ? size.y / (maxY - minY) : 0;
}

if (this.lngRange) {
const lngRange = this.lngRange;

if (lngRange) {
minX = wrap(
mercatorXfromLng(lngRange[0]) * this.worldSize,
0,
Expand Down Expand Up @@ -773,9 +778,12 @@ export class Transform {
if (y + h2 > maxY) y2 = maxY - h2;
}

if (this.lngRange) {
if (lngRange) {
const centerX = (minX + maxX) / 2;
const x = wrap(point.x, centerX - this.worldSize / 2, centerX + this.worldSize / 2);
let x = point.x;
if (this._renderWorldCopies) {
x = wrap(point.x, centerX - this.worldSize / 2, centerX + this.worldSize / 2);
}
const w2 = size.x / 2;

if (x - w2 < minX) x2 = minX + w2;
Expand Down
57 changes: 57 additions & 0 deletions src/ui/map.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1079,6 +1079,63 @@ describe('Map', () => {

});

describe('renderWorldCopies', () => {
test('does not constrain horizontal panning when renderWorldCopies is set to true', () => {
const map = createMap({renderWorldCopies: true});
map.setCenter({lng: 180, lat: 0});
expect(map.getCenter().lng).toBe(180);
});

test('constrains horizontal panning when renderWorldCopies is set to false', () => {
const map = createMap({renderWorldCopies: false});
map.setCenter({lng: 180, lat: 0});
expect(map.getCenter().lng).toBeCloseTo(110, 0);
});

test('does not wrap the map when renderWorldCopies is set to false', () => {
const map = createMap({renderWorldCopies: false});
map.setCenter({lng: 200, lat: 0});
expect(map.getCenter().lng).toBeCloseTo(110, 0);
});

test('panTo is constrained to single globe when renderWorldCopies is set to false', () => {
const map = createMap({renderWorldCopies: false});
map.panTo({lng: 180, lat: 0}, {duration: 0});
expect(map.getCenter().lng).toBeCloseTo(110, 0);
map.panTo({lng: -3000, lat: 0}, {duration: 0});
expect(map.getCenter().lng).toBeCloseTo(-110, 0);
});

test('flyTo is constrained to single globe when renderWorldCopies is set to false', () => {
const map = createMap({renderWorldCopies: false});
map.flyTo({center: [1000, 0], zoom: 3, animate: false});
expect(map.getCenter().lng).toBeCloseTo(171, 0);
map.flyTo({center: [-1000, 0], zoom: 5, animate: false});
expect(map.getCenter().lng).toBeCloseTo(-178, 0);
});

test('lng is constrained to a single globe when zooming with {renderWorldCopies: false}', () => {
const map = createMap({renderWorldCopies: false, center: [180, 0], zoom: 2});
expect(map.getCenter().lng).toBeCloseTo(162, 0);
map.zoomTo(1, {animate: false});
expect(map.getCenter().lng).toBeCloseTo(145, 0);
});

test('lng is constrained by maxBounds when {renderWorldCopies: false}', () => {
const map = createMap({
renderWorldCopies: false,
maxBounds: [
[70, 30],
[80, 40]
],
zoom: 8,
center: [75, 35]
});
map.setCenter({lng: 180, lat: 0});
expect(map.getCenter().lng).toBeCloseTo(80, 0);
});
});

test('#setMinZoom', () => {
const map = createMap({zoom: 5});
map.setMinZoom(3.5);
Expand Down
12 changes: 12 additions & 0 deletions src/ui/marker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {Terrain} from '../render/terrain';

type MapOptions = {
width?: number;
renderWorldCopies?: boolean;
}

function createMap(options: MapOptions = {}) {
Expand Down Expand Up @@ -1027,4 +1028,15 @@ describe('marker', () => {
expect(marker.getElement().style.opacity).toMatch('0.35');
map.remove();
});

test('Marker\'s lng is wrapped when slightly crossing 180 with {renderWorldCopies: false}', () => {
const map = createMap({width: 1024, renderWorldCopies: false});
const marker = new Marker()
.setLngLat([179, 0])
.addTo(map);

marker.setLngLat([181, 0]);

expect(marker._lngLat.lng).toBe(-179);
});
});
2 changes: 2 additions & 0 deletions src/ui/marker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,8 @@ export class Marker extends Evented {

if (this._map.transform.renderWorldCopies) {
this._lngLat = smartWrap(this._lngLat, this._flatPos, this._map.transform);
} else {
this._lngLat = this._lngLat?.wrap();
}

this._flatPos = this._pos = this._map.project(this._lngLat)._add(this._offset);
Expand Down
2 changes: 2 additions & 0 deletions src/ui/popup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,8 @@ export class Popup extends Evented {

if (this._map.transform.renderWorldCopies && !this._trackPointer) {
this._lngLat = smartWrap(this._lngLat, this._flatPos, this._map.transform);
} else {
this._lngLat = this._lngLat?.wrap();
}

if (this._trackPointer && !cursor) return;
Expand Down
1 change: 1 addition & 0 deletions test/examples/render-world-copies.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
function switchRenderOption(option) {
const status = option.target.id;
map.setRenderWorldCopies(status === 'true');
map.panTo(map.getCenter());
}

for (let i = 0; i < inputs.length; i++) {
Expand Down

0 comments on commit 7a7cc77

Please sign in to comment.