maplibre-gl
Version:
BSD licensed community fork of mapbox-gl, a WebGL interactive maps library
182 lines (134 loc) • 6.75 kB
text/typescript
import {afterEach, beforeEach, describe, test, expect} from 'vitest';
import {createMap, beforeMapTest, createIdentityCrsAnsweringInside, createPolarStereographicCrs, createRotatedCrs, createWidePlateCarreeCrs} from '../../util/test/util.ts';
import {addProjection, removeProjection} from '../../geo/projection/projection_crud.ts';
import {MAX_VALID_LATITUDE} from '../../util/util.ts';
import {LngLat} from '../../geo/lng_lat.ts';
beforeEach(() => {
beforeMapTest();
global.fetch = null;
});
describe('Map in the identity projection', () => {
test('loads a style that declares the identity projection', async () => {
const map = createMap({style: {version: 8, sources: {}, layers: [], projection: {type: 'identity'}}});
await map.once('style.load');
expect(map.getProjection()).toEqual({type: 'identity'});
});
test('keeps an initial center north of the mercator latitude limit', async () => {
const latitudeNorthOfMercatorLimit = MAX_VALID_LATITUDE + 1;
const map = createMap({style: {version: 8, sources: {}, layers: [], projection: {type: 'identity'}}, center: [0, latitudeNorthOfMercatorLimit], zoom: 6});
await map.once('style.load');
expect(map.getCenter().lat).toBeCloseTo(latitudeNorthOfMercatorLimit, 6);
});
test('returns to the identity projection after a round trip through globe', async () => {
const map = createMap();
await map.once('style.load');
map.setProjection({type: 'identity'});
map.setProjection({type: 'globe'});
expect(map.getProjection()).toEqual({type: 'globe'});
map.setProjection({type: 'identity'});
expect(map.getProjection()).toEqual({type: 'identity'});
});
test('keeps mercator between its latitude limits after max bounds were cleared in the identity projection', async () => {
const map = createMap({style: {version: 8, sources: {}, layers: [], projection: {type: 'identity'}}, zoom: 3});
await map.once('style.load');
map.setMaxBounds(null);
map.setProjection({type: 'mercator'});
map.setCenter([0, 89]);
expect(map.getCenter().lat).toBeCloseTo(84.231948, 6);
});
test('stops the center at the east edge of the world square', async () => {
const map = createMap({style: {version: 8, sources: {}, layers: [], projection: {type: 'identity'}}, zoom: 3});
await map.once('style.load');
map.setCenter([170, 0]);
expect(map.getCenter().lng).toBeCloseTo(90, 6);
});
});
describe('Map in a CRS registered with addProjection', () => {
const crs = createRotatedCrs();
beforeEach(() => {
addProjection(crs);
});
afterEach(() => {
removeProjection(crs.name);
});
test('setProjection accepts the registered name', async () => {
const map = createMap();
await map.once('style.load');
map.setProjection({type: crs.name});
expect(map.getProjection()).toEqual({type: crs.name});
});
test('projects lng/lat to the screen through the registered CRS', async () => {
const map = createMap();
await map.once('style.load');
map.setProjection({type: crs.name});
const worldSizeAtZoom0 = 512;
const worldOffsetInContainer = (worldSizeAtZoom0 - map.getContainer().clientWidth) / 2;
const worldFractionOfCrsPoint = {x: 0.7, y: 0.5};
const {origin, extentAtZoom0} = crs.tileMatrixSet;
const crsPoint = {x: origin[0] + worldFractionOfCrsPoint.x * extentAtZoom0, y: origin[1] - worldFractionOfCrsPoint.y * extentAtZoom0};
const [lng, lat] = crs.projection.inverse([crsPoint.x, crsPoint.y]);
const screenPoint = map.project([lng, lat]);
expect(screenPoint.x).toBeCloseTo(worldFractionOfCrsPoint.x * worldSizeAtZoom0 - worldOffsetInContainer, 6);
expect(screenPoint.y).toBeCloseTo(worldFractionOfCrsPoint.y * worldSizeAtZoom0 - worldOffsetInContainer, 6);
});
});
describe('Map in a polar stereographic CRS', () => {
const crs = createPolarStereographicCrs();
beforeEach(() => {
addProjection(crs);
});
afterEach(() => {
removeProjection(crs.name);
});
test('fits the cap north of 60N with the pole at the center', async () => {
const map = createMap({style: {version: 8, sources: {}, layers: [], projection: {type: crs.name}}});
await map.once('style.load');
const camera = map.cameraForBounds([[-180, 60], [180, 90]]);
expect(LngLat.convert(camera.center).lat).toBeCloseTo(90, 6);
});
test('keeps a center on the far side of the pole inside maxBounds of the cap north of 60N', async () => {
const map = createMap({style: {version: 8, sources: {}, layers: [], projection: {type: crs.name}}, maxBounds: [[-180, 60], [180, 90]], zoom: 3});
await map.once('style.load');
map.setCenter([45, 70]);
expect(map.getCenter().lng).toBeCloseTo(45, 6);
expect(map.getCenter().lat).toBeCloseTo(70, 6);
});
});
describe('Map in a CRS whose converter answers only inside its tile matrix set', () => {
const crs = createIdentityCrsAnsweringInside(null);
beforeEach(() => {
addProjection(crs);
});
afterEach(() => {
removeProjection(crs.name);
});
test('unprojects a point past the west edge of the world square to the edge', async () => {
const map = createMap({style: {version: 8, sources: {}, layers: [], projection: {type: crs.name}}});
await map.once('style.load');
const containerMiddle = 100;
const pastTheWestEdge = map.unproject([-1000, containerMiddle]);
expect(pastTheWestEdge.lng).toBeCloseTo(-90, 6);
expect(pastTheWestEdge.lat).toBeCloseTo(0, 6);
});
});
describe('Map in a CRS wider than ±180°', () => {
const crs = createWidePlateCarreeCrs();
beforeEach(() => {
addProjection(crs);
});
afterEach(() => {
removeProjection(crs.name);
});
test('eases to a center past 180° without wrapping it', async () => {
const map = createMap({style: {version: 8, sources: {}, layers: [], projection: {type: crs.name}}});
await map.once('style.load');
map.easeTo({center: [200, 0], duration: 0});
expect(map.getCenter().lng).toBeCloseTo(200, 6);
});
test('flies to a center past 180° without wrapping it', async () => {
const map = createMap({style: {version: 8, sources: {}, layers: [], projection: {type: crs.name}}});
await map.once('style.load');
map.flyTo({center: [200, 0], duration: 0});
expect(map.getCenter().lng).toBeCloseTo(200, 6);
});
});