@deck.gl/extensions 9.3.7 → 9.4.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Plug-and-play functionalities for deck.gl layers",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
- "version": "9.3.7",
6
+ "version": "9.4.0-alpha.1",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
@@ -43,9 +43,9 @@
43
43
  "@math.gl/core": "^4.1.0"
44
44
  },
45
45
  "peerDependencies": {
46
- "@deck.gl/core": "~9.3.0",
46
+ "@deck.gl/core": "~9.3.0-beta.2",
47
47
  "@luma.gl/core": "~9.3.3",
48
48
  "@luma.gl/engine": "~9.3.3"
49
49
  },
50
- "gitHead": "ac8cd3dd7a70b1f1471729425d63fcff38dc0a13"
50
+ "gitHead": "a30177af4ec82fd612d116d1528deac09f1800b4"
51
51
  }
@@ -3,9 +3,17 @@
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
5
  import {Device, Framebuffer} from '@luma.gl/core';
6
- import {joinLayerBounds, getRenderBounds, makeViewport, Bounds} from '../utils/projection-utils';
6
+ import {
7
+ joinLayerBounds,
8
+ getRenderBounds,
9
+ makeViewport,
10
+ getMercatorReferenceViewport,
11
+ lngLatToMercatorCommon,
12
+ Bounds
13
+ } from '../utils/projection-utils';
7
14
  import {createRenderTarget} from './utils';
8
15
 
16
+ import {_GlobeViewport as GlobeViewport} from '@deck.gl/core';
9
17
  import type {Viewport, Layer} from '@deck.gl/core';
10
18
 
11
19
  const MAP_MAX_SIZE = 2048;
@@ -72,10 +80,10 @@ export class HeightMapBuilder {
72
80
  );
73
81
 
74
82
  if (layersChanged) {
75
- // Recalculate cached bounds
83
+ // Recalculate cached bounds in absolute Mercator common space
76
84
  this.layers = layers;
77
85
  this.layersBounds = layers.map(layer => layer.getBounds());
78
- this.layersBoundsCommon = joinLayerBounds(layers, viewport);
86
+ this.layersBoundsCommon = joinLayerBounds(layers, getMercatorReferenceViewport(viewport));
79
87
  }
80
88
 
81
89
  const viewportChanged = !this.lastViewport || !viewport.equals(this.lastViewport);
@@ -83,7 +91,11 @@ export class HeightMapBuilder {
83
91
  if (!this.layersBoundsCommon) {
84
92
  this.renderViewport = null;
85
93
  } else if (layersChanged || viewportChanged) {
86
- const bounds = getRenderBounds(this.layersBoundsCommon, viewport);
94
+ // On GlobeView, viewport bounds are sphere cartesian — skip intersection
95
+ const bounds =
96
+ viewport instanceof GlobeViewport
97
+ ? this.layersBoundsCommon
98
+ : getRenderBounds(this.layersBoundsCommon, viewport);
87
99
  if (bounds[2] <= bounds[0] || bounds[3] <= bounds[1]) {
88
100
  this.renderViewport = null;
89
101
  return false;
@@ -96,6 +108,14 @@ export class HeightMapBuilder {
96
108
  const pixelWidth = (bounds[2] - bounds[0]) * scale;
97
109
  const pixelHeight = (bounds[3] - bounds[1]) * scale;
98
110
 
111
+ // Use Mercator center — viewport.center on GlobeView is sphere cartesian
112
+ const centerMerc = viewport.isGeospatial
113
+ ? lngLatToMercatorCommon([
114
+ (viewport as {longitude?: number}).longitude ?? 0,
115
+ (viewport as {latitude?: number}).latitude ?? 0
116
+ ])
117
+ : [viewport.center[0], viewport.center[1]];
118
+
99
119
  this.renderViewport =
100
120
  pixelWidth > 0 || pixelHeight > 0
101
121
  ? makeViewport({
@@ -103,12 +123,7 @@ export class HeightMapBuilder {
103
123
  // vertices will not use the standard project_to_clipspace in the DRAW_TO_HEIGHT_MAP shader
104
124
  // However the viewport must have the same center and zoom as the screen viewport
105
125
  // So that projection uniforms used for calculating z are the same
106
- bounds: [
107
- viewport.center[0] - 1,
108
- viewport.center[1] - 1,
109
- viewport.center[0] + 1,
110
- viewport.center[1] + 1
111
- ],
126
+ bounds: [centerMerc[0] - 1, centerMerc[1] - 1, centerMerc[0] + 1, centerMerc[1] + 1],
112
127
  zoom: viewport.zoom,
113
128
  width: Math.min(pixelWidth, MAP_MAX_SIZE),
114
129
  height: Math.min(pixelHeight, MAP_MAX_SIZE),
@@ -67,10 +67,21 @@ uniform sampler2D terrain_map;
67
67
  export const terrainModule = {
68
68
  name: 'terrain',
69
69
  dependencies: [project],
70
- // eslint-disable-next-line prefer-template
71
- vs: uniformBlock + /* glsl */ 'out vec3 commonPos;',
72
- // eslint-disable-next-line prefer-template
73
- fs: uniformBlock + /* glsl */ 'in vec3 commonPos;',
70
+ vs: `${uniformBlock}
71
+ out vec3 commonPos;
72
+ // In globe mode, absolute Mercator position for terrain FBO UV lookups
73
+ out vec2 terrainMercPos;
74
+ out float terrainHeight;
75
+
76
+ vec2 terrain_globe_to_mercator(vec3 globePosition) {
77
+ float D = length(globePosition);
78
+ float sinLat = clamp(globePosition.z / D, -0.999998, 0.999998);
79
+ float x = atan(globePosition.x, -globePosition.y);
80
+ float y = atanh(sinLat);
81
+ return (vec2(x, y) + PI) * WORLD_SCALE;
82
+ }
83
+ `,
84
+ fs: `${uniformBlock}in vec2 terrainMercPos;\nin float terrainHeight;`,
74
85
  inject: {
75
86
  'vs:#main-start': /* glsl */ `
76
87
  if (terrain.mode == TERRAIN_MODE_SKIP) {
@@ -80,32 +91,48 @@ if (terrain.mode == TERRAIN_MODE_SKIP) {
80
91
  `,
81
92
  'vs:DECKGL_FILTER_GL_POSITION': /* glsl */ `
82
93
  commonPos = geometry.position.xyz;
94
+ terrainHeight = commonPos.z + project.commonOrigin.z;
95
+ if (project.projectionMode == PROJECTION_MODE_GLOBE) {
96
+ terrainMercPos = terrain_globe_to_mercator(commonPos);
97
+ terrainHeight = length(commonPos) - GLOBE_RADIUS;
98
+ } else {
99
+ terrainMercPos = commonPos.xy;
100
+ }
83
101
  if (terrain.mode == TERRAIN_MODE_WRITE_HEIGHT_MAP) {
84
- vec2 texCoords = (commonPos.xy - terrain.bounds.xy) / terrain.bounds.zw;
102
+ vec2 texCoords = (terrainMercPos - terrain.bounds.xy) / terrain.bounds.zw;
85
103
  position = vec4(texCoords * 2.0 - 1.0, 0.0, 1.0);
86
- commonPos.z += project.commonOrigin.z;
87
104
  }
88
105
  if (terrain.mode == TERRAIN_MODE_USE_HEIGHT_MAP) {
89
106
  vec3 anchor = geometry.worldPosition;
90
107
  anchor.z = 0.0;
91
108
  vec3 anchorCommon = project_position(anchor);
92
- vec2 texCoords = (anchorCommon.xy - terrain.bounds.xy) / terrain.bounds.zw;
109
+ vec2 anchorMercPos = project.projectionMode == PROJECTION_MODE_GLOBE
110
+ ? terrain_globe_to_mercator(anchorCommon)
111
+ : anchorCommon.xy;
112
+ vec2 texCoords = (anchorMercPos - terrain.bounds.xy) / terrain.bounds.zw;
93
113
  if (texCoords.x >= 0.0 && texCoords.y >= 0.0 && texCoords.x <= 1.0 && texCoords.y <= 1.0) {
94
114
  float terrainZ = texture(terrain_map, texCoords).r;
95
- geometry.position.z += terrainZ;
115
+ if (project.projectionMode == PROJECTION_MODE_GLOBE) {
116
+ // Height map is written in Mercator common space (units = TILE_SIZE / EARTH_CIRCUMFERENCE / cos(lat))
117
+ // Convert to globe radial units (units = GLOBE_RADIUS / EARTH_RADIUS)
118
+ terrainZ *= cos(radians(geometry.worldPosition.y)) * PI;
119
+ geometry.position.xyz += normalize(geometry.position.xyz) * terrainZ;
120
+ } else {
121
+ geometry.position.z += terrainZ;
122
+ }
96
123
  position = project_common_position_to_clipspace(geometry.position);
97
124
  }
98
125
  }
99
126
  `,
100
127
  'fs:#main-start': /* glsl */ `
101
128
  if (terrain.mode == TERRAIN_MODE_WRITE_HEIGHT_MAP) {
102
- fragColor = vec4(commonPos.z, 0.0, 0.0, 1.0);
129
+ fragColor = vec4(terrainHeight, 0.0, 0.0, 1.0);
103
130
  return;
104
131
  }
105
132
  `,
106
133
  'fs:DECKGL_FILTER_COLOR': /* glsl */ `
107
134
  if ((terrain.mode == TERRAIN_MODE_USE_COVER) || (terrain.mode == TERRAIN_MODE_USE_COVER_ONLY)) {
108
- vec2 texCoords = (commonPos.xy - terrain.bounds.xy) / terrain.bounds.zw;
135
+ vec2 texCoords = (terrainMercPos - terrain.bounds.xy) / terrain.bounds.zw;
109
136
  vec4 pixel = texture(terrain_map, texCoords);
110
137
  if (terrain.mode == TERRAIN_MODE_USE_COVER_ONLY) {
111
138
  color = pixel;
@@ -173,7 +200,7 @@ if ((terrain.mode == TERRAIN_MODE_USE_COVER) || (terrain.mode == TERRAIN_MODE_US
173
200
  return {
174
201
  mode,
175
202
  terrain_map: sampler,
176
- // Convert bounds to the common space, as [minX, minY, width, height]
203
+ // Convert bounds to common space, as [minX, minY, width, height]
177
204
  bounds: bounds
178
205
  ? [
179
206
  bounds[0] - commonOrigin[0],
@@ -4,10 +4,18 @@
4
4
 
5
5
  import {Framebuffer} from '@luma.gl/core';
6
6
 
7
+ import {_GlobeViewport as GlobeViewport} from '@deck.gl/core';
7
8
  import type {Layer, Viewport} from '@deck.gl/core';
8
9
 
9
10
  import {createRenderTarget} from './utils';
10
- import {joinLayerBounds, makeViewport, getRenderBounds, Bounds} from '../utils/projection-utils';
11
+ import {
12
+ getMercatorReferenceViewport,
13
+ joinLayerBounds,
14
+ lngLatToMercatorCommon,
15
+ makeViewport,
16
+ getRenderBounds,
17
+ Bounds
18
+ } from '../utils/projection-utils';
11
19
 
12
20
  type TileHeader = {
13
21
  boundingBox: [min: number[], max: number[]];
@@ -113,13 +121,14 @@ export class TerrainCover {
113
121
  const targetLayer = this.targetLayer;
114
122
  let shouldRedraw = false;
115
123
 
124
+ // Bounds in absolute Mercator common space (matches terrain cover FBO viewport)
116
125
  if (this.tile && 'boundingBox' in this.tile) {
117
126
  if (!this.targetBounds) {
118
127
  shouldRedraw = true;
119
128
  this.targetBounds = this.tile.boundingBox;
120
129
 
121
- const bottomLeftCommon = viewport.projectPosition(this.targetBounds[0]);
122
- const topRightCommon = viewport.projectPosition(this.targetBounds[1]);
130
+ const bottomLeftCommon = lngLatToMercatorCommon(this.targetBounds[0]);
131
+ const topRightCommon = lngLatToMercatorCommon(this.targetBounds[1]);
123
132
  this.targetBoundsCommon = [
124
133
  bottomLeftCommon[0],
125
134
  bottomLeftCommon[1],
@@ -131,7 +140,10 @@ export class TerrainCover {
131
140
  // console.log('bounds changed', this.bounds, '>>', newBounds);
132
141
  shouldRedraw = true;
133
142
  this.targetBounds = targetLayer.getBounds();
134
- this.targetBoundsCommon = joinLayerBounds([targetLayer], viewport);
143
+ this.targetBoundsCommon = joinLayerBounds(
144
+ [targetLayer],
145
+ getMercatorReferenceViewport(viewport)
146
+ );
135
147
  }
136
148
 
137
149
  if (!this.targetBoundsCommon) {
@@ -146,7 +158,11 @@ export class TerrainCover {
146
158
  } else {
147
159
  const oldZoom = this.renderViewport?.zoom;
148
160
  shouldRedraw = shouldRedraw || newZoom !== oldZoom;
149
- const newBounds = getRenderBounds(this.targetBoundsCommon, viewport);
161
+ // On GlobeView, viewport bounds are sphere cartesian — skip intersection
162
+ const newBounds =
163
+ viewport instanceof GlobeViewport
164
+ ? this.targetBoundsCommon
165
+ : getRenderBounds(this.targetBoundsCommon, viewport);
150
166
  const oldBounds = this.bounds;
151
167
  shouldRedraw = shouldRedraw || !oldBounds || newBounds.some((x, i) => x !== oldBounds[i]);
152
168
  this.bounds = newBounds;
@@ -8,6 +8,26 @@ import type {Layer, Viewport} from '@deck.gl/core';
8
8
  /** Bounds in CARTESIAN coordinates */
9
9
  export type Bounds = [minX: number, minY: number, maxX: number, maxY: number];
10
10
 
11
+ /** Fixed Mercator viewport for projection-independent coordinate conversion */
12
+ const MERCATOR_REFERENCE_VIEWPORT = new WebMercatorViewport({
13
+ width: 1,
14
+ height: 1,
15
+ longitude: 0,
16
+ latitude: 0,
17
+ zoom: 0
18
+ });
19
+
20
+ /** Project lng/lat to absolute Mercator common space. */
21
+ export function lngLatToMercatorCommon(lngLat: number[]): [number, number] {
22
+ const [x, y] = MERCATOR_REFERENCE_VIEWPORT.projectPosition(lngLat);
23
+ return [x, y];
24
+ }
25
+
26
+ /** Returns a Mercator viewport for bounds computation, bypassing GlobeView. */
27
+ export function getMercatorReferenceViewport(viewport: Viewport): Viewport {
28
+ return viewport.isGeospatial ? MERCATOR_REFERENCE_VIEWPORT : viewport;
29
+ }
30
+
11
31
  /*
12
32
  * Compute the union of bounds from multiple layers
13
33
  * Returns bounds in CARTESIAN coordinates
@@ -63,11 +83,14 @@ export function makeViewport(opts: {
63
83
  return null;
64
84
  }
65
85
 
66
- const centerWorld = viewport.unprojectPosition([
67
- (bounds[0] + bounds[2]) / 2,
68
- (bounds[1] + bounds[3]) / 2,
69
- 0
70
- ]);
86
+ // Unproject through Mercator reference (GlobeView would give sphere coords)
87
+ const centerWorld = isGeospatial
88
+ ? MERCATOR_REFERENCE_VIEWPORT.unprojectPosition([
89
+ (bounds[0] + bounds[2]) / 2,
90
+ (bounds[1] + bounds[3]) / 2,
91
+ 0
92
+ ])
93
+ : viewport.unprojectPosition([(bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2, 0]);
71
94
 
72
95
  let {width, height, zoom} = opts;
73
96
  if (zoom === undefined) {