maplibre-gl
Version:
BSD licensed community fork of mapbox-gl, a WebGL interactive maps library
606 lines (512 loc) • 31.1 kB
text/typescript
import {describe, expect, test} from 'vitest';
import {EXTENT} from '../../data/extent';
import Point from '@mapbox/point-geometry';
import {LngLat} from '../lng_lat';
import {GlobeTransform} from './globe_transform';
import {CanonicalTileID, OverscaledTileID, UnwrappedTileID} from '../../source/tile_id';
import {angularCoordinatesRadiansToVector, mercatorCoordinatesToAngularCoordinatesRadians, sphereSurfacePointToCoordinates} from './globe_utils';
import {expectToBeCloseToArray} from '../../util/test/util';
import {MercatorCoordinate} from '../mercator_coordinate';
import {tileCoordinatesToLocation} from './mercator_utils';
import {MercatorTransform} from './mercator_transform';
import {globeConstants} from './vertical_perspective_projection';
function testPlaneAgainstLngLat(lngDegrees: number, latDegrees: number, plane: Array<number>) {
const lat = latDegrees / 180.0 * Math.PI;
const lng = lngDegrees / 180.0 * Math.PI;
const len = Math.cos(lat);
const pointOnSphere = [
Math.sin(lng) * len,
Math.sin(lat),
Math.cos(lng) * len
];
return planeDistance(pointOnSphere, plane);
}
function planeDistance(point: Array<number>, plane: Array<number>) {
return point[0] * plane[0] + point[1] * plane[1] + point[2] * plane[2] + plane[3];
}
function createGlobeTransform() {
const globeTransform = new GlobeTransform();
globeTransform.resize(640, 480);
globeTransform.setFov(45);
return globeTransform;
}
describe('GlobeTransform', () => {
// Force faster animations so we can use shorter sleeps when testing them
globeConstants.errorTransitionTimeSeconds = 0.1;
describe('getProjectionData', () => {
const globeTransform = createGlobeTransform();
test('mercator tile extents are set', () => {
const projectionData = globeTransform.getProjectionData({overscaledTileID: new OverscaledTileID(1, 0, 1, 1, 0)});
expectToBeCloseToArray(projectionData.tileMercatorCoords, [0.5, 0, 0.5 / EXTENT, 0.5 / EXTENT]);
});
test('Globe transition is 0 when not applying the globe matrix', () => {
const projectionData = globeTransform.getProjectionData({overscaledTileID: new OverscaledTileID(1, 0, 1, 1, 0)});
expect(projectionData.projectionTransition).toBe(0);
});
test('Applying the globe matrix sets transition to something different than 0', () => {
const projectionData = globeTransform.getProjectionData({overscaledTileID: new OverscaledTileID(1, 0, 1, 1, 0), applyGlobeMatrix: true});
expect(projectionData.projectionTransition).not.toBe(0);
});
});
describe('clipping plane', () => {
const globeTransform = createGlobeTransform();
describe('general plane properties', () => {
const projectionData = globeTransform.getProjectionData({overscaledTileID: new OverscaledTileID(0, 0, 0, 0, 0)});
test('plane vector length <= 1 so they are not clipped by the near plane.', () => {
const len = Math.sqrt(
projectionData.clippingPlane[0] * projectionData.clippingPlane[0] +
projectionData.clippingPlane[1] * projectionData.clippingPlane[1] +
projectionData.clippingPlane[2] * projectionData.clippingPlane[2]
);
expect(len).toBeLessThanOrEqual(1);
});
test('camera is in positive halfspace', () => {
expect(planeDistance(globeTransform.cameraPosition as [number, number, number], projectionData.clippingPlane)).toBeGreaterThan(0);
});
test('coordinates 0E,0N are in positive halfspace', () => {
expect(testPlaneAgainstLngLat(0, 0, projectionData.clippingPlane)).toBeGreaterThan(0);
});
test('coordinates 40E,0N are in positive halfspace', () => {
expect(testPlaneAgainstLngLat(40, 0, projectionData.clippingPlane)).toBeGreaterThan(0);
});
test('coordinates 0E,90N are in negative halfspace', () => {
expect(testPlaneAgainstLngLat(0, 90, projectionData.clippingPlane)).toBeLessThan(0);
});
test('coordinates 90E,0N are in negative halfspace', () => {
expect(testPlaneAgainstLngLat(90, 0, projectionData.clippingPlane)).toBeLessThan(0);
});
test('coordinates 180E,0N are in negative halfspace', () => {
expect(testPlaneAgainstLngLat(180, 0, projectionData.clippingPlane)).toBeLessThan(0);
});
});
});
describe('projection', () => {
test('mercator coordinate to sphere point', () => {
const precisionDigits = 10;
let projectedAngles;
let projected;
projectedAngles = mercatorCoordinatesToAngularCoordinatesRadians(0.5, 0.5);
expectToBeCloseToArray(projectedAngles, [0, 0], precisionDigits);
projected = angularCoordinatesRadiansToVector(projectedAngles[0], projectedAngles[1]) as [number, number, number];
expectToBeCloseToArray(projected, [0, 0, 1], precisionDigits);
projectedAngles = mercatorCoordinatesToAngularCoordinatesRadians(0, 0.5);
expectToBeCloseToArray(projectedAngles, [Math.PI, 0], precisionDigits);
projected = angularCoordinatesRadiansToVector(projectedAngles[0], projectedAngles[1]) as [number, number, number];
expectToBeCloseToArray(projected, [0, 0, -1], precisionDigits);
projectedAngles = mercatorCoordinatesToAngularCoordinatesRadians(0.75, 0.5);
expectToBeCloseToArray(projectedAngles, [Math.PI / 2.0, 0], precisionDigits);
projected = angularCoordinatesRadiansToVector(projectedAngles[0], projectedAngles[1]) as [number, number, number];
expectToBeCloseToArray(projected, [1, 0, 0], precisionDigits);
projectedAngles = mercatorCoordinatesToAngularCoordinatesRadians(0.5, 0);
expectToBeCloseToArray(projectedAngles, [0, 1.4844222297453324], precisionDigits); // ~0.47pi
projected = angularCoordinatesRadiansToVector(projectedAngles[0], projectedAngles[1]) as [number, number, number];
expectToBeCloseToArray(projected, [0, 0.99627207622075, 0.08626673833405434], precisionDigits);
});
test('camera position', () => {
const precisionDigits = 10;
const globeTransform = createGlobeTransform();
expectToBeCloseToArray(globeTransform.cameraPosition as Array<number>, [0, 0, 8.110445867263898], precisionDigits);
globeTransform.resize(512, 512);
globeTransform.setZoom(-0.5);
globeTransform.setCenter(new LngLat(0, 80));
expectToBeCloseToArray(globeTransform.cameraPosition as Array<number>, [0, 2.2818294674820794, 0.40234810049271963], precisionDigits);
globeTransform.setPitch(35);
globeTransform.setBearing(70);
expectToBeCloseToArray(globeTransform.cameraPosition as Array<number>, [-0.7098603286961542, 2.002400604307631, 0.6154310261827212], precisionDigits);
globeTransform.setPitch(35);
globeTransform.setBearing(70);
globeTransform.setRoll(40);
expectToBeCloseToArray(globeTransform.cameraPosition as Array<number>, [-0.7098603286961542, 2.002400604307631, 0.6154310261827212], precisionDigits);
globeTransform.setPitch(35);
globeTransform.setBearing(70);
globeTransform.setRoll(180);
expectToBeCloseToArray(globeTransform.cameraPosition as Array<number>, [-0.7098603286961542, 2.002400604307631, 0.6154310261827212], precisionDigits);
globeTransform.setCenter(new LngLat(-10, 42));
expectToBeCloseToArray(globeTransform.cameraPosition as Array<number>, [-3.8450970996236364, 2.9368285470351516, 4.311953269048194], precisionDigits);
});
test('sphere point to coordinate', () => {
const precisionDigits = 10;
let unprojected = sphereSurfacePointToCoordinates([0, 0, 1]) as LngLat;
expect(unprojected.lng).toBeCloseTo(0, precisionDigits);
expect(unprojected.lat).toBeCloseTo(0, precisionDigits);
unprojected = sphereSurfacePointToCoordinates([0, 1, 0]) as LngLat;
expect(unprojected.lng).toBeCloseTo(0, precisionDigits);
expect(unprojected.lat).toBeCloseTo(90, precisionDigits);
unprojected = sphereSurfacePointToCoordinates([1, 0, 0]) as LngLat;
expect(unprojected.lng).toBeCloseTo(90, precisionDigits);
expect(unprojected.lat).toBeCloseTo(0, precisionDigits);
});
const screenCenter = new Point(640 / 2, 480 / 2); // We need the exact screen center
const screenTopEdgeCenter = new Point(640 / 2, 0);
describe('project location to coordinates', () => {
const precisionDigits = 10;
const globeTransform = createGlobeTransform();
test('basic test', () => {
globeTransform.setCenter(new LngLat(0, 0));
let projected = globeTransform.locationToScreenPoint(globeTransform.center);
expect(projected.x).toBeCloseTo(screenCenter.x, precisionDigits);
expect(projected.y).toBeCloseTo(screenCenter.y, precisionDigits);
globeTransform.setCenter(new LngLat(70, 50));
projected = globeTransform.locationToScreenPoint(globeTransform.center);
expect(projected.x).toBeCloseTo(screenCenter.x, precisionDigits);
expect(projected.y).toBeCloseTo(screenCenter.y, precisionDigits);
globeTransform.setCenter(new LngLat(0, 84));
projected = globeTransform.locationToScreenPoint(globeTransform.center);
expect(projected.x).toBeCloseTo(screenCenter.x, precisionDigits);
expect(projected.y).toBeCloseTo(screenCenter.y, precisionDigits);
});
test('project a location that is slightly above and below map\'s center point', () => {
globeTransform.setCenter(new LngLat(0, 0));
let projected = globeTransform.locationToScreenPoint(new LngLat(0, 1));
expect(projected.x).toBeCloseTo(screenCenter.x, precisionDigits);
expect(projected.y).toBeLessThan(screenCenter.y);
projected = globeTransform.locationToScreenPoint(new LngLat(0, -1));
expect(projected.x).toBeCloseTo(screenCenter.x, precisionDigits);
expect(projected.y).toBeGreaterThan(screenCenter.y);
});
});
describe('unproject', () => {
test('unproject screen center', () => {
const precisionDigits = 10;
const globeTransform = createGlobeTransform();
let unprojected = globeTransform.screenPointToLocation(screenCenter);
expect(unprojected.lng).toBeCloseTo(globeTransform.center.lng, precisionDigits);
expect(unprojected.lat).toBeCloseTo(globeTransform.center.lat, precisionDigits);
globeTransform.setCenter(new LngLat(90.0, 0.0));
unprojected = globeTransform.screenPointToLocation(screenCenter);
expect(unprojected.lng).toBeCloseTo(globeTransform.center.lng, precisionDigits);
expect(unprojected.lat).toBeCloseTo(globeTransform.center.lat, precisionDigits);
globeTransform.setCenter(new LngLat(0.0, 60.0));
unprojected = globeTransform.screenPointToLocation(screenCenter);
expect(unprojected.lng).toBeCloseTo(globeTransform.center.lng, precisionDigits);
expect(unprojected.lat).toBeCloseTo(globeTransform.center.lat, precisionDigits);
});
test('unproject point to the side', () => {
const precisionDigits = 10;
const globeTransform = createGlobeTransform();
let coords: LngLat;
let projected: Point;
let unprojected: LngLat;
coords = new LngLat(0, 0);
projected = globeTransform.locationToScreenPoint(coords);
unprojected = globeTransform.screenPointToLocation(projected);
expect(unprojected.lng).toBeCloseTo(coords.lng, precisionDigits);
expect(unprojected.lat).toBeCloseTo(coords.lat, precisionDigits);
coords = new LngLat(10, 20);
projected = globeTransform.locationToScreenPoint(coords);
unprojected = globeTransform.screenPointToLocation(projected);
expect(unprojected.lng).toBeCloseTo(coords.lng, precisionDigits);
expect(unprojected.lat).toBeCloseTo(coords.lat, precisionDigits);
coords = new LngLat(15, -2);
projected = globeTransform.locationToScreenPoint(coords);
unprojected = globeTransform.screenPointToLocation(projected);
expect(unprojected.lng).toBeCloseTo(coords.lng, precisionDigits);
expect(unprojected.lat).toBeCloseTo(coords.lat, precisionDigits);
});
test('unproject behind the pole', () => {
// This test tries to unproject a point that is beyond the north pole
// from the camera's point of view.
// This particular case turned out to be problematic, hence this test.
const precisionDigits = 10;
const globeTransform = createGlobeTransform();
// Transform settings from the render test projection/globe/fill-planet-pole
// See the expected result for how the globe should look with this transform.
globeTransform.resize(512, 512);
globeTransform.setZoom(-0.5);
globeTransform.setCenter(new LngLat(0, 80));
let coords: LngLat;
let projected: Point;
let unprojected: LngLat;
coords = new LngLat(179.9, 71);
projected = globeTransform.locationToScreenPoint(coords);
unprojected = globeTransform.screenPointToLocation(projected);
expect(projected.x).toBeCloseTo(256.2434702034287, precisionDigits);
expect(projected.y).toBeCloseTo(48.27080146399297, precisionDigits);
expect(unprojected.lng).toBeCloseTo(coords.lng, precisionDigits);
expect(unprojected.lat).toBeCloseTo(coords.lat, precisionDigits);
// Near the pole
coords = new LngLat(179.9, 89.0);
projected = globeTransform.locationToScreenPoint(coords);
unprojected = globeTransform.screenPointToLocation(projected);
expect(projected.x).toBeCloseTo(256.0140972925064, precisionDigits);
expect(projected.y).toBeCloseTo(167.69159699932908, precisionDigits);
expect(unprojected.lng).toBeCloseTo(coords.lng, precisionDigits);
expect(unprojected.lat).toBeCloseTo(coords.lat, precisionDigits);
});
test('unproject outside of sphere', () => {
const precisionDigits = 10;
const globeTransform = createGlobeTransform();
// Try unprojection a point somewhere above the western horizon
globeTransform.setPitch(60);
globeTransform.setBearing(-90);
const unprojected = globeTransform.screenPointToLocation(screenTopEdgeCenter);
expect(unprojected.lng).toBeCloseTo(-28.990298145461963, precisionDigits);
expect(unprojected.lat).toBeCloseTo(0.0, precisionDigits);
});
test('unproject further outside of sphere clamps to horizon', () => {
const globeTransform = createGlobeTransform();
globeTransform.setPitch(60);
globeTransform.setBearing(-90);
const screenPointAboveWesternHorizon = screenTopEdgeCenter;
const screenPointFurtherAboveWesternHorizon = screenTopEdgeCenter.sub(new Point(0, -100));
const unprojected = globeTransform.screenPointToLocation(screenPointAboveWesternHorizon);
const unprojected2 = globeTransform.screenPointToLocation(screenPointFurtherAboveWesternHorizon);
expect(unprojected).toEqual(unprojected2);
});
});
describe('setLocationAtPoint', () => {
const precisionDigits = 10;
const globeTransform = createGlobeTransform();
globeTransform.setZoom(1);
let coords: LngLat;
let point: Point;
let projected: Point;
let unprojected: LngLat;
test('identity', () => {
// Should do nothing
coords = new LngLat(0, 0);
point = new Point(320, 240);
globeTransform.setLocationAtPoint(coords, point);
unprojected = globeTransform.screenPointToLocation(point);
projected = globeTransform.locationToScreenPoint(coords);
expect(unprojected.lng).toBeCloseTo(coords.lng, precisionDigits);
expect(unprojected.lat).toBeCloseTo(coords.lat, precisionDigits);
expect(projected.x).toBeCloseTo(point.x, precisionDigits);
expect(projected.y).toBeCloseTo(point.y, precisionDigits);
});
test('offset lnglat', () => {
coords = new LngLat(5, 10);
point = new Point(320, 240);
globeTransform.setLocationAtPoint(coords, point);
unprojected = globeTransform.screenPointToLocation(point);
projected = globeTransform.locationToScreenPoint(coords);
expect(unprojected.lng).toBeCloseTo(coords.lng, precisionDigits);
expect(unprojected.lat).toBeCloseTo(coords.lat, precisionDigits);
expect(projected.x).toBeCloseTo(point.x, precisionDigits);
expect(projected.y).toBeCloseTo(point.y, precisionDigits);
});
test('offset pixel + lnglat', () => {
coords = new LngLat(5, 10);
point = new Point(330, 240); // 10 pixels to the right
globeTransform.setLocationAtPoint(coords, point);
unprojected = globeTransform.screenPointToLocation(point);
projected = globeTransform.locationToScreenPoint(coords);
expect(unprojected.lng).toBeCloseTo(coords.lng, precisionDigits);
expect(unprojected.lat).toBeCloseTo(coords.lat, precisionDigits);
expect(projected.x).toBeCloseTo(point.x, precisionDigits);
expect(projected.y).toBeCloseTo(point.y, precisionDigits);
});
test('larger offset', () => {
coords = new LngLat(30, -2);
point = new Point(250, 180);
globeTransform.setLocationAtPoint(coords, point);
unprojected = globeTransform.screenPointToLocation(point);
projected = globeTransform.locationToScreenPoint(coords);
expect(unprojected.lng).toBeCloseTo(coords.lng, precisionDigits);
expect(unprojected.lat).toBeCloseTo(coords.lat, precisionDigits);
expect(projected.x).toBeCloseTo(point.x, precisionDigits);
expect(projected.y).toBeCloseTo(point.y, precisionDigits);
});
describe('rotated', () => {
globeTransform.setBearing(90);
test('identity', () => {
// Should do nothing
coords = new LngLat(0, 0);
point = new Point(320, 240);
globeTransform.setLocationAtPoint(coords, point);
unprojected = globeTransform.screenPointToLocation(point);
projected = globeTransform.locationToScreenPoint(coords);
expect(unprojected.lng).toBeCloseTo(coords.lng, precisionDigits);
expect(unprojected.lat).toBeCloseTo(coords.lat, precisionDigits);
expect(projected.x).toBeCloseTo(point.x, precisionDigits);
expect(projected.y).toBeCloseTo(point.y, precisionDigits);
});
test('offset lnglat', () => {
coords = new LngLat(5, 0);
point = new Point(320, 240);
globeTransform.setLocationAtPoint(coords, point);
unprojected = globeTransform.screenPointToLocation(point);
projected = globeTransform.locationToScreenPoint(coords);
expect(unprojected.lng).toBeCloseTo(coords.lng, precisionDigits);
expect(unprojected.lat).toBeCloseTo(coords.lat, precisionDigits);
expect(projected.x).toBeCloseTo(point.x, precisionDigits);
expect(projected.y).toBeCloseTo(point.y, precisionDigits);
});
test('offset pixel + lnglat', () => {
coords = new LngLat(0, 10);
point = new Point(350, 240); // 30 pixels to the right
globeTransform.setLocationAtPoint(coords, point);
unprojected = globeTransform.screenPointToLocation(point);
projected = globeTransform.locationToScreenPoint(coords);
expect(unprojected.lng).toBeCloseTo(coords.lng, precisionDigits);
expect(unprojected.lat).toBeCloseTo(coords.lat, precisionDigits);
expect(projected.x).toBeCloseTo(point.x, precisionDigits);
expect(projected.y).toBeCloseTo(point.y, precisionDigits);
expect(globeTransform.center.lat).toBeCloseTo(20.659450722109348, precisionDigits);
});
});
});
});
describe('isPointOnMapSurface', () => {
const globeTransform = new GlobeTransform();
globeTransform.resize(640, 480);
globeTransform.setZoom(1);
test('Top screen edge', () => {
expect(globeTransform.isPointOnMapSurface(new Point(320, 0))).toBe(false);
});
test('Screen center', () => {
expect(globeTransform.isPointOnMapSurface(new Point(320, 240))).toBe(true);
});
test('Top', () => {
expect(globeTransform.isPointOnMapSurface(new Point(320, 104))).toBe(false);
expect(globeTransform.isPointOnMapSurface(new Point(320, 105))).toBe(true);
});
test('Bottom', () => {
expect(globeTransform.isPointOnMapSurface(new Point(320, 480 - 105))).toBe(true);
expect(globeTransform.isPointOnMapSurface(new Point(320, 480 - 104))).toBe(false);
});
test('Left', () => {
expect(globeTransform.isPointOnMapSurface(new Point(184, 240))).toBe(false);
expect(globeTransform.isPointOnMapSurface(new Point(185, 240))).toBe(true);
});
test('Right', () => {
expect(globeTransform.isPointOnMapSurface(new Point(640 - 185, 240))).toBe(true);
expect(globeTransform.isPointOnMapSurface(new Point(640 - 184, 240))).toBe(false);
});
test('Diagonal', () => {
expect(globeTransform.isPointOnMapSurface(new Point(223, 147))).toBe(true);
expect(globeTransform.isPointOnMapSurface(new Point(221, 144))).toBe(false);
});
});
test('pointCoordinate', () => {
const precisionDigits = 10;
const globeTransform = createGlobeTransform();
let coords: LngLat;
let coordsMercator: MercatorCoordinate;
let projected: Point;
let unprojectedCoordinates: MercatorCoordinate;
coords = new LngLat(0, 0);
coordsMercator = MercatorCoordinate.fromLngLat(coords);
projected = globeTransform.locationToScreenPoint(coords);
unprojectedCoordinates = globeTransform.screenPointToMercatorCoordinate(projected);
expect(unprojectedCoordinates.x).toBeCloseTo(coordsMercator.x, precisionDigits);
expect(unprojectedCoordinates.y).toBeCloseTo(coordsMercator.y, precisionDigits);
coords = new LngLat(10, 20);
coordsMercator = MercatorCoordinate.fromLngLat(coords);
projected = globeTransform.locationToScreenPoint(coords);
unprojectedCoordinates = globeTransform.screenPointToMercatorCoordinate(projected);
expect(unprojectedCoordinates.x).toBeCloseTo(coordsMercator.x, precisionDigits);
expect(unprojectedCoordinates.y).toBeCloseTo(coordsMercator.y, precisionDigits);
});
describe('getBounds', () => {
const precisionDigits = 10;
const globeTransform = new GlobeTransform();
globeTransform.resize(640, 480);
test('basic', () => {
globeTransform.setCenter(new LngLat(0, 0));
globeTransform.setZoom(1);
const bounds = globeTransform.getBounds();
expect(bounds._ne.lat).toBeCloseTo(79.3636705287052, precisionDigits);
expect(bounds._ne.lng).toBeCloseTo(79.36367052870514, precisionDigits);
expect(bounds._sw.lat).toBeCloseTo(-79.3636705287052, precisionDigits);
expect(bounds._sw.lng).toBeCloseTo(-79.3636705287052, precisionDigits);
});
test('zoomed in', () => {
globeTransform.setCenter(new LngLat(0, 0));
globeTransform.setZoom(4);
const bounds = globeTransform.getBounds();
expect(bounds._ne.lat).toBeCloseTo(11.76627084591695, precisionDigits);
expect(bounds._ne.lng).toBeCloseTo(16.124697669965144, precisionDigits);
expect(bounds._sw.lat).toBeCloseTo(-11.76627084591695, precisionDigits);
expect(bounds._sw.lng).toBeCloseTo(-16.124697669965144, precisionDigits);
});
test('looking at south pole', () => {
globeTransform.setCenter(new LngLat(0, -84));
globeTransform.setZoom(-2);
const bounds = globeTransform.getBounds();
expect(bounds._ne.lat).toBeCloseTo(-6.299534770946991, precisionDigits);
expect(bounds._ne.lng).toBeCloseTo(180, precisionDigits);
expect(bounds._sw.lat).toBeCloseTo(-90, precisionDigits);
expect(bounds._sw.lng).toBeCloseTo(-180, precisionDigits);
});
test('looking at south edge of mercator', () => {
globeTransform.setCenter(new LngLat(-163, -83));
globeTransform.setZoom(3);
const bounds = globeTransform.getBounds();
expect(bounds._ne.lat).toBeCloseTo(-79.75570418234764, precisionDigits);
expect(bounds._ne.lng).toBeCloseTo(-124.19771985801174, precisionDigits);
expect(bounds._sw.lat).toBeCloseTo(-85.59109073899032, precisionDigits);
expect(bounds._sw.lng).toBeCloseTo(-201.80228014198985, precisionDigits);
});
});
describe('projectTileCoordinates', () => {
const precisionDigits = 10;
const transform = new GlobeTransform();
transform.resize(512, 512);
transform.setCenter(new LngLat(10.0, 50.0));
transform.setZoom(-1);
test('basic', () => {
const projection = transform.projectTileCoordinates(1024, 1024, new UnwrappedTileID(0, new CanonicalTileID(1, 1, 0)), (_x, _y) => 0);
expect(projection.point.x).toBeCloseTo(0.008635590705360347, precisionDigits);
expect(projection.point.y).toBeCloseTo(0.16970500709841846, precisionDigits);
expect(projection.signedDistanceFromCamera).toBeCloseTo(781.0549201758624, precisionDigits);
expect(projection.isOccluded).toBe(false);
});
test('rotated', () => {
transform.setBearing(12);
transform.setPitch(10);
const projection = transform.projectTileCoordinates(1024, 1024, new UnwrappedTileID(0, new CanonicalTileID(1, 1, 0)), (_x, _y) => 0);
expect(projection.point.x).toBeCloseTo(-0.026585319983152694, precisionDigits);
expect(projection.point.y).toBeCloseTo(0.15506884411121183, precisionDigits);
expect(projection.signedDistanceFromCamera).toBeCloseTo(788.4423931260653, precisionDigits);
expect(projection.isOccluded).toBe(false);
});
test('occluded by planet', () => {
transform.setBearing(-90);
transform.setPitch(60);
const projection = transform.projectTileCoordinates(8192, 8192, new UnwrappedTileID(0, new CanonicalTileID(1, 1, 0)), (_x, _y) => 0);
expect(projection.point.x).toBeCloseTo(0.22428309892086878, precisionDigits);
expect(projection.point.y).toBeCloseTo(-0.4462620847133465, precisionDigits);
expect(projection.signedDistanceFromCamera).toBeCloseTo(822.280942015371, precisionDigits);
expect(projection.isOccluded).toBe(true);
});
});
describe('isLocationOccluded', () => {
const transform = new GlobeTransform();
transform.resize(512, 512);
transform.setCenter(new LngLat(0.0, 0.0));
transform.setZoom(-1);
test('center', () => {
expect(transform.isLocationOccluded(new LngLat(0, 0))).toBe(false);
});
test('center from tile', () => {
expect(transform.isLocationOccluded(tileCoordinatesToLocation(0, 0, new CanonicalTileID(1, 1, 1)))).toBe(false);
});
test('backside', () => {
expect(transform.isLocationOccluded(new LngLat(179.9, 0))).toBe(true);
});
test('backside from tile', () => {
expect(transform.isLocationOccluded(tileCoordinatesToLocation(0, 0, new CanonicalTileID(1, 0, 1)))).toBe(true);
});
test('barely visible', () => {
expect(transform.isLocationOccluded(new LngLat(84.49, 0))).toBe(false);
});
test('barely hidden', () => {
expect(transform.isLocationOccluded(new LngLat(84.50, 0))).toBe(true);
});
});
describe('render world copies', () => {
test('change projection and make sure render world copies is kept', () => {
const globeTransform = createGlobeTransform();
globeTransform.setRenderWorldCopies(true);
expect(globeTransform.renderWorldCopies).toBeTruthy();
});
test('change transform and make sure render world copies is kept', () => {
const globeTransform = createGlobeTransform();
globeTransform.setRenderWorldCopies(true);
const mercator = new MercatorTransform(0, 1, 2, 3, false);
mercator.apply(globeTransform);
expect(mercator.renderWorldCopies).toBeTruthy();
});
});
});