diff --git a/docs/api-reference/core/globe-viewport.md b/docs/api-reference/core/globe-viewport.md index 69f29e1f4ec..0678bffee3d 100644 --- a/docs/api-reference/core/globe-viewport.md +++ b/docs/api-reference/core/globe-viewport.md @@ -64,7 +64,7 @@ Projects world coordinates to pixel coordinates on screen. Parameters: -* `coordinates` (number[]) - `[longitude, latitude, altitude]`. `altitude` is in meters and default to `0` if not supplied. +* `coordinates` ([number, number, number]) - `[longitude, latitude, altitude]`. `altitude` is in meters and default to `0` if not supplied. * `opts` (object) + `topLeft` (boolean, optional) - Whether projected coords are top left. Default to `true`. diff --git a/docs/upgrade-guide.md b/docs/upgrade-guide.md index a94ff0b7811..78d95fe8031 100644 --- a/docs/upgrade-guide.md +++ b/docs/upgrade-guide.md @@ -32,6 +32,10 @@ Breaking changes: - `PointLight.attenuation` was previously ignored. To retain old behavior, use the default (`[1, 0, 0]`). +### MapViewState / ViewportOptions + +- The `position` property changed from `number[]` to `[number, number, number]` (lng, lat, elevation). + ## Upgrading to v9.0 **Before you upgrade: known issues** diff --git a/modules/aggregation-layers/src/common/utils/bounds-utils.ts b/modules/aggregation-layers/src/common/utils/bounds-utils.ts index 17a117c0ac2..14c3b45b3bf 100644 --- a/modules/aggregation-layers/src/common/utils/bounds-utils.ts +++ b/modules/aggregation-layers/src/common/utils/bounds-utils.ts @@ -2,6 +2,8 @@ // SPDX-License-Identifier: MIT // Copyright (c) vis.gl contributors +import {NumberArray2} from '@math.gl/core'; + /** Utility to estimate binIdRange as expected by AggregatorProps */ export function getBinIdRange({ dataBounds, @@ -9,9 +11,9 @@ export function getBinIdRange({ padding = 0 }: { /** Bounds of the input data */ - dataBounds: [min: number[], max: number[]]; + dataBounds: [min: NumberArray2, max: NumberArray2]; /** Given a data point, returns the bin id that it belongs to */ - getBinId: (p: number[]) => number[]; + getBinId: (p: NumberArray2) => NumberArray2; /** Add a border around the result to avoid clipping */ padding?: number; }): [number, number][] { @@ -20,7 +22,7 @@ export function getBinIdRange({ dataBounds[1], [dataBounds[0][0], dataBounds[1][1]], [dataBounds[1][0], dataBounds[0][1]] - ].map(p => getBinId(p)); + ].map(p => getBinId(p as NumberArray2)); const minX = Math.min(...corners.map(p => p[0])) - padding; const minY = Math.min(...corners.map(p => p[1])) - padding; diff --git a/modules/aggregation-layers/src/contour-layer/contour-layer.ts b/modules/aggregation-layers/src/contour-layer/contour-layer.ts index ded0d2acd82..55aa1bb5572 100644 --- a/modules/aggregation-layers/src/contour-layer/contour-layer.ts +++ b/modules/aggregation-layers/src/contour-layer/contour-layer.ts @@ -15,6 +15,7 @@ import { UpdateParameters, DefaultProps } from '@deck.gl/core'; +import {Matrix4, type NumberArray2, type NumberArray3} from '@math.gl/core'; import {PathLayer, SolidPolygonLayer} from '@deck.gl/layers'; import {WebGLAggregator, CPUAggregator, AggregationOperation} from '../common/aggregator/index'; import AggregationLayer from '../common/aggregation-layer'; @@ -22,7 +23,6 @@ import {AggregationLayerProps} from '../common/aggregation-layer'; import {generateContours, Contour, ContourLine, ContourPolygon} from './contour-utils'; import {getAggregatorValueReader} from './value-reader'; import {getBinIdRange} from '../common/utils/bounds-utils'; -import {Matrix4} from '@math.gl/core'; import {BinOptions, binOptionsUniforms} from './bin-options-uniforms'; const DEFAULT_COLOR = [255, 255, 255, 255]; @@ -140,7 +140,7 @@ export default class GridLayer extends dimensions: 2, getBin: { sources: ['positions'], - getValue: ({positions}: {positions: number[]}, index: number, opts: BinOptions) => { + getValue: ({positions}: {positions: NumberArray3}, index: number, opts: BinOptions) => { const viewport = this.state.aggregatorViewport; // project to common space const p = viewport.projectPosition(positions); @@ -241,7 +241,10 @@ export default class GridLayer extends let viewport = this.context.viewport; if (bounds && Number.isFinite(bounds[0][0])) { - let centroid = [(bounds[0][0] + bounds[1][0]) / 2, (bounds[0][1] + bounds[1][1]) / 2]; + let centroid: NumberArray2 = [ + (bounds[0][0] + bounds[1][0]) / 2, + (bounds[0][1] + bounds[1][1]) / 2 + ]; const {cellSize, gridOrigin} = this.props; const {unitsPerMeter} = viewport.getDistanceScales(centroid); cellSizeCommon[0] = unitsPerMeter[0] * cellSize; @@ -271,8 +274,8 @@ export default class GridLayer extends binIdRange = getBinIdRange({ dataBounds: bounds, - getBinId: (p: number[]) => { - const positionCommon = viewport.projectFlat(p); + getBinId: p => { + const positionCommon = viewport.projectFlat(p as NumberArray2); return [ Math.floor((positionCommon[0] - cellOriginCommon[0]) / cellSizeCommon[0]), Math.floor((positionCommon[1] - cellOriginCommon[1]) / cellSizeCommon[1]) diff --git a/modules/aggregation-layers/src/grid-layer/grid-layer.ts b/modules/aggregation-layers/src/grid-layer/grid-layer.ts index f0b82e8028b..bf08a9140d4 100644 --- a/modules/aggregation-layers/src/grid-layer/grid-layer.ts +++ b/modules/aggregation-layers/src/grid-layer/grid-layer.ts @@ -19,6 +19,7 @@ import { UpdateParameters, DefaultProps } from '@deck.gl/core'; +import type {NumberArray2, NumberArray3} from '@math.gl/core'; import {WebGLAggregator, CPUAggregator, AggregationOperation} from '../common/aggregator/index'; import AggregationLayer from '../common/aggregation-layer'; import {AggregateAccessor} from '../common/types'; @@ -313,7 +314,7 @@ export default class GridLayer extends } const viewport = this.state.aggregatorViewport; // project to common space - const p = viewport.projectPosition(positions); + const p = viewport.projectPosition(positions as NumberArray3); const {cellSizeCommon, cellOriginCommon} = opts; return [ Math.floor((p[0] - cellOriginCommon[0]) / cellSizeCommon[0]), @@ -446,7 +447,10 @@ export default class GridLayer extends let viewport = this.context.viewport; if (bounds && Number.isFinite(bounds[0][0])) { - let centroid = [(bounds[0][0] + bounds[1][0]) / 2, (bounds[0][1] + bounds[1][1]) / 2]; + let centroid: NumberArray2 = [ + (bounds[0][0] + bounds[1][0]) / 2, + (bounds[0][1] + bounds[1][1]) / 2 + ]; const {cellSize} = this.props; const {unitsPerMeter} = viewport.getDistanceScales(centroid); cellSizeCommon[0] = unitsPerMeter[0] * cellSize; @@ -475,7 +479,7 @@ export default class GridLayer extends binIdRange = getBinIdRange({ dataBounds: bounds, getBinId: (p: number[]) => { - const positionCommon = viewport.projectFlat(p); + const positionCommon = viewport.projectFlat(p as NumberArray2); return [ Math.floor((positionCommon[0] - cellOriginCommon[0]) / cellSizeCommon[0]), Math.floor((positionCommon[1] - cellOriginCommon[1]) / cellSizeCommon[1]) diff --git a/modules/aggregation-layers/src/heatmap-layer/heatmap-layer.ts b/modules/aggregation-layers/src/heatmap-layer/heatmap-layer.ts index a1ad95b8d53..8109b5d1290 100644 --- a/modules/aggregation-layers/src/heatmap-layer/heatmap-layer.ts +++ b/modules/aggregation-layers/src/heatmap-layer/heatmap-layer.ts @@ -28,6 +28,7 @@ import { DefaultProps, project32 } from '@deck.gl/core'; +import type {NumberArray2} from '@math.gl/core'; import TriangleLayer from './triangle-layer'; import AggregationLayer, {AggregationLayerProps} from './aggregation-layer'; import {defaultColorRange, colorRangeToFlatArray} from '../common/utils/color-utils'; @@ -546,7 +547,7 @@ export default class HeatmapLayer< triPositionBuffer!.write(packVertices(viewportCorners, 3)); const textureBounds = viewportCorners.map(p => - getTextureCoordinates(viewport.projectPosition(p), normalizedCommonBounds!) + getTextureCoordinates(viewport.projectPosition(p as NumberArray2), normalizedCommonBounds!) ); triTexCoordBuffer!.write(packVertices(textureBounds, 2)); } diff --git a/modules/aggregation-layers/src/hexagon-layer/hexagon-layer.ts b/modules/aggregation-layers/src/hexagon-layer/hexagon-layer.ts index 0be249804d7..5a3d438f1b1 100644 --- a/modules/aggregation-layers/src/hexagon-layer/hexagon-layer.ts +++ b/modules/aggregation-layers/src/hexagon-layer/hexagon-layer.ts @@ -19,6 +19,7 @@ import { UpdateParameters, DefaultProps } from '@deck.gl/core'; +import type {NumberArray2, NumberArray3} from '@math.gl/core'; import {WebGLAggregator, CPUAggregator, AggregationOperation} from '../common/aggregator/index'; import AggregationLayer from '../common/aggregation-layer'; import {AggregateAccessor} from '../common/types'; @@ -311,7 +312,11 @@ export default class HexagonLayer< dimensions: 2, getBin: { sources: ['positions'], - getValue: ({positions}: {positions: number[]}, index: number, opts: BinOptions) => { + getValue: ( + {positions}: {positions: NumberArray2 | NumberArray3}, + index: number, + opts: BinOptions + ) => { if (hexagonAggregator) { return hexagonAggregator(positions, radius); } @@ -451,7 +456,10 @@ export default class HexagonLayer< let viewport = this.context.viewport; if (bounds && Number.isFinite(bounds[0][0])) { - let centroid = [(bounds[0][0] + bounds[1][0]) / 2, (bounds[0][1] + bounds[1][1]) / 2]; + let centroid: NumberArray2 = [ + (bounds[0][0] + bounds[1][0]) / 2, + (bounds[0][1] + bounds[1][1]) / 2 + ]; const {radius} = this.props; const {unitsPerMeter} = viewport.getDistanceScales(centroid); radiusCommon = unitsPerMeter[0] * radius; @@ -473,7 +481,7 @@ export default class HexagonLayer< binIdRange = getBinIdRange({ dataBounds: bounds, - getBinId: (p: number[]) => { + getBinId: (p: NumberArray2) => { const positionCommon = viewport.projectFlat(p); positionCommon[0] -= hexOriginCommon[0]; positionCommon[1] -= hexOriginCommon[1]; diff --git a/modules/aggregation-layers/src/screen-grid-layer/screen-grid-layer.ts b/modules/aggregation-layers/src/screen-grid-layer/screen-grid-layer.ts index 0be80371606..244e8662afd 100644 --- a/modules/aggregation-layers/src/screen-grid-layer/screen-grid-layer.ts +++ b/modules/aggregation-layers/src/screen-grid-layer/screen-grid-layer.ts @@ -15,6 +15,7 @@ import { UpdateParameters, DefaultProps } from '@deck.gl/core'; +import type {NumberArray3} from '@math.gl/core'; import {WebGLAggregator, CPUAggregator, AggregationOperation} from '../common/aggregator/index'; import AggregationLayer from '../common/aggregation-layer'; import ScreenGridCellLayer from './screen-grid-cell-layer'; @@ -136,7 +137,7 @@ export default class ScreenGridLayer< dimensions: 2, getBin: { sources: ['positions'], - getValue: ({positions}: {positions: number[]}, index: number, opts: BinOptions) => { + getValue: ({positions}: {positions: NumberArray3}, index: number, opts: BinOptions) => { const viewport = this.context.viewport; const p = viewport.project(positions); const cellSizePixels: number = opts.cellSizePixels; diff --git a/modules/core/src/lib/layer.ts b/modules/core/src/lib/layer.ts index 04e16aa177b..92b16aa6e2d 100644 --- a/modules/core/src/lib/layer.ts +++ b/modules/core/src/lib/layer.ts @@ -3,8 +3,10 @@ // Copyright (c) vis.gl contributors /* eslint-disable react/no-direct-mutation-state */ -import {Buffer, Parameters as LumaParameters, TypedArray} from '@luma.gl/core'; +import {Buffer} from '@luma.gl/core'; import {WebGLDevice} from '@luma.gl/webgl'; +import {load} from '@loaders.gl/core'; +import {worldToPixels} from '@math.gl/web-mercator'; import {COORDINATE_SYSTEM} from './constants'; import AttributeManager from './attribute/attribute-manager'; import UniformTransitionManager from './uniform-transition-manager'; @@ -22,14 +24,13 @@ import typedArrayManager from '../utils/typed-array-manager'; import Component from '../lifecycle/component'; import LayerState, {ChangeFlags} from './layer-state'; -import {worldToPixels} from '@math.gl/web-mercator'; - -import {load} from '@loaders.gl/core'; - +import type {Parameters as LumaParameters, TypedArray, RenderPass} from '@luma.gl/core'; +import type {Model} from '@luma.gl/engine'; +import type {PickingProps} from '@luma.gl/shadertools'; import type {Loader} from '@loaders.gl/loader-utils'; +import type {NumberArray2, NumberArray3} from '@math.gl/core'; import type {CoordinateSystem} from './constants'; import type Attribute from './attribute/attribute'; -import type {Model} from '@luma.gl/engine'; import type {PickingInfo, GetPickingInfoParams} from './picking/pick-info'; import type Viewport from '../viewports/viewport'; import type {NumericArray} from '../types/types'; @@ -37,8 +38,6 @@ import type {DefaultProps} from '../lifecycle/prop-types'; import type {LayerData, LayerProps} from '../types/layer-props'; import type {LayerContext} from './layer-manager'; import type {BinaryAttribute} from './attribute/attribute'; -import {RenderPass} from '@luma.gl/core'; -import {PickingProps} from '@luma.gl/shadertools'; const TRACE_CHANGE_FLAG = 'layer.changeFlag'; const TRACE_INITIALIZE = 'layer.initialize'; @@ -219,7 +218,7 @@ export default abstract class Layer extends Component< // Public API for users /** Projects a point with current view state from the current layer's coordinate system to screen */ - project(xyz: number[]): number[] { + project(xyz: NumberArray2 | NumberArray3): NumberArray2 | NumberArray3 { assert(this.internalState); const viewport = this.internalState.viewport || this.context.viewport; @@ -230,20 +229,20 @@ export default abstract class Layer extends Component< coordinateSystem: this.props.coordinateSystem }); const [x, y, z] = worldToPixels(worldPosition, viewport.pixelProjectionMatrix); - return xyz.length === 2 ? [x, y] : [x, y, z]; + return [x, y, z]; } /** Unprojects a screen pixel to the current view's default coordinate system Note: this does not reverse `project`. */ - unproject(xy: number[]): number[] { + unproject(xyz: NumberArray2 | NumberArray3): NumberArray2 | NumberArray3 { assert(this.internalState); const viewport = this.internalState.viewport || this.context.viewport; - return viewport.unproject(xy); + return viewport.unproject(xyz); } /** Projects a point with current view state from the current layer's coordinate system to the world space */ projectPosition( - xyz: number[], + xyz: NumberArray2 | NumberArray3, params?: { /** The viewport to use */ viewport?: Viewport; @@ -442,7 +441,7 @@ export default abstract class Layer extends Component< } // Default implementation - getBounds(): [number[], number[]] | null { + getBounds(): [NumberArray2, NumberArray2] | null { return this.getAttributeManager()?.getBounds(['positions', 'instancePositions']); } diff --git a/modules/core/src/lib/picking/pick-info.ts b/modules/core/src/lib/picking/pick-info.ts index 4a1d36bb379..67be87e57af 100644 --- a/modules/core/src/lib/picking/pick-info.ts +++ b/modules/core/src/lib/picking/pick-info.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: MIT // Copyright (c) vis.gl contributors +import type {NumberArray2, NumberArray3} from '@math.gl/core'; import type Layer from '../layer'; import type Viewport from '../../viewports/viewport'; import type {PickedPixel} from './query-object'; @@ -53,12 +54,9 @@ export function getEmptyPickingInfo({ // Find the viewport that contain the picked pixel pickedViewport = getViewportFromCoordinates(pickInfo?.pickedViewports || viewports, {x, y}); } - let coordinate: number[] | undefined; + let coordinate: NumberArray2 | NumberArray3 | undefined; if (pickedViewport) { - const point = [x - pickedViewport.x, y - pickedViewport.y]; - if (z !== undefined) { - point[2] = z; - } + const point: NumberArray3 = [x - pickedViewport.x, y - pickedViewport.y, z ?? 0]; coordinate = pickedViewport.unproject(point); } diff --git a/modules/core/src/lib/view-manager.ts b/modules/core/src/lib/view-manager.ts index 8d68f61aebd..ca33dd99621 100644 --- a/modules/core/src/lib/view-manager.ts +++ b/modules/core/src/lib/view-manager.ts @@ -6,12 +6,13 @@ import {deepEqual} from '../utils/deep-equal'; import log from '../utils/log'; import {flatten} from '../utils/flatten'; +import type {Timeline} from '@luma.gl/engine'; +import type {NumberArray3} from '@math.gl/core'; +import type {EventManager} from 'mjolnir.js'; import type Controller from '../controllers/controller'; import type {ViewStateChangeParameters, InteractionState} from '../controllers/controller'; import type Viewport from '../viewports/viewport'; import type View from '../views/view'; -import type {Timeline} from '@luma.gl/engine'; -import type {EventManager} from 'mjolnir.js'; import type {ConstructorOf} from '../types/types'; import type {default as MapView, MapViewState} from '../views/map-view'; @@ -20,14 +21,14 @@ type ViewStateOf = ViewT extends View ? ViewStateT : ne type OneOfViews = ViewsT extends null ? MapView : ViewsT extends View[] - ? ViewsT[number] - : ViewsT; + ? ViewsT[number] + : ViewsT; export type AnyViewStateOf = ViewStateOf>; export type ViewStateMap = ViewsT extends null ? MapViewState : ViewsT extends View - ? ViewStateOf - : {[viewId: string]: AnyViewStateOf}; + ? ViewStateOf + : {[viewId: string]: AnyViewStateOf}; /** This is a very lose type of all "acceptable" viewState * It's not good for type hinting but matches what may exist internally @@ -195,13 +196,13 @@ export default class ViewManager { * @param {Object} opts.topLeft=true - Whether origin is top left * @return {Array|null} - [lng, lat, Z] or [X, Y, Z] */ - unproject(xyz: number[], opts?: {topLeft?: boolean}): number[] | null { + unproject(xyz: NumberArray3, opts?: {topLeft?: boolean}): number[] | null { const viewports = this.getViewports(); const pixel = {x: xyz[0], y: xyz[1]}; for (let i = viewports.length - 1; i >= 0; --i) { const viewport = viewports[i]; if (viewport.containsPixel(pixel)) { - const p = xyz.slice(); + const p = xyz.slice() as NumberArray3; p[0] -= viewport.x; p[1] -= viewport.y; return viewport.unproject(p, opts); diff --git a/modules/core/src/shaderlib/project/project-functions.ts b/modules/core/src/shaderlib/project/project-functions.ts index bc66d1498c1..ca722621dac 100644 --- a/modules/core/src/shaderlib/project/project-functions.ts +++ b/modules/core/src/shaderlib/project/project-functions.ts @@ -10,14 +10,14 @@ 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 {type NumberArray2, type NumberArray3, 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]; +const DEFAULT_COORDINATE_ORIGIN: NumberArray3 = [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 @@ -27,7 +27,7 @@ function lngLatZToWorldPosition( viewport: Viewport, offsetMode: boolean = false ): [number, number, number] { - const p = viewport.projectPosition(lngLatZ); + const p = viewport.projectPosition(lngLatZ) as NumberArray3; // TODO - avoid using instanceof if (offsetMode && viewport instanceof WebMercatorViewport) { @@ -81,7 +81,7 @@ function normalizeParameters(opts: { /** Get the common space position from world coordinates in the given coordinate system */ export function getWorldPosition( - position: number[], + position: NumberArray2 | NumberArray3, { viewport, modelMatrix, @@ -124,7 +124,7 @@ export function getWorldPosition( default: return viewport.isGeospatial ? [x + coordinateOrigin[0], y + coordinateOrigin[1], z + coordinateOrigin[2]] - : viewport.projectPosition([x, y, z]); + : (viewport.projectPosition([x, y, z]) as NumberArray3); } } @@ -134,7 +134,7 @@ export function getWorldPosition( * a reference coordinate system */ export function projectPosition( - position: number[], + position: NumberArray2 | NumberArray3, params: { /** The current viewport */ viewport: Viewport; @@ -155,7 +155,7 @@ export function projectPosition( * Default `true` */ autoOffset?: boolean; } -): [number, number, number] { +): NumberArray3 { const { viewport, coordinateSystem, diff --git a/modules/core/src/transitions/linear-interpolator.ts b/modules/core/src/transitions/linear-interpolator.ts index b064b9a743b..579ce6f9273 100644 --- a/modules/core/src/transitions/linear-interpolator.ts +++ b/modules/core/src/transitions/linear-interpolator.ts @@ -2,8 +2,8 @@ // SPDX-License-Identifier: MIT // Copyright (c) vis.gl contributors +import {lerp, type NumberArray3} from '@math.gl/core'; import TransitionInterpolator from './transition-interpolator'; -import {lerp} from '@math.gl/core'; import type Viewport from '../viewports/viewport'; @@ -11,7 +11,7 @@ const DEFAULT_PROPS = ['longitude', 'latitude', 'zoom', 'bearing', 'pitch']; const DEFAULT_REQUIRED_PROPS = ['longitude', 'latitude', 'zoom']; type PropsWithAnchor = { - around?: number[]; + around?: NumberArray3; aroundPosition?: number[]; [key: string]: any; }; @@ -21,7 +21,7 @@ type PropsWithAnchor = { */ export default class LinearInterpolator extends TransitionInterpolator { opts: { - around?: number[]; + around?: NumberArray3; makeViewport?: (props: Record) => Viewport; }; @@ -42,7 +42,7 @@ export default class LinearInterpolator extends TransitionInterpolator { extract?: string[]; required?: string[]; }; - around?: number[]; + around?: NumberArray3; makeViewport?: (props: Record) => Viewport; } = {} ) { @@ -77,7 +77,7 @@ export default class LinearInterpolator extends TransitionInterpolator { if (makeViewport && around) { const startViewport = makeViewport(startProps); const endViewport = makeViewport(endProps); - const aroundPosition = startViewport.unproject(around); + const aroundPosition = startViewport.unproject(around) as NumberArray3; result.start.around = around; Object.assign(result.end, { around: endViewport.project(aroundPosition), diff --git a/modules/core/src/viewports/first-person-viewport.ts b/modules/core/src/viewports/first-person-viewport.ts index 41d709ac2d5..f5931bc7ce5 100644 --- a/modules/core/src/viewports/first-person-viewport.ts +++ b/modules/core/src/viewports/first-person-viewport.ts @@ -2,9 +2,13 @@ // SPDX-License-Identifier: MIT // Copyright (c) vis.gl contributors -import Viewport from '../viewports/viewport'; +import { + Matrix4, + type NumberArray3, + _SphericalCoordinates as SphericalCoordinates +} from '@math.gl/core'; import {getMeterZoom} from '@math.gl/web-mercator'; -import {Matrix4, _SphericalCoordinates as SphericalCoordinates} from '@math.gl/core'; +import Viewport from '../viewports/viewport'; export type FirstPersonViewportOptions = { /** Name of the viewport */ @@ -21,8 +25,8 @@ export type FirstPersonViewportOptions = { longitude?: number; /** Latitude of the camera, in the geospatial case. */ latitude?: number; - /** Meter offsets of the camera from the lng-lat anchor point. Default `[0, 0, 0]`. */ - position?: [number, number, number]; + /** Meter offsets of the camera from the lng-lat-elevation anchor point. Default `[0, 0, 0]`. */ + position?: NumberArray3; /** Bearing (heading) of the camera in degrees. Default `0` (north). */ bearing?: number; /** Pitch (tilt) of the camera in degrees. Default `0` (horizontal). */ diff --git a/modules/core/src/viewports/globe-viewport.ts b/modules/core/src/viewports/globe-viewport.ts index 1d7657dd7e5..d615e175803 100644 --- a/modules/core/src/viewports/globe-viewport.ts +++ b/modules/core/src/viewports/globe-viewport.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: MIT // Copyright (c) vis.gl contributors -import {Matrix4} from '@math.gl/core'; +import {Matrix4, type NumberArray2, type NumberArray3} from '@math.gl/core'; import Viewport from './viewport'; import {PROJECTION_MODE} from '../lib/constants'; import {altitudeToFovy, fovyToAltitude} from '@math.gl/web-mercator'; @@ -47,7 +47,7 @@ export type GlobeViewportOptions = { /** Camera altitude relative to the viewport height, used to control the FOV. Default `1.5` */ altitude?: number; /* Meter offsets of the viewport center from lng, lat */ - position?: number[]; + position?: NumberArray3; /** Zoom level */ zoom?: number; /** Use orthographic projection */ @@ -155,10 +155,10 @@ export default class GlobeViewport extends Viewport { } unproject( - xyz: number[], + xyz: NumberArray2 | NumberArray3, {topLeft = true, targetZ}: {topLeft?: boolean; targetZ?: number} = {} - ): number[] { - const [x, y, z] = xyz; + ): NumberArray2 | NumberArray3 { + const [x, y, z = 0] = xyz; const y2 = topLeft ? y : this.height - y; const {pixelUnprojectionMatrix} = this; @@ -188,12 +188,12 @@ export default class GlobeViewport extends Viewport { const [X, Y, Z] = this.unprojectPosition(coord); if (Number.isFinite(z)) { - return [X, Y, Z]; + return Z ? [X, Y, Z] : [X, Y]; } return Number.isFinite(targetZ) ? [X, Y, targetZ as number] : [X, Y]; } - projectPosition(xyz: number[]): [number, number, number] { + projectPosition(xyz: NumberArray2 | NumberArray3): NumberArray2 | NumberArray3 { const [lng, lat, Z = 0] = xyz; const lambda = lng * DEGREES_TO_RADIANS; const phi = lat * DEGREES_TO_RADIANS; @@ -203,8 +203,8 @@ export default class GlobeViewport extends Viewport { return [Math.sin(lambda) * cosPhi * D, -Math.cos(lambda) * cosPhi * D, Math.sin(phi) * D]; } - unprojectPosition(xyz: number[]): [number, number, number] { - const [x, y, z] = xyz; + unprojectPosition(xyz: NumberArray2 | NumberArray3): NumberArray2 | NumberArray3 { + const [x, y, z = 0] = xyz; const D = vec3.len(xyz); const phi = Math.asin(z / D); const lambda = Math.atan2(x, -y); @@ -215,15 +215,15 @@ export default class GlobeViewport extends Viewport { return [lng, lat, Z]; } - projectFlat(xyz: number[]): [number, number] { - return xyz as [number, number]; + projectFlat(xy: NumberArray2): NumberArray2 { + return xy; } - unprojectFlat(xyz: number[]): [number, number] { - return xyz as [number, number]; + unprojectFlat(xy: NumberArray2): NumberArray2 { + return xy; } - panByPosition(coords: number[], pixel: number[]): GlobeViewportOptions { + panByPosition(coords: number[], pixel: NumberArray2 | NumberArray3): GlobeViewportOptions { const fromPosition = this.unproject(pixel); return { longitude: coords[0] - fromPosition[0] + this.longitude, diff --git a/modules/core/src/viewports/orbit-viewport.ts b/modules/core/src/viewports/orbit-viewport.ts index 333f6afaaf4..a329c99668a 100644 --- a/modules/core/src/viewports/orbit-viewport.ts +++ b/modules/core/src/viewports/orbit-viewport.ts @@ -2,10 +2,9 @@ // SPDX-License-Identifier: MIT // Copyright (c) vis.gl contributors -import Viewport from '../viewports/viewport'; - -import {Matrix4} from '@math.gl/core'; +import {Matrix4, type NumberArray3} from '@math.gl/core'; import {pixelsToWorld, fovyToAltitude} from '@math.gl/web-mercator'; +import Viewport from '../viewports/viewport'; const DEGREES_TO_RADIANS = Math.PI / 180; @@ -136,7 +135,7 @@ export default class OrbitViewport extends Viewport { return [X, Y, Z]; } - panByPosition(coords: number[], pixel: number[]): OrbitViewportOptions { + panByPosition(coords: NumberArray3, pixel: number[]): OrbitViewportOptions { const p0 = this.project(coords); const nextCenter = [ this.width / 2 + p0[0] - pixel[0], diff --git a/modules/core/src/viewports/orthographic-viewport.ts b/modules/core/src/viewports/orthographic-viewport.ts index c97635c339a..7e4d32996c7 100644 --- a/modules/core/src/viewports/orthographic-viewport.ts +++ b/modules/core/src/viewports/orthographic-viewport.ts @@ -2,10 +2,9 @@ // SPDX-License-Identifier: MIT // Copyright (c) vis.gl contributors -import Viewport from '../viewports/viewport'; - -import {Matrix4, clamp, vec2} from '@math.gl/core'; +import {Matrix4, type NumberArray3, clamp, vec2} from '@math.gl/core'; import {pixelsToWorld} from '@math.gl/web-mercator'; +import Viewport from '../viewports/viewport'; import type {Padding} from './viewport'; @@ -60,7 +59,7 @@ export type OrthographicViewportOptions = { /** Viewport height in pixels */ height?: number; /** The world position at the center of the viewport. Default `[0, 0, 0]`. */ - target?: [number, number, number] | [number, number]; + target?: NumberArray3; /** The zoom level of the viewport. `zoom: 0` maps one unit distance to one pixel on screen, and increasing `zoom` by `1` scales the same object to twice as large. * To apply independent zoom levels to the X and Y axes, supply an array `[zoomX, zoomY]`. Default `0`. */ zoom?: number | [number, number]; @@ -121,24 +120,24 @@ export default class OrthographicViewport extends Viewport { }); } - projectFlat([X, Y]: number[]): [number, number] { + projectFlat([X, Y]: [number, number]): [number, number] { const {unitsPerMeter} = this.distanceScales; return [X * unitsPerMeter[0], Y * unitsPerMeter[1]]; } - unprojectFlat([x, y]: number[]): [number, number] { + unprojectFlat([x, y]: [number, number]): [number, number] { const {metersPerUnit} = this.distanceScales; return [x * metersPerUnit[0], y * metersPerUnit[1]]; } /* Needed by LinearInterpolator */ - panByPosition(coords: number[], pixel: number[]): OrthographicViewportOptions { + panByPosition(coords: [number, number], pixel: number[]): OrthographicViewportOptions { const fromLocation = pixelsToWorld(pixel, this.pixelUnprojectionMatrix); const toLocation = this.projectFlat(coords); const translate = vec2.add([], toLocation, vec2.negate([], fromLocation)); const newCenter = vec2.add([], this.center, translate); - - return {target: this.unprojectFlat(newCenter)}; + const target = this.unprojectFlat(newCenter); + return {target: [target[0], target[1], 0]}; } } diff --git a/modules/core/src/viewports/viewport.ts b/modules/core/src/viewports/viewport.ts index 05597627ce3..9d7a1fd514d 100644 --- a/modules/core/src/viewports/viewport.ts +++ b/modules/core/src/viewports/viewport.ts @@ -2,11 +2,15 @@ // SPDX-License-Identifier: MIT // Copyright (c) vis.gl contributors -import log from '../utils/log'; -import {createMat4, getCameraPosition, getFrustumPlanes, FrustumPlane} from '../utils/math-utils'; - -import {Matrix4, Vector3, equals, clamp, mat4} from '@math.gl/core'; - +import { + Matrix4, + Vector3, + equals, + clamp, + mat4, + type NumberArray2, + type NumberArray3 +} from '@math.gl/core'; import { getDistanceScales, getMeterZoom, @@ -15,7 +19,8 @@ import { worldToPixels, pixelsToWorld } from '@math.gl/web-mercator'; - +import log from '../utils/log'; +import {createMat4, getCameraPosition, getFrustumPlanes, FrustumPlane} from '../utils/math-utils'; import {PROJECTION_MODE} from '../lib/constants'; export type DistanceScales = { @@ -45,8 +50,8 @@ export type ViewportOptions = { longitude?: number; /** Latitude in degrees (geospatial only) */ latitude?: number; - /** Viewport center in world space. If geospatial, refers to meter offsets from lng, lat */ - position?: number[]; + /** Viewport center in world space. If geospatial, refers to meter offsets from lng, lat, elevation */ + position?: NumberArray3; /** Zoom level */ zoom?: number; /** Padding around the viewport, in pixels. */ @@ -76,7 +81,7 @@ const DEGREES_TO_RADIANS = Math.PI / 180; const IDENTITY = createMat4(); -const ZERO_VECTOR = [0, 0, 0]; +const ZERO_VECTOR: NumberArray3 = [0, 0, 0]; const DEFAULT_DISTANCE_SCALES: DistanceScales = { unitsPerMeter: [1, 1, 1], @@ -138,7 +143,7 @@ export default class Viewport { isGeospatial: boolean; zoom: number; focalDistance: number; - position: number[]; + position: NumberArray3; modelMatrix: number[] | null; /** Derived parameters */ @@ -148,8 +153,8 @@ export default class Viewport { distanceScales: DistanceScales; /** scale factors between world space and common space */ scale!: number; /** scale factor, equals 2^zoom */ - center!: number[]; /** viewport center in common space */ - cameraPosition!: number[]; /** Camera position in common space */ + center!: NumberArray2 | NumberArray3; /** viewport center in common space */ + cameraPosition!: NumberArray2 | NumberArray3; /** Camera position in common space */ projectionMatrix!: number[]; viewMatrix!: number[]; viewMatrixUncentered!: number[]; @@ -234,22 +239,24 @@ export default class Viewport { /** * Projects xyz (possibly latitude and longitude) to pixel coordinates in window * using viewport projection parameters - * - [longitude, latitude] to [x, y] * - [longitude, latitude, Z] => [x, y, z] * Note: By default, returns top-left coordinates for canvas/SVG type render * - * @param {Array} lngLatZ - [lng, lat] or [lng, lat, Z] + * @param {Array} xyz - [lng, lat, Z] * @param {Object} opts - options * @param {Object} opts.topLeft=true - Whether projected coords are top left * @return {Array} - [x, y] or [x, y, z] in top left coords */ - project(xyz: number[], {topLeft = true}: {topLeft?: boolean} = {}): number[] { + project( + xyz: NumberArray2 | NumberArray3, + {topLeft = true}: {topLeft?: boolean} = {} + ): NumberArray2 | NumberArray3 { const worldPosition = this.projectPosition(xyz); const coord = worldToPixels(worldPosition, this.pixelProjectionMatrix); const [x, y] = coord; const y2 = topLeft ? y : this.height - y; - return xyz.length === 2 ? [x, y2] : [x, y2, coord[2]]; + return [x, y2, coord[2] ?? 0]; } /** @@ -263,33 +270,37 @@ export default class Viewport { * @return {Array|null} - [lng, lat, Z] or [X, Y, Z] */ unproject( - xyz: number[], + xyz: NumberArray2 | NumberArray3, {topLeft = true, targetZ}: {topLeft?: boolean; targetZ?: number} = {} - ): number[] { - const [x, y, z] = xyz; + ): NumberArray2 | NumberArray3 { + const [x, y, z = 0] = xyz; const y2 = topLeft ? y : this.height - y; const targetZWorld = targetZ && targetZ * this.distanceScales.unitsPerMeter[2]; - const coord = pixelsToWorld([x, y2, z], this.pixelUnprojectionMatrix, targetZWorld); + const coord = pixelsToWorld( + [x, y2, z], + this.pixelUnprojectionMatrix, + targetZWorld + ) as NumberArray3; const [X, Y, Z] = this.unprojectPosition(coord); if (Number.isFinite(z)) { - return [X, Y, Z]; + return Z ? [X, Y, Z] : [X, Y]; } - return Number.isFinite(targetZ) ? [X, Y, targetZ as number] : [X, Y]; + return [X, Y, targetZ as number]; } // NON_LINEAR PROJECTION HOOKS // Used for web meractor projection - projectPosition(xyz: number[]): [number, number, number] { - const [X, Y] = this.projectFlat(xyz); + projectPosition(xyz: NumberArray2 | NumberArray3): NumberArray2 | NumberArray3 { + const [X, Y] = this.projectFlat([xyz[0], xyz[1]]); const Z = (xyz[2] || 0) * this.distanceScales.unitsPerMeter[2]; return [X, Y, Z]; } - unprojectPosition(xyz: number[]): [number, number, number] { - const [X, Y] = this.unprojectFlat(xyz); + unprojectPosition(xyz: NumberArray2 | NumberArray3): NumberArray2 | NumberArray3 { + const [X, Y] = this.unprojectFlat([xyz[0], xyz[1]]); const Z = (xyz[2] || 0) * this.distanceScales.metersPerUnit[2]; return [X, Y, Z]; } @@ -303,16 +314,16 @@ export default class Viewport { * Specifies a point on the sphere to project onto the map. * @return {Array} [x,y] coordinates. */ - projectFlat(xyz: number[]): [number, number] { + projectFlat(xy: NumberArray2): NumberArray2 { if (this.isGeospatial) { // Shader clamps latitude to +-89.9, see /shaderlib/project/project.glsl.js // lngLatToWorld([0, -89.9])[1] = -317.9934163758329 // lngLatToWorld([0, 89.9])[1] = 829.9934163758271 - const result = lngLatToWorld(xyz); + const result = lngLatToWorld(xy); result[1] = clamp(result[1], -318, 830); return result; } - return xyz as [number, number]; + return xy; } /** @@ -323,11 +334,11 @@ export default class Viewport { * Has toArray method if you need a GeoJSON Array. * Per cartographic tradition, lat and lon are specified as degrees. */ - unprojectFlat(xyz: number[]): [number, number] { + unprojectFlat(xy: NumberArray2): NumberArray2 { if (this.isGeospatial) { - return worldToLngLat(xyz); + return worldToLngLat(xy as [number, number]); } - return xyz as [number, number]; + return xy; } /** @@ -431,10 +442,10 @@ export default class Viewport { this.scale = scale; const {position, modelMatrix} = opts; - let meterOffset: number[] = ZERO_VECTOR; + let meterOffset: NumberArray3 = ZERO_VECTOR; if (position) { meterOffset = modelMatrix - ? (new Matrix4(modelMatrix).transformAsVector(position, []) as number[]) + ? (new Matrix4(modelMatrix).transformAsVector(position, []) as NumberArray3) : position; } @@ -445,7 +456,7 @@ export default class Viewport { this.center = new Vector3(meterOffset) // Convert to pixels in current zoom .scale(this.distanceScales.unitsPerMeter) - .add(center); + .toArray() as NumberArray3; } else { this.center = this.projectPosition(meterOffset); } diff --git a/modules/core/src/viewports/web-mercator-viewport.ts b/modules/core/src/viewports/web-mercator-viewport.ts index 55dc1c360f7..2cc8359c0b8 100644 --- a/modules/core/src/viewports/web-mercator-viewport.ts +++ b/modules/core/src/viewports/web-mercator-viewport.ts @@ -4,8 +4,7 @@ // View and Projection Matrix calculations for mapbox-js style // map view properties -import Viewport from './viewport'; - +import {Matrix4, type NumberArray2, type NumberArray3, clamp, vec2} from '@math.gl/core'; import { pixelsToWorld, getViewMatrix, @@ -17,9 +16,7 @@ import { fitBounds, getBounds } from '@math.gl/web-mercator'; -import {Padding} from './viewport'; - -import {Matrix4, clamp, vec2} from '@math.gl/core'; +import Viewport, {Padding} from './viewport'; export type WebMercatorViewportOptions = { /** Name of the viewport */ @@ -44,8 +41,8 @@ export type WebMercatorViewportOptions = { altitude?: number; /** Camera fovy in degrees. If provided, overrides `altitude` */ fovy?: number; - /** Viewport center in world space. If geospatial, refers to meter offsets from lng, lat */ - position?: number[]; + /** Viewport center in world space. If geospatial, refers to meter offsets from lng, lat, elevation */ + position?: NumberArray3; /** Zoom level */ zoom?: number; /** Padding around the viewport, in pixels. */ @@ -236,22 +233,22 @@ export default class WebMercatorViewport extends Viewport { return this._subViewports; } - projectPosition(xyz: number[]): [number, number, number] { + projectPosition(xyz: NumberArray2 | NumberArray3): NumberArray2 | NumberArray3 { if (this._pseudoMeters) { // Backward compatibility return super.projectPosition(xyz); } - const [X, Y] = this.projectFlat(xyz); + const [X, Y] = this.projectFlat([xyz[0], xyz[1]]); const Z = (xyz[2] || 0) * unitsPerMeter(xyz[1]); return [X, Y, Z]; } - unprojectPosition(xyz: number[]): [number, number, number] { + unprojectPosition(xyz: NumberArray2 | NumberArray3): NumberArray2 | NumberArray3 { if (this._pseudoMeters) { // Backward compatibility return super.unprojectPosition(xyz); } - const [X, Y] = this.unprojectFlat(xyz); + const [X, Y] = this.unprojectFlat([xyz[0], xyz[1]]); const Z = (xyz[2] || 0) / unitsPerMeter(Y); return [X, Y, Z]; } @@ -270,7 +267,7 @@ export default class WebMercatorViewport extends Viewport { return addMetersToLngLat(lngLatZ, xyz); } - panByPosition(coords: number[], pixel: number[]): WebMercatorViewportOptions { + panByPosition(coords: NumberArray2, pixel: number[]): WebMercatorViewportOptions { const fromLocation = pixelsToWorld(pixel, this.pixelUnprojectionMatrix); const toLocation = this.projectFlat(coords); diff --git a/modules/core/src/views/map-view.ts b/modules/core/src/views/map-view.ts index 4d80ef73cc3..4cc5c672bb3 100644 --- a/modules/core/src/views/map-view.ts +++ b/modules/core/src/views/map-view.ts @@ -6,6 +6,7 @@ import View, {CommonViewState, CommonViewProps} from './view'; import WebMercatorViewport from '../viewports/web-mercator-viewport'; import MapController from '../controllers/map-controller'; +import type {NumberArray3} from '@math.gl/core'; import type {NumericArray} from '../types/types'; export type MapViewState = { @@ -27,8 +28,8 @@ export type MapViewState = { minPitch?: number; /** Max pitch, default `60` */ maxPitch?: number; - /** Viewport center offsets from lng, lat in meters */ - position?: number[]; + /** Viewport center offsets from lng, lat, and elevation in meters */ + position?: NumberArray3; } & CommonViewState; export type MapViewProps = { diff --git a/modules/extensions/src/brushing/shader-module.ts b/modules/extensions/src/brushing/shader-module.ts index d2a487090e8..e74a0a8ff9c 100644 --- a/modules/extensions/src/brushing/shader-module.ts +++ b/modules/extensions/src/brushing/shader-module.ts @@ -6,7 +6,7 @@ import type {ShaderModule} from '@luma.gl/shadertools'; import {project} from '@deck.gl/core'; import type {Viewport} from '@deck.gl/core'; - +import type {NumberArray2} from '@math.gl/core'; import type {BrushingExtensionProps} from './brushing-extension'; export type BrushingModuleProps = { @@ -129,10 +129,9 @@ export default { radius: brushingRadius, target: TARGET[brushingTarget] || 0, mousePos: mousePosition - ? (viewport.unproject([mousePosition.x - viewport.x, mousePosition.y - viewport.y]) as [ - number, - number - ]) + ? (viewport + .unproject([mousePosition.x - viewport.x, mousePosition.y - viewport.y, 0]) + .slice(0, 2) as NumberArray2) : [0, 0] }; }, diff --git a/modules/extensions/src/terrain/terrain-cover.ts b/modules/extensions/src/terrain/terrain-cover.ts index cb39fedfb28..9f5feb69747 100644 --- a/modules/extensions/src/terrain/terrain-cover.ts +++ b/modules/extensions/src/terrain/terrain-cover.ts @@ -3,14 +3,14 @@ // Copyright (c) vis.gl contributors import {Framebuffer} from '@luma.gl/core'; - import type {Layer, Viewport} from '@deck.gl/core'; +import type {NumberArray2, NumberArray3} from '@math.gl/core'; import {createRenderTarget} from './utils'; import {joinLayerBounds, makeViewport, getRenderBounds, Bounds} from '../utils/projection-utils'; type TileHeader = { - boundingBox: [min: number[], max: number[]]; + boundingBox: [min: NumberArray2 | NumberArray3, max: NumberArray2 | NumberArray3]; }; /** @@ -34,7 +34,7 @@ export class TerrainCover { private layers: string[] = []; private tile: TileHeader | null; /** Cached version of targetLayer.getBounds() */ - private targetBounds: [number[], number[]] | null = null; + private targetBounds: [NumberArray2 | NumberArray3, NumberArray2 | NumberArray3] | null = null; /** targetBounds in cartesian space */ private targetBoundsCommon: Bounds | null = null; diff --git a/modules/extensions/src/utils/projection-utils.ts b/modules/extensions/src/utils/projection-utils.ts index 3d8b491ddb5..6092107eab4 100644 --- a/modules/extensions/src/utils/projection-utils.ts +++ b/modules/extensions/src/utils/projection-utils.ts @@ -4,6 +4,7 @@ import {WebMercatorViewport, OrthographicViewport} from '@deck.gl/core'; import type {Layer, Viewport} from '@deck.gl/core'; +import type {NumberArray2, NumberArray3} from '@math.gl/core'; /** Bounds in CARTESIAN coordinates */ export type Bounds = [minX: number, minY: number, maxX: number, maxY: number]; @@ -67,7 +68,7 @@ export function makeViewport(opts: { (bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2, 0 - ]); + ]) as NumberArray3; let {width, height, zoom} = opts; if (zoom === undefined) { @@ -135,8 +136,10 @@ export function getViewportBounds(viewport: Viewport, zRange?: [number, number]) } // Viewport bounds in cartesian coordinates - const viewportBottomLeftCommon = viewport.projectPosition(viewportBoundsWorld.slice(0, 2)); - const viewportTopRightCommon = viewport.projectPosition(viewportBoundsWorld.slice(2, 4)); + const bottomLeft = viewportBoundsWorld.slice(0, 2) as NumberArray2; + const topRight = viewportBoundsWorld.slice(2, 4) as NumberArray2; + const viewportBottomLeftCommon = viewport.projectPosition(bottomLeft); + const viewportTopRightCommon = viewport.projectPosition(topRight); return [ viewportBottomLeftCommon[0], viewportBottomLeftCommon[1], diff --git a/modules/geo-layers/src/h3-layers/h3-utils.ts b/modules/geo-layers/src/h3-layers/h3-utils.ts index 0ffa9fa524d..9188204840f 100644 --- a/modules/geo-layers/src/h3-layers/h3-utils.ts +++ b/modules/geo-layers/src/h3-layers/h3-utils.ts @@ -3,7 +3,7 @@ // Copyright (c) vis.gl contributors import {CoordPair, H3IndexInput, cellToBoundary, cellToLatLng} from 'h3-js'; -import {lerp} from '@math.gl/core'; +import {lerp, NumberArray2} from '@math.gl/core'; // normalize longitudes w.r.t center (refLng), when not provided first vertex export function normalizeLongitudes(vertices: CoordPair[], refLng?: number): void { @@ -42,7 +42,7 @@ export function getHexagonCentroid(getHexagon, object, objectInfo) { return [lng, lat]; } -export function h3ToPolygon(hexId: H3IndexInput, coverage: number = 1): number[][] { +export function h3ToPolygon(hexId: H3IndexInput, coverage: number = 1): NumberArray2[] { const vertices = cellToBoundary(hexId, true); if (coverage !== 1) { diff --git a/modules/geo-layers/src/tile-3d-layer/tile-3d-layer.ts b/modules/geo-layers/src/tile-3d-layer/tile-3d-layer.ts index 1710e20e09e..6c3c5d37050 100644 --- a/modules/geo-layers/src/tile-3d-layer/tile-3d-layer.ts +++ b/modules/geo-layers/src/tile-3d-layer/tile-3d-layer.ts @@ -241,7 +241,6 @@ export default class Tile3DLayer exten return; } - // eslint-disable-next-line @typescript-eslint/no-floating-promises tileset3d.selectTiles(Object.values(viewports)).then(frameNumber => { const tilesetChanged = this.state.frameNumber !== frameNumber; if (tilesetChanged) { diff --git a/modules/geo-layers/src/tileset-2d/tile-2d-traversal.ts b/modules/geo-layers/src/tileset-2d/tile-2d-traversal.ts index ab3dba4ef55..609ca0e6979 100644 --- a/modules/geo-layers/src/tileset-2d/tile-2d-traversal.ts +++ b/modules/geo-layers/src/tileset-2d/tile-2d-traversal.ts @@ -13,6 +13,7 @@ import { import {lngLatToWorld} from '@math.gl/web-mercator'; import {Bounds, TileIndex, ZRange} from './types'; import {osmTile2lngLat} from './utils'; +import type {NumberArray2, NumberArray3} from '@math.gl/core'; const TILE_SIZE = 512; // number of world copies to check @@ -70,7 +71,7 @@ class OSMNode { // eslint-disable-next-line complexity update(params: { viewport: Viewport; - project: ((xyz: number[]) => number[]) | null; + project: ((xyz: NumberArray2 | NumberArray3) => NumberArray2 | NumberArray3) | null; cullingVolume: CullingVolume; elevationBounds: ZRange; minZ: number; @@ -144,7 +145,7 @@ class OSMNode { getBoundingVolume( zRange: ZRange, worldOffset: number, - project: ((xyz: number[]) => number[]) | null + project: ((xyz: NumberArray2 | NumberArray3) => NumberArray2 | NumberArray3) | null ) { if (project) { // Custom projection @@ -155,7 +156,7 @@ class OSMNode { // Convert from tile-relative coordinates to common space const refPointPositions: number[][] = []; for (const p of refPoints) { - const lngLat: number[] = osmTile2lngLat(this.x + p[0], this.y + p[1], this.z); + const lngLat: NumberArray3 = osmTile2lngLat(this.x + p[0], this.y + p[1], this.z); lngLat[2] = zRange[0]; refPointPositions.push(project(lngLat)); @@ -190,7 +191,7 @@ export function getOSMTileIndices( zRange: ZRange | null, bounds?: Bounds ): TileIndex[] { - const project: ((xyz: number[]) => number[]) | null = + const project: ((xyz: NumberArray2 | NumberArray3) => NumberArray2 | NumberArray3) | null = viewport instanceof _GlobeViewport && viewport.resolution ? // eslint-disable-next-line @typescript-eslint/unbound-method viewport.projectPosition diff --git a/modules/geo-layers/src/tileset-2d/utils.ts b/modules/geo-layers/src/tileset-2d/utils.ts index 4de85da8edd..56ffd2aedb2 100644 --- a/modules/geo-layers/src/tileset-2d/utils.ts +++ b/modules/geo-layers/src/tileset-2d/utils.ts @@ -3,7 +3,7 @@ // Copyright (c) vis.gl contributors import {Viewport} from '@deck.gl/core'; -import {Matrix4} from '@math.gl/core'; +import {Matrix4, type NumberArray3} from '@math.gl/core'; import {getOSMTileIndices} from './tile-2d-traversal'; import {Bounds, GeoBoundingBox, TileBoundingBox, TileIndex, ZRange} from './types'; @@ -204,12 +204,12 @@ function getScale(z: number, tileSize: number): number { } // https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Lon..2Flat._to_tile_numbers_2 -export function osmTile2lngLat(x: number, y: number, z: number): [number, number] { +export function osmTile2lngLat(x: number, y: number, z: number): NumberArray3 { const scale = getScale(z, TILE_SIZE); const lng = (x / scale) * 360 - 180; const n = Math.PI - (2 * Math.PI * y) / scale; const lat = (180 / Math.PI) * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))); - return [lng, lat]; + return [lng, lat, 0]; } function tile2XY(x: number, y: number, z: number, tileSize: number): [number, number] { diff --git a/modules/layers/src/path-layer/path-layer.ts b/modules/layers/src/path-layer/path-layer.ts index fe8d61c7677..0894e902541 100644 --- a/modules/layers/src/path-layer/path-layer.ts +++ b/modules/layers/src/path-layer/path-layer.ts @@ -24,6 +24,7 @@ import type { DefaultProps } from '@deck.gl/core'; import type {PathGeometry} from './path'; +import {NumberArray2} from '@math.gl/core'; type _PathLayerProps = { data: LayerDataSource; @@ -142,7 +143,7 @@ export default class PathLayer extends return false; } - getBounds(): [number[], number[]] | null { + getBounds(): [NumberArray2, NumberArray2] | null { return this.getAttributeManager()?.getBounds(['vertexPositions']); } diff --git a/modules/layers/src/solid-polygon-layer/solid-polygon-layer.ts b/modules/layers/src/solid-polygon-layer/solid-polygon-layer.ts index 675a90c6383..85d5f9caf2b 100644 --- a/modules/layers/src/solid-polygon-layer/solid-polygon-layer.ts +++ b/modules/layers/src/solid-polygon-layer/solid-polygon-layer.ts @@ -26,6 +26,7 @@ import type { PickingInfo, DefaultProps } from '@deck.gl/core'; +import type {NumberArray2, NumberArray3} from '@math.gl/core'; import type {PolygonGeometry} from './polygon'; type _SolidPolygonLayerProps = { @@ -148,7 +149,7 @@ export default class SolidPolygonLayer return false; } - getBounds(): [number[], number[]] | null { + getBounds(): [NumberArray2, NumberArray2] | null { return this.getAttributeManager()?.getBounds(['vertexPositions']); } @@ -160,13 +161,16 @@ export default class SolidPolygonLayer coordinateSystem = COORDINATE_SYSTEM.LNGLAT; } - let preproject: ((xy: number[]) => number[]) | undefined; + let preproject: ((xyz: NumberArray3) => NumberArray2 | NumberArray3) | undefined; if (coordinateSystem === COORDINATE_SYSTEM.LNGLAT) { if (_full3d) { preproject = viewport.projectPosition.bind(viewport); } else { - preproject = viewport.projectFlat.bind(viewport); + preproject = (xyz: NumberArray3) => { + const [x, y, z = 0] = viewport.projectFlat([xyz[0], xyz[1]]); + return [x, y, z]; + }; } } diff --git a/modules/mesh-layers/src/simple-mesh-layer/simple-mesh-layer.ts b/modules/mesh-layers/src/simple-mesh-layer/simple-mesh-layer.ts index 9fc1b15ca13..ccf23a92aa9 100644 --- a/modules/mesh-layers/src/simple-mesh-layer/simple-mesh-layer.ts +++ b/modules/mesh-layers/src/simple-mesh-layer/simple-mesh-layer.ts @@ -30,6 +30,7 @@ import type { import type {MeshAttribute, MeshAttributes} from '@loaders.gl/schema'; import type {Geometry as GeometryType} from '@luma.gl/engine'; import {getMeshBoundingBox} from '@loaders.gl/schema'; +import {NumberArray2} from '@math.gl/core'; function normalizeGeometryAttributes(attributes: MeshAttributes): MeshAttributes { const positionAttribute = attributes.positions || attributes.POSITION; @@ -198,7 +199,7 @@ export default class SimpleMeshLayer e model?: Model; emptyTexture: Texture; hasNormals?: boolean; - positionBounds?: [number[], number[]] | null; + positionBounds?: [NumberArray2, NumberArray2] | null; }; getShaders() { @@ -209,7 +210,7 @@ export default class SimpleMeshLayer e }); } - getBounds(): [number[], number[]] | null { + getBounds(): [NumberArray2, NumberArray2] | null { if (this.props._instanced) { return super.getBounds(); } @@ -234,7 +235,7 @@ export default class SimpleMeshLayer e } this.state.positionBounds = result; - return result; + return result ?? null; } initializeState() { diff --git a/test/modules/geo-layers/tile-3d-layer/tile-3d-layer.spec.ts b/test/modules/geo-layers/tile-3d-layer/tile-3d-layer.spec.ts index 2d1f1c100b7..ad595c53ae3 100644 --- a/test/modules/geo-layers/tile-3d-layer/tile-3d-layer.spec.ts +++ b/test/modules/geo-layers/tile-3d-layer/tile-3d-layer.spec.ts @@ -4,13 +4,14 @@ import test from 'tape-promise/tape'; -import {testLayerAsync} from '@deck.gl/test-utils'; +import {LayerTestCase, testLayerAsync} from '@deck.gl/test-utils'; import {Tile3DLayer} from '@deck.gl/geo-layers'; import {WebMercatorViewport} from '@deck.gl/core'; test('Tile3DLayer', async t => { - const testCases = [ + const testCases: LayerTestCase[] = [ { + title: 'Tile3DLayer initial load', props: { data: './test/data/3d-tiles/tileset.json', getPointColor: [0, 0, 0] @@ -23,6 +24,7 @@ test('Tile3DLayer', async t => { } }, { + title: 'Tile3DLayer update opacity', updateProps: { opacity: 0.5 },