@deck.gl/core
Version:
deck.gl core library
192 lines (170 loc) • 6.14 kB
text/typescript
// deck.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
/**
* Projection utils
* TODO: move to Viewport class?
*/
import {COORDINATE_SYSTEM} from '../../lib/constants';
import {getOffsetOrigin} from './viewport-uniforms';
import WebMercatorViewport from '../../viewports/web-mercator-viewport';
import {vec3, vec4} from '@math.gl/core';
import {addMetersToLngLat} from '@math.gl/web-mercator';
import type {CoordinateSystem} from '../../lib/constants';
import type Viewport from '../../viewports/viewport';
import type {NumericArray} from '../../types/types';
const DEFAULT_COORDINATE_ORIGIN = [0, 0, 0];
// In project.glsl, offset modes calculate z differently from LNG_LAT mode.
// offset modes apply the y adjustment (unitsPerMeter2) when projecting z
// LNG_LAT mode only use the linear scale.
function lngLatZToWorldPosition(
lngLatZ: [number, number, number],
viewport: Viewport,
offsetMode: boolean = false
): [number, number, number] {
const p = viewport.projectPosition(lngLatZ);
// TODO - avoid using instanceof
if (offsetMode && viewport instanceof WebMercatorViewport) {
const [longitude, latitude, z = 0] = lngLatZ;
const distanceScales = viewport.getDistanceScales([longitude, latitude]);
p[2] = z * distanceScales.unitsPerMeter[2];
}
return p;
}
function normalizeParameters(opts: {
viewport: Viewport;
coordinateSystem: CoordinateSystem;
coordinateOrigin: [number, number, number];
modelMatrix?: NumericArray | null;
fromCoordinateSystem?: CoordinateSystem;
fromCoordinateOrigin?: [number, number, number];
}): {
viewport: Viewport;
coordinateSystem: CoordinateSystem;
coordinateOrigin: [number, number, number];
modelMatrix?: NumericArray | null;
fromCoordinateSystem: CoordinateSystem;
fromCoordinateOrigin: [number, number, number];
} {
const {viewport, modelMatrix, coordinateOrigin} = opts;
let {coordinateSystem, fromCoordinateSystem, fromCoordinateOrigin} = opts;
if (coordinateSystem === COORDINATE_SYSTEM.DEFAULT) {
coordinateSystem = viewport.isGeospatial
? COORDINATE_SYSTEM.LNGLAT
: COORDINATE_SYSTEM.CARTESIAN;
}
if (fromCoordinateSystem === undefined) {
fromCoordinateSystem = coordinateSystem;
}
if (fromCoordinateOrigin === undefined) {
fromCoordinateOrigin = coordinateOrigin;
}
return {
viewport,
coordinateSystem,
coordinateOrigin,
modelMatrix,
fromCoordinateSystem,
fromCoordinateOrigin
};
}
/** Get the common space position from world coordinates in the given coordinate system */
export function getWorldPosition(
position: number[],
{
viewport,
modelMatrix,
coordinateSystem,
coordinateOrigin,
offsetMode
}: {
viewport: Viewport;
modelMatrix?: NumericArray | null;
coordinateSystem: CoordinateSystem;
coordinateOrigin: [number, number, number];
offsetMode?: boolean;
}
): [number, number, number] {
let [x, y, z = 0] = position;
if (modelMatrix) {
[] = vec4.transformMat4([], [x, y, z, 1.0], modelMatrix);
}
switch (coordinateSystem) {
case COORDINATE_SYSTEM.LNGLAT:
return lngLatZToWorldPosition([x, y, z], viewport, offsetMode);
case COORDINATE_SYSTEM.LNGLAT_OFFSETS:
return lngLatZToWorldPosition(
[], y + coordinateOrigin[1], z + (coordinateOrigin[2] || 0)],
viewport,
offsetMode
);
case COORDINATE_SYSTEM.METER_OFFSETS:
return lngLatZToWorldPosition(
addMetersToLngLat(coordinateOrigin, [x, y, z]) as [number, number, number],
viewport,
offsetMode
);
case COORDINATE_SYSTEM.CARTESIAN:
default:
return viewport.isGeospatial
? [x + coordinateOrigin[0], y + coordinateOrigin[1], z + coordinateOrigin[2]]
: viewport.projectPosition([x, y, z]);
}
}
/**
* Equivalent to project_position in project.glsl
* projects a user supplied position to world position directly with or without
* a reference coordinate system
*/
export function projectPosition(
position: number[],
params: {
/** The current viewport */
viewport: Viewport;
/** The reference coordinate system used to align world position */
coordinateSystem: CoordinateSystem;
/** The reference coordinate origin used to align world position */
coordinateOrigin: [number, number, number];
/** The model matrix of the supplied position */
modelMatrix?: NumericArray | null;
/** The coordinate system that the supplied position is in. Default to the same as `coordinateSystem`. */
fromCoordinateSystem?: CoordinateSystem;
/** The coordinate origin that the supplied position is in. Default to the same as `coordinateOrigin`. */
fromCoordinateOrigin?: [number, number, number];
/** Whether to apply offset mode automatically as does the project shader module.
* Offset mode places the origin of the common space at the given viewport's center. It is used in some use cases
* to improve precision in the vertex shader due to the fp32 float limitation.
* Use `autoOffset:false` if the returned position should not be dependent on the current viewport.
* Default `true` */
autoOffset?: boolean;
}
): [number, number, number] {
const {
viewport,
coordinateSystem,
coordinateOrigin,
modelMatrix,
fromCoordinateSystem,
fromCoordinateOrigin
} = normalizeParameters(params);
const {autoOffset = true} = params;
const {
geospatialOrigin = DEFAULT_COORDINATE_ORIGIN,
shaderCoordinateOrigin = DEFAULT_COORDINATE_ORIGIN,
offsetMode = false
} = autoOffset ? getOffsetOrigin(viewport, coordinateSystem, coordinateOrigin) : {};
const worldPosition = getWorldPosition(position, {
viewport,
modelMatrix,
coordinateSystem: fromCoordinateSystem,
coordinateOrigin: fromCoordinateOrigin,
offsetMode
});
if (offsetMode) {
const positionCommonSpace = viewport.projectPosition(
geospatialOrigin || shaderCoordinateOrigin
);
vec3.sub(worldPosition, worldPosition, positionCommonSpace);
}
return worldPosition;
}