UNPKG

@deck.gl/core

Version:

deck.gl core library

192 lines (170 loc) 6.14 kB
// 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) { [x, y, z] = 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( [x + coordinateOrigin[0], 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; }