Skip to content

Commit ce528a4

Browse files
feat(core): layer coordinateSystem supports arbitrary CRS (#10874)
1 parent 6bebbea commit ce528a4

174 files changed

Lines changed: 2760 additions & 1099 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎docs/api-reference/aggregation-layers/hexagon-layer.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,7 @@ This callback will be called when hexagon elevation domain has been calculated.
517517

518518
The [PickingInfo.object](../../developer-guide/interactivity.md#the-pickinginfo-object) field returned by hover/click events of this layer represents an aggregated hexagon. The object contains the following fields:
519519

520+
- `position` (number[] | null) - Center of the picked hexagon in the source CRS, or `null` when inverse projection is unavailable.
520521
- `col` (number) - Column index of the picked hexagon.
521522
- `row` (number) - Row index of the picked hexagon.
522523
- `colorValue` (number) - Aggregated color value, as determined by `getColorWeight` and `colorAggregation`

‎docs/api-reference/core/custom-projection-view.md‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {Deck, _CustomProjectionView as CustomProjectionView} from '@deck.gl/core
1212
new Deck({
1313
views: new CustomProjectionView({
1414
crs: '+proj=eqearth +lon_0=0 +datum=WGS84 +units=m',
15-
resolution: 5,
15+
resolution: 72,
1616
controller: true
1717
}),
1818
initialViewState: {center: [0, 0, 0], zoom: 1}
@@ -28,7 +28,7 @@ Inherits [View options](./view.md#constructor), including layout, padding and co
2828
| Option | Default | Description |
2929
| --- | --- | --- |
3030
| `crs` | Required | A supported planar CRS definition: an identifier such as `EPSG:3857`, a PROJ string, WKT, PROJJSON object, or math.gl spatial-reference object. Linear axis units are read from the descriptor. |
31-
| `resolution` | `0` | Maximum subdivision cell size in WGS84 degrees for paths, polygons and bitmaps. Smaller positive values smooth projected curves at higher processing cost. Zero disables subdivision. |
31+
| `resolution` | `0` | Subdivision intervals around the earth for paths, polygons and bitmaps. The step is `EarthCircumference / resolution`, converted to source CRS units. Higher values generate more detailed geometry. Zero disables subdivision. |
3232
| `orthographic` | `false` | Use an orthographic camera instead of perspective. |
3333

3434
The descriptor supplies conversion functions, WGS84 normalization, target-coordinate bounds and ground distance scales. For example, Web Mercator normalizes longitude and clamps latitude to its supported range. Bounds describe the size-sampling rectangle; they do not clip geometry.
@@ -73,10 +73,10 @@ When displaying multiple views with different projections, create a separate lay
7373
## Limitations
7474

7575
- Tiled layers and `WMSLayer` are not supported, including `TileLayer`, `Tile3DLayer`, `MVTLayer`, `TerrainLayer` and layers built on them.
76-
- `BitmapLayer` approximates the projection with a triangle mesh. A positive `resolution` creates an interior grid in WGS84 and projects every grid vertex; smaller values improve the approximation at a higher processing cost. Image coordinates are linearly interpolated within each triangle. If the image is generated in a different coordinate system than WGS84, subdivision cannot correct its pixel-to-coordinate mapping.
76+
- `BitmapLayer` approximates the projection with a triangle mesh. A positive `resolution` creates an interior grid in the source CRS and projects every grid vertex; higher values improve the approximation at a higher processing cost. Image coordinates are linearly interpolated within each triangle. Subdivision cannot correct an image's pixel-to-coordinate mapping when it differs from the declared source CRS.
7777
- Great-circle paths are not supported by `GreatCircleLayer` or `ArcLayer` with `greatCircle: true`. They fall back to ordinary arcs between projected endpoints, as with `greatCircle: false`. To preserve a great-circle path, sample it in geographic coordinates and render the samples with `PathLayer`.
78-
- `LineLayer`, `ArcLayer`, `PathLayer` and `SolidPolygonLayer` ignore `wrapLongitude` when the viewport uses preprojection. Split geometry at the desired longitude boundary before passing it to these layers.
79-
- With `coordinateSystem: 'default'`, layer positions are interpreted as coordinates in WGS84. `modelMatrix` is applied before conversion, and `coordinateOrigin` is ignored. Bypass this behavior with `coordinateSystem: 'cartesian'`. Cartesian positions are in target CRS units in `crs`, and `modelMatrix` and `coordinateOrigin` both apply.
78+
- `LineLayer`, `ArcLayer`, `PathLayer` and `SolidPolygonLayer` ignore `wrapLongitude` when the layer uses CPU preprojection or its source coordinates are not WGS84. Split geometry at the desired longitude boundary before passing it to these layers.
79+
- With `coordinateSystem: 'default'`, layer positions are interpreted as coordinates in WGS84. `modelMatrix` and then `coordinateOrigin` are applied in the source CRS before conversion. Use a CRS identifier or PROJ string to declare another source CRS. Positions whose CRS signature matches the view's descriptor bypass CPU conversion. Legacy `coordinateSystem: 'cartesian'` also bypasses conversion. Cartesian positions are in target CRS units in `crs`, and `modelMatrix` and `coordinateOrigin` both apply.
8080
- Split geometry at projection discontinuities before passing it to the layer. This view does not automatically clip geometry at those boundaries. The converter must return finite coordinates for the geometry you render.
8181
- Picked coordinates are returned as world coordinates in WGS84. They may be unavailable where the descriptor’s inverse converter cannot return a valid position.
8282

‎docs/api-reference/core/custom-projection-viewport.md‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,9 @@ The local scale field samples the descriptor's finite target-coordinate `renderi
4848

4949
Inherits [Viewport](./viewport.md) methods with these coordinate semantics:
5050

51-
### `preproject(position)`
51+
### `getPreprojection(sourceCrs)`
5252

53-
Normalizes WGS84 coordinates and converts them to target XYZ. Independent of camera state.
54-
55-
### `postUnproject(position)`
56-
57-
Delegates target XYZ conversion to `crs.converter.WGS84.to`. Returns `null` if math.gl reports a conversion failure.
53+
Returns `{preproject, unpreproject, packedCrs}` for the resolved source descriptor. When its signature matches the viewport's descriptor, both functions are `null` and attributes remain in target CRS units. Otherwise, conversion goes through WGS84 and the target descriptor's normalization. `unpreproject` reverses the conversion. These functions are independent of camera state and exclude layer transforms; layers retain them in internal state.
5854

5955
### `project(position, options)`
6056

‎docs/api-reference/core/deck.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ Notes:
259259
260260
- `layerFilter` does not override the visibility if the layer is disabled via `visible: false` or `pickable: false` props.
261261
- All the lifecycle methods other than `draw` are still triggered even a if a layer is filtered out using this method.
262+
- To select a layer's initial viewport, accept it in `layerFilter` with `isPicking: false` and `renderPass: 'screen'`. The first matching viewport supplies its initial projection context. The filter runs before initialization, so use layer props rather than initialized state. If none match, initialization uses the shared viewport context.
262263
263264
#### `views` (View | View[]) {#views}
264265

‎docs/api-reference/core/globe-view.md‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,6 @@ const view = new GlobeView({id, ...});
5050

5151
`GlobeView` takes the same parameters as the [View](./view.md) superclass constructor, plus the following:
5252

53-
#### `resolution` (number, optional) {#resolution}
54-
55-
The resolution at which to turn flat features into 3D meshes, in degrees. Smaller numbers will generate more detailed mesh. Default `10`.
5653

5754
#### `nearZMultiplier` (number, optional) {#nearzmultiplier}
5855

‎docs/api-reference/core/layer.md‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -241,23 +241,27 @@ Normally only used when the application wants to work with coordinates that are
241241

242242
* Default: `'default'`
243243

244-
Specifies how layer positions and offsets should be geographically interpreted. One of:
244+
Specifies the CRS of layer positions after `modelMatrix` and `coordinateOrigin` are applied. Built-in coordinate systems include:
245245

246246
- `'default'`
247247
- `'cartesian'`
248248
- `'lnglat'`
249249
- `'meter-offsets'`
250250
- `'lnglat-offsets'`
251251

252-
The default is to interpret positions as latitude and longitude, however it is also possible to interpret positions as meter offsets added to projection center specified by the `coordinateOrigin` prop.
252+
`'lnglat'`, `'WGS84'`, `'EPSG:4326'`, and `'EPSG:4979'` identify WGS84 longitude/latitude/elevation. `'globe'` identifies geocentric meter coordinates on deck.gl's existing sphere; `'EPSG:4978'` identifies WGS84 ellipsoid geocentric coordinates. Other CRS identifiers and PROJ strings are resolved by `@math.gl/projection`. While resolution is pending, the layer waits before initialization or updates; failures are reported through the layer's error handler.
253+
254+
In [multi-view applications](../../developer-guide/views.md), the first viewport allowed by the layer filter determines the layer's CPU coordinate conversion. Use separate layer instances when views require different packed coordinate systems.
255+
256+
`'default'` uses WGS84 in geospatial views and Cartesian coordinates in non-geospatial views. Shader-supported source CRSs retain their attribute coordinates. Other source CRSs are converted on the CPU to WGS84, or to the target CRS of a custom projection.
253257

254258
See the article on [Coordinate Systems](../../developer-guide/coordinate-systems.md) for details.
255259

256260
#### `coordinateOrigin` (number[3], optional) {#coordinateorigin}
257261

258262
* Default: `[0, 0, 0]`
259263

260-
Specifies a reference point for coordinates that are expressed as offsets. Used when the `coordinateSystem` is set to one of the following modes:
264+
For an absolute CRS, this value is added in source CRS units after `modelMatrix`, before projection. For offset modes, it specifies the geographic reference point:
261265

262266
- `'cartesian'` (optional)
263267
- `'meter-offsets'` (required)
@@ -856,7 +860,7 @@ Parameters:
856860
- `modelMatrix` (Matrix4 | null) - defaults to `this.props.modelMatrix`. Pass `null` to skip the layer matrix.
857861
- `coordinateOrigin` (number[3]) - defaults to `this.props.coordinateOrigin`.
858862
- `coordinateSystem` (string) - defaults to `this.props.coordinateSystem`.
859-
- `fromCoordinateOrigin` (number[3]) - defaults to passed `coordinateOrigin`.
863+
- `fromCoordinateOrigin` (number[3]) - defaults to passed `coordinateOrigin` for the same source system, or `[0, 0, 0]` for a different system.
860864
- `fromCoordinateSystem` (string) - defaults to passed `coordinateSystem`.
861865

862866
Returns:

‎docs/api-reference/core/project.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ The JavaScript uniforms are extracted from both contextual and per-layer props.
4040

4141
Uniforms are considered private to each shader module. They may change in between patch releases. Always use documented functions instead of accessing module uniforms directly.
4242

43-
The uniforms of the `project` shader module are prefixed with `project_` in their names.
43+
The `project.coordinateFrame` uniform describes packed attributes as identity, geodetic, projected, or geocentric coordinates. It is distinct from a layer's `coordinateSystem` CRS string. Local meter and degree offsets select their own projection modes. Both numeric enum definitions are internal to the project shader module.
4444

4545
## GLSL Functions
4646

‎docs/api-reference/core/view.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ A relative (e.g. `'50%'`) or absolute extent. Accepts CSS-like expressions that
5252

5353
Padding around the viewport, in the shape of `{left, right, top, bottom}` where each value is either a relative (e.g. `'50%'`) or absolute pixels. These values support the same CSS-style expressions (numbers/percentages/`px` with parentheses and `calc()` addition/subtraction) as `x`, `y`, `width`, and `height`. This can be used to move the "look at"/target/vanishing point away from the center of the viewport rectangle.
5454

55+
#### `resolution` (number, optional) {#resolution}
56+
57+
Divide long straight lines in paths, polygons or meshes into smaller segments so that they closely follow the shape of the target CRS. Higher values produce finer geometry details but requires more processing and memory. Default `0` disables subdivision. See [geometry approximation](./viewport.md#geometry-approximation).
58+
5559
#### `controller` (Function | boolean | object, optional) {#controller}
5660

5761
Options for viewport interactivity.

‎docs/api-reference/core/viewport.md‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,13 @@ Returns:
146146
* `[longitude, latitude, altitude]`
147147

148148

149+
#### `getPreprojection`
150+
151+
Returns conversions between a source CRS and the CRS accepted by the viewport's projection shader.
152+
153+
- `sourceCrs` (`CRSDescriptor | null`) - Resolved source descriptor; `null` denotes Cartesian or meter-offset input.
154+
- Returns `{preproject, unpreproject, packedCrs}`. Both functions are `null` when the shader accepts the source CRS directly. Otherwise, `preproject` converts source coordinates to packed coordinates and `unpreproject` reverses that conversion. `packedCrs` describes the attribute coordinates. These functions do not apply the layer's model matrix or coordinate origin.
155+
149156
#### `getBounds` {#getbounds}
150157

151158
Extracts the axis-aligned bounding box of the current visible area.
@@ -200,6 +207,24 @@ if (outDir) {
200207
* To support pixel project/unproject functions (in addition to the clipspace projection that Camera classes typically manage), the `Viewport` is also aware of the viewport extents.
201208
* In geospatial setups, Viewports can contain geospatial anchors.
202209

210+
211+
### Geometry Approximation
212+
213+
![subdivision](https://raw.githubusercontent.com/visgl/deck.gl-data/refs/heads/master/images/docs/viewport-resolution.png)
214+
215+
<small>
216+
Left to right: same polygons in resolution 0, 10, 60, rendering 487, 501, 591 outline vertices respectively.
217+
Notice the shape of the 49th-parallel.
218+
<br/><i>Natural Earth 1:110 country boundaries of continental Canada and United States. Albers projection, central metidian -96°, standard parallels 29.5° / 45.5°</i>
219+
</small>
220+
221+
222+
When a geometry is displayed in a different CRS from its definition, long segments are still rendered as straight lines, instead of warping along the surface of the new CRS.
223+
224+
Use `resolution` to divide long straight lines into smaller segments to better follow the shape of the target CRS. Subdivision adds vertices before projection so paths, polygon edges and bitmap meshes can follow projected curves more closely. Higher values produce finer detail, with more processing and memory overhead. Subdivision is by default disabled.
225+
226+
The setting is relative to the earth's circumference and works across source CRSs with different units. For example, `resolution: 36` gives a 10-degree step for WGS84 geometry. When implementing a custom layer, use `context.viewport.getSubdivisionStep(sourceCrs)` to retrieve a step in the geometry's source CRS units.
227+
203228
## Source
204229

205230
[modules/core/src/viewports/viewport.ts](https://github.com/visgl/deck.gl/blob/master/modules/core/src/viewports/viewport.ts)

‎docs/api-reference/extensions/fill-style-extension.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ the pattern and the background color styles the area behind it.
231231
The units of the pattern size, one of `'meters'`, `'common'` and `'pixels'`. See [unit system](../../developer-guide/coordinate-systems.md#supported-units). A 24 x 24 pixel pattern at [`getFillPatternScale: 1`](#getfillpatternscale) covers 24 units of the chosen unit. Procedural pattern stroke widths, gaps, and radii use the same units.
232232

233233
+ `'meters'` anchors the pattern to the ground, so it zooms along with the rest of the map.
234-
+ `'common'` sizes the pattern in [common space](../../developer-guide/coordinate-systems.md) units, which is what a non-geospatial view (`COORDINATE_SYSTEM.CARTESIAN`) wants - meters are converted with a fixed Web Mercator ratio and are not meaningful there.
234+
+ `'common'` sizes the pattern in [common space](../../developer-guide/coordinate-systems.md) units, which is what a non-geospatial view (`'cartesian'`) wants - meters are converted with a fixed Web Mercator ratio and are not meaningful there.
235235
+ `'pixels'` keeps the pattern at a constant size on screen instead. The size is re-anchored at each integer zoom level rather than followed continuously, so that the tiling stays put while zooming within a level; the pattern therefore stays within a factor of `sqrt(2)` of its nominal pixel size.
236236

237237
```js

0 commit comments

Comments
 (0)