@deck.gl/core 9.3.0-alpha.1 → 9.3.0-alpha.3

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.
Files changed (134) hide show
  1. package/debug.min.js +1 -1
  2. package/dist/controllers/controller.d.ts +5 -4
  3. package/dist/controllers/controller.d.ts.map +1 -1
  4. package/dist/controllers/controller.js +18 -7
  5. package/dist/controllers/controller.js.map +1 -1
  6. package/dist/controllers/first-person-controller.d.ts +3 -2
  7. package/dist/controllers/first-person-controller.d.ts.map +1 -1
  8. package/dist/controllers/first-person-controller.js +13 -5
  9. package/dist/controllers/first-person-controller.js.map +1 -1
  10. package/dist/controllers/globe-controller.d.ts +1 -0
  11. package/dist/controllers/globe-controller.d.ts.map +1 -1
  12. package/dist/controllers/globe-controller.js +66 -5
  13. package/dist/controllers/globe-controller.js.map +1 -1
  14. package/dist/controllers/map-controller.d.ts +7 -18
  15. package/dist/controllers/map-controller.d.ts.map +1 -1
  16. package/dist/controllers/map-controller.js +94 -50
  17. package/dist/controllers/map-controller.js.map +1 -1
  18. package/dist/controllers/orbit-controller.d.ts +12 -4
  19. package/dist/controllers/orbit-controller.d.ts.map +1 -1
  20. package/dist/controllers/orbit-controller.js +118 -10
  21. package/dist/controllers/orbit-controller.js.map +1 -1
  22. package/dist/controllers/orthographic-controller.d.ts +117 -9
  23. package/dist/controllers/orthographic-controller.d.ts.map +1 -1
  24. package/dist/controllers/orthographic-controller.js +302 -37
  25. package/dist/controllers/orthographic-controller.js.map +1 -1
  26. package/dist/controllers/terrain-controller.d.ts +29 -0
  27. package/dist/controllers/terrain-controller.d.ts.map +1 -0
  28. package/dist/controllers/terrain-controller.js +108 -0
  29. package/dist/controllers/terrain-controller.js.map +1 -0
  30. package/dist/controllers/view-state.d.ts +2 -1
  31. package/dist/controllers/view-state.d.ts.map +1 -1
  32. package/dist/controllers/view-state.js +2 -1
  33. package/dist/controllers/view-state.js.map +1 -1
  34. package/dist/debug/loggers.d.ts.map +1 -1
  35. package/dist/debug/loggers.js +1 -4
  36. package/dist/debug/loggers.js.map +1 -1
  37. package/dist/dist.dev.js +3800 -1675
  38. package/dist/effects/lighting/lighting-effect.d.ts +1 -0
  39. package/dist/effects/lighting/lighting-effect.d.ts.map +1 -1
  40. package/dist/effects/lighting/lighting-effect.js +14 -5
  41. package/dist/effects/lighting/lighting-effect.js.map +1 -1
  42. package/dist/index.cjs +775 -123
  43. package/dist/index.cjs.map +4 -4
  44. package/dist/index.d.ts +1 -0
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +1 -0
  47. package/dist/index.js.map +1 -1
  48. package/dist/lib/attribute/attribute-manager.d.ts.map +1 -1
  49. package/dist/lib/attribute/attribute-manager.js +2 -0
  50. package/dist/lib/attribute/attribute-manager.js.map +1 -1
  51. package/dist/lib/deck-picker.d.ts +6 -1
  52. package/dist/lib/deck-picker.d.ts.map +1 -1
  53. package/dist/lib/deck-picker.js +15 -3
  54. package/dist/lib/deck-picker.js.map +1 -1
  55. package/dist/lib/deck-renderer.d.ts +6 -1
  56. package/dist/lib/deck-renderer.d.ts.map +1 -1
  57. package/dist/lib/deck-renderer.js +14 -2
  58. package/dist/lib/deck-renderer.js.map +1 -1
  59. package/dist/lib/deck.d.ts +5 -0
  60. package/dist/lib/deck.d.ts.map +1 -1
  61. package/dist/lib/deck.js +13 -3
  62. package/dist/lib/deck.js.map +1 -1
  63. package/dist/lib/init.js +2 -2
  64. package/dist/lib/layer.d.ts.map +1 -1
  65. package/dist/lib/layer.js +1 -0
  66. package/dist/lib/layer.js.map +1 -1
  67. package/dist/passes/draw-layers-pass.d.ts +2 -0
  68. package/dist/passes/draw-layers-pass.d.ts.map +1 -1
  69. package/dist/passes/draw-layers-pass.js +3 -0
  70. package/dist/passes/draw-layers-pass.js.map +1 -1
  71. package/dist/passes/layers-pass.d.ts +2 -1
  72. package/dist/passes/layers-pass.d.ts.map +1 -1
  73. package/dist/passes/layers-pass.js +3 -0
  74. package/dist/passes/layers-pass.js.map +1 -1
  75. package/dist/passes/pick-layers-pass.d.ts +5 -2
  76. package/dist/passes/pick-layers-pass.d.ts.map +1 -1
  77. package/dist/passes/pick-layers-pass.js +3 -2
  78. package/dist/passes/pick-layers-pass.js.map +1 -1
  79. package/dist/shaderlib/project/project.glsl.d.ts.map +1 -1
  80. package/dist/shaderlib/project/project.glsl.js +3 -0
  81. package/dist/shaderlib/project/project.glsl.js.map +1 -1
  82. package/dist/utils/deep-merge.d.ts +5 -0
  83. package/dist/utils/deep-merge.d.ts.map +1 -0
  84. package/dist/utils/deep-merge.js +31 -0
  85. package/dist/utils/deep-merge.js.map +1 -0
  86. package/dist/utils/math-utils.d.ts +4 -0
  87. package/dist/utils/math-utils.d.ts.map +1 -1
  88. package/dist/utils/math-utils.js +8 -0
  89. package/dist/utils/math-utils.js.map +1 -1
  90. package/dist/viewports/globe-viewport.d.ts +1 -0
  91. package/dist/viewports/globe-viewport.d.ts.map +1 -1
  92. package/dist/viewports/globe-viewport.js +1 -1
  93. package/dist/viewports/globe-viewport.js.map +1 -1
  94. package/dist/viewports/orbit-viewport.d.ts.map +1 -1
  95. package/dist/viewports/orbit-viewport.js +7 -2
  96. package/dist/viewports/orbit-viewport.js.map +1 -1
  97. package/dist/viewports/orthographic-viewport.d.ts +8 -2
  98. package/dist/viewports/orthographic-viewport.d.ts.map +1 -1
  99. package/dist/viewports/orthographic-viewport.js.map +1 -1
  100. package/dist/views/orthographic-view.d.ts +38 -4
  101. package/dist/views/orthographic-view.d.ts.map +1 -1
  102. package/dist/views/orthographic-view.js.map +1 -1
  103. package/dist/views/view.d.ts.map +1 -1
  104. package/dist/views/view.js +2 -8
  105. package/dist/views/view.js.map +1 -1
  106. package/dist.min.js +220 -144
  107. package/package.json +9 -9
  108. package/src/controllers/controller.ts +23 -9
  109. package/src/controllers/first-person-controller.ts +18 -8
  110. package/src/controllers/globe-controller.ts +89 -5
  111. package/src/controllers/map-controller.ts +105 -56
  112. package/src/controllers/orbit-controller.ts +147 -13
  113. package/src/controllers/orthographic-controller.ts +417 -41
  114. package/src/controllers/terrain-controller.ts +146 -0
  115. package/src/controllers/view-state.ts +8 -1
  116. package/src/debug/loggers.ts +1 -5
  117. package/src/effects/lighting/lighting-effect.ts +20 -8
  118. package/src/index.ts +1 -0
  119. package/src/lib/attribute/attribute-manager.ts +1 -0
  120. package/src/lib/deck-picker.ts +18 -4
  121. package/src/lib/deck-renderer.ts +17 -3
  122. package/src/lib/deck.ts +19 -3
  123. package/src/lib/layer.ts +1 -0
  124. package/src/passes/draw-layers-pass.ts +5 -0
  125. package/src/passes/layers-pass.ts +5 -1
  126. package/src/passes/pick-layers-pass.ts +8 -4
  127. package/src/shaderlib/project/project.glsl.ts +3 -0
  128. package/src/utils/deep-merge.ts +33 -0
  129. package/src/utils/math-utils.ts +12 -0
  130. package/src/viewports/globe-viewport.ts +1 -1
  131. package/src/viewports/orbit-viewport.ts +8 -2
  132. package/src/viewports/orthographic-viewport.ts +8 -2
  133. package/src/views/orthographic-view.ts +38 -4
  134. package/src/views/view.ts +2 -8
package/dist/index.cjs CHANGED
@@ -50,6 +50,7 @@ __export(dist_exports, {
50
50
  PointLight: () => PointLight,
51
51
  PostProcessEffect: () => PostProcessEffect,
52
52
  TRANSITION_EVENTS: () => TRANSITION_EVENTS,
53
+ TerrainController: () => TerrainController,
53
54
  Tesselator: () => Tesselator,
54
55
  TransitionInterpolator: () => TransitionInterpolator,
55
56
  UNIT: () => UNIT,
@@ -183,15 +184,12 @@ var getLoggers = (log) => ({
183
184
  },
184
185
  /* Render events */
185
186
  "deckRenderer.renderLayers": (deckRenderer, renderStats, opts) => {
186
- const { pass, redrawReason, stats } = opts;
187
+ const { pass, redrawReason } = opts;
187
188
  for (const status of renderStats) {
188
189
  const { totalCount, visibleCount, compositeCount, pickableCount } = status;
189
190
  const primitiveCount = totalCount - compositeCount;
190
191
  const hiddenCount = primitiveCount - visibleCount;
191
192
  log.log(LOG_LEVEL_DRAW, `RENDER #${deckRenderer.renderCount} ${visibleCount} (of ${totalCount} layers) to ${pass} because ${redrawReason} (${hiddenCount} hidden, ${compositeCount} composite ${pickableCount} pickable)`)();
192
- if (stats) {
193
- stats.get("Redraw Layers").add(visibleCount);
194
- }
195
193
  }
196
194
  }
197
195
  });
@@ -232,7 +230,7 @@ var json_loader_default = {
232
230
 
233
231
  // dist/lib/init.js
234
232
  function checkVersion() {
235
- const version = true ? "9.3.0-alpha.1" : globalThis.DECK_VERSION || "untranspiled source";
233
+ const version = true ? "9.3.0-alpha.3" : globalThis.DECK_VERSION || "untranspiled source";
236
234
  const existingVersion = globalThis.deck && globalThis.deck.VERSION;
237
235
  if (existingVersion && existingVersion !== version) {
238
236
  throw new Error(`deck.gl - multiple versions detected: ${existingVersion} vs ${version}`);
@@ -1160,6 +1158,9 @@ return offset * project.focalDistance;
1160
1158
  float project_size_to_pixel(float meters) {
1161
1159
  return project_size(meters) * project.scale;
1162
1160
  }
1161
+ vec2 project_size_to_pixel(vec2 meters) {
1162
+ return project_size(meters) * project.scale;
1163
+ }
1163
1164
  float project_size_to_pixel(float size, int unit) {
1164
1165
  if (unit == UNIT_METERS) return project_size_to_pixel(size);
1165
1166
  if (unit == UNIT_COMMON) return size * project.scale;
@@ -1641,6 +1642,9 @@ var LayersPass = class extends Pass {
1641
1642
  this._lastRenderIndex = -1;
1642
1643
  }
1643
1644
  render(options) {
1645
+ this._render(options);
1646
+ }
1647
+ _render(options) {
1644
1648
  const canvasContext = this.device.canvasContext;
1645
1649
  const framebuffer = options.target ?? canvasContext.getCurrentFramebuffer();
1646
1650
  const [width, height] = canvasContext.getDrawingBufferSize();
@@ -2097,9 +2101,7 @@ var LightingEffect = class {
2097
2101
  } : {};
2098
2102
  const lightingProps = {
2099
2103
  enabled: true,
2100
- ambientLight: this.ambientLight,
2101
- directionalLights: this.directionalLights.map((directionalLight) => directionalLight.getProjectedLight({ layer })),
2102
- pointLights: this.pointLights.map((pointLight) => pointLight.getProjectedLight({ layer }))
2104
+ lights: this._getLights(layer)
2103
2105
  };
2104
2106
  const materialProps = layer.props.material;
2105
2107
  return {
@@ -2143,6 +2145,19 @@ var LightingEffect = class {
2143
2145
  this.directionalLights.push(new DirectionalLight(DEFAULT_DIRECTIONAL_LIGHT_PROPS[0]), new DirectionalLight(DEFAULT_DIRECTIONAL_LIGHT_PROPS[1]));
2144
2146
  }
2145
2147
  }
2148
+ _getLights(layer) {
2149
+ const lights = [];
2150
+ if (this.ambientLight) {
2151
+ lights.push(this.ambientLight);
2152
+ }
2153
+ for (const pointLight of this.pointLights) {
2154
+ lights.push(pointLight.getProjectedLight({ layer }));
2155
+ }
2156
+ for (const directionalLight of this.directionalLights) {
2157
+ lights.push(directionalLight.getProjectedLight({ layer }));
2158
+ }
2159
+ return lights;
2160
+ }
2146
2161
  };
2147
2162
 
2148
2163
  // dist/utils/typed-array-manager.js
@@ -2231,6 +2246,14 @@ function mod(value, divisor) {
2231
2246
  function getCameraPosition(viewMatrixInverse) {
2232
2247
  return [viewMatrixInverse[12], viewMatrixInverse[13], viewMatrixInverse[14]];
2233
2248
  }
2249
+ function getProjectionParameters(projectionMatrix) {
2250
+ const m22 = projectionMatrix[10];
2251
+ const m23 = projectionMatrix[14];
2252
+ return {
2253
+ near: m23 / (m22 - 1),
2254
+ far: m23 / (m22 + 1)
2255
+ };
2256
+ }
2234
2257
  function getFrustumPlanes(viewProjectionMatrix) {
2235
2258
  return {
2236
2259
  left: getFrustumPlane(viewProjectionMatrix[3] + viewProjectionMatrix[0], viewProjectionMatrix[7] + viewProjectionMatrix[4], viewProjectionMatrix[11] + viewProjectionMatrix[8], viewProjectionMatrix[15] + viewProjectionMatrix[12]),
@@ -3099,7 +3122,8 @@ var PickLayersPass = class extends LayersPass {
3099
3122
  if ("pickingFBO" in props) {
3100
3123
  return this._drawPickingBuffer(props);
3101
3124
  }
3102
- return super.render(props);
3125
+ const stats = super._render(props);
3126
+ return { decodePickingColor: null, stats };
3103
3127
  }
3104
3128
  // Private
3105
3129
  // Draws list of layers and viewports into the picking buffer
@@ -3108,7 +3132,7 @@ var PickLayersPass = class extends LayersPass {
3108
3132
  this.pickZ = pickZ;
3109
3133
  const colorEncoderState = this._resetColorEncoder(pickZ);
3110
3134
  const scissorRect = [x, y, width, height];
3111
- const renderStatus = super.render({
3135
+ const renderStatus = super._render({
3112
3136
  target: pickingFBO,
3113
3137
  layers,
3114
3138
  layerFilter,
@@ -4181,6 +4205,31 @@ function isAddSubSymbol(token) {
4181
4205
  return Boolean(token && token.type === "symbol" && (token.value === "+" || token.value === "-"));
4182
4206
  }
4183
4207
 
4208
+ // dist/utils/deep-merge.js
4209
+ function deepMergeViewState(a, b) {
4210
+ const result = { ...a };
4211
+ for (const key in b) {
4212
+ if (key === "id")
4213
+ continue;
4214
+ if (Array.isArray(result[key]) && Array.isArray(b[key])) {
4215
+ result[key] = mergeNumericArray(result[key], b[key]);
4216
+ } else {
4217
+ result[key] = b[key];
4218
+ }
4219
+ }
4220
+ return result;
4221
+ }
4222
+ function mergeNumericArray(target, source3) {
4223
+ target = target.slice();
4224
+ for (let i = 0; i < source3.length; i++) {
4225
+ const v = source3[i];
4226
+ if (Number.isFinite(v)) {
4227
+ target[i] = v;
4228
+ }
4229
+ }
4230
+ return target;
4231
+ }
4232
+
4184
4233
  // dist/views/view.js
4185
4234
  var View = class {
4186
4235
  constructor(props) {
@@ -4234,13 +4283,7 @@ var View = class {
4234
4283
  if (!this.props.viewState.id) {
4235
4284
  return this.props.viewState;
4236
4285
  }
4237
- const newViewState = { ...viewState };
4238
- for (const key in this.props.viewState) {
4239
- if (key !== "id") {
4240
- newViewState[key] = this.props.viewState[key];
4241
- }
4242
- }
4243
- return newViewState;
4286
+ return deepMergeViewState(viewState, this.props.viewState);
4244
4287
  }
4245
4288
  return viewState;
4246
4289
  }
@@ -4855,6 +4898,7 @@ var Controller = class {
4855
4898
  this.onStateChange = opts.onStateChange || (() => {
4856
4899
  });
4857
4900
  this.makeViewport = opts.makeViewport;
4901
+ this.pickPosition = opts.pickPosition;
4858
4902
  }
4859
4903
  set events(customEvents) {
4860
4904
  this.toggleEvents(this._customEvents, false);
@@ -4959,6 +5003,7 @@ var Controller = class {
4959
5003
  if (props.dragMode) {
4960
5004
  this.dragMode = props.dragMode;
4961
5005
  }
5006
+ const oldProps = this.props;
4962
5007
  this.props = props;
4963
5008
  if (!("transitionInterpolator" in props)) {
4964
5009
  props.transitionInterpolator = this._getTransitionProps().transitionInterpolator;
@@ -4981,6 +5026,15 @@ var Controller = class {
4981
5026
  this.touchZoom = touchZoom;
4982
5027
  this.touchRotate = touchRotate;
4983
5028
  this.keyboard = keyboard;
5029
+ const dimensionChanged = !oldProps || oldProps.height !== props.height || oldProps.width !== props.width || oldProps.maxBounds !== props.maxBounds;
5030
+ if (dimensionChanged && props.maxBounds) {
5031
+ const controllerState = new this.ControllerState({ ...props, makeViewport: this.makeViewport });
5032
+ const normalizedProps = controllerState.getViewportProps();
5033
+ const changed = Object.keys(normalizedProps).some((key) => !deepEqual(normalizedProps[key], props[key], 1));
5034
+ if (changed) {
5035
+ this.updateViewport(controllerState);
5036
+ }
5037
+ }
4984
5038
  }
4985
5039
  updateTransition() {
4986
5040
  this.transitionManager.updateTransition();
@@ -5032,15 +5086,13 @@ var Controller = class {
5032
5086
  if (this.invertPan || this.dragMode === "pan") {
5033
5087
  alternateMode = !alternateMode;
5034
5088
  }
5035
- const newControllerState = alternateMode ? this.controllerState.panStart({ pos }) : this.controllerState.rotateStart(this._getRotateStartParams(pos));
5089
+ const newControllerState = this.controllerState[alternateMode ? "panStart" : "rotateStart"]({
5090
+ pos
5091
+ });
5036
5092
  this._panMove = alternateMode;
5037
5093
  this.updateViewport(newControllerState, NO_TRANSITION_PROPS, { isDragging: true });
5038
5094
  return true;
5039
5095
  }
5040
- /** Returns parameters for rotateStart. Override to add extra params (e.g. altitude). */
5041
- _getRotateStartParams(pos) {
5042
- return { pos };
5043
- }
5044
5096
  // Default handler for the `panmove` and `panend` event.
5045
5097
  _onPan(event) {
5046
5098
  if (!this.isDragging()) {
@@ -5388,7 +5440,8 @@ var Controller = class {
5388
5440
 
5389
5441
  // dist/controllers/view-state.js
5390
5442
  var ViewState = class {
5391
- constructor(props, state) {
5443
+ constructor(props, state, makeViewport) {
5444
+ this.makeViewport = makeViewport;
5392
5445
  this._viewportProps = this.applyConstraints(props);
5393
5446
  this._state = state;
5394
5447
  }
@@ -5404,6 +5457,22 @@ var ViewState = class {
5404
5457
  var import_web_mercator7 = require("@math.gl/web-mercator");
5405
5458
  var PITCH_MOUSE_THRESHOLD = 5;
5406
5459
  var PITCH_ACCEL = 1.2;
5460
+ var WEB_MERCATOR_TILE_SIZE = 512;
5461
+ var WEB_MERCATOR_MAX_BOUNDS = [
5462
+ [-Infinity, -90],
5463
+ [Infinity, 90]
5464
+ ];
5465
+ function lngLatToWorld2([lng, lat]) {
5466
+ if (Math.abs(lat) > 90) {
5467
+ lat = Math.sign(lat) * 90;
5468
+ }
5469
+ if (Number.isFinite(lng)) {
5470
+ const [x, y2] = (0, import_web_mercator7.lngLatToWorld)([lng, lat]);
5471
+ return [x, (0, import_core15.clamp)(y2, 0, WEB_MERCATOR_TILE_SIZE)];
5472
+ }
5473
+ const [, y] = (0, import_web_mercator7.lngLatToWorld)([0, lat]);
5474
+ return [lng, (0, import_core15.clamp)(y, 0, WEB_MERCATOR_TILE_SIZE)];
5475
+ }
5407
5476
  var MapState = class extends ViewState {
5408
5477
  constructor(options) {
5409
5478
  const {
@@ -5456,6 +5525,7 @@ var MapState = class extends ViewState {
5456
5525
  assert(Number.isFinite(longitude));
5457
5526
  assert(Number.isFinite(latitude));
5458
5527
  assert(Number.isFinite(zoom));
5528
+ const maxBounds = options.maxBounds || (normalize ? WEB_MERCATOR_MAX_BOUNDS : null);
5459
5529
  super({
5460
5530
  width,
5461
5531
  height,
@@ -5470,7 +5540,8 @@ var MapState = class extends ViewState {
5470
5540
  maxPitch,
5471
5541
  minPitch,
5472
5542
  normalize,
5473
- position
5543
+ position,
5544
+ maxBounds
5474
5545
  }, {
5475
5546
  startPanLngLat,
5476
5547
  startZoomLngLat,
@@ -5479,8 +5550,8 @@ var MapState = class extends ViewState {
5479
5550
  startBearing,
5480
5551
  startPitch,
5481
5552
  startZoom
5482
- });
5483
- this.makeViewport = options.makeViewport;
5553
+ }, options.makeViewport);
5554
+ this.getAltitude = options.getAltitude;
5484
5555
  }
5485
5556
  /**
5486
5557
  * Start panning
@@ -5518,12 +5589,10 @@ var MapState = class extends ViewState {
5518
5589
  /**
5519
5590
  * Start rotating
5520
5591
  * @param {[Number, Number]} pos - position on screen where the center is
5521
- * @param {Number} altitude - optional altitude for rotation pivot
5522
- * - undefined: rotate around viewport center (no pivot point)
5523
- * - 0: rotate around pointer position at ground level
5524
- * - other value: rotate around pointer position at specified altitude
5525
5592
  */
5526
- rotateStart({ pos, altitude }) {
5593
+ rotateStart({ pos }) {
5594
+ var _a;
5595
+ const altitude = (_a = this.getAltitude) == null ? void 0 : _a.call(this, pos);
5527
5596
  return this._getUpdatedState({
5528
5597
  startRotatePos: pos,
5529
5598
  startRotateLngLat: altitude !== void 0 ? this._unproject3D(pos, altitude) : void 0,
@@ -5601,9 +5670,7 @@ var MapState = class extends ViewState {
5601
5670
  if (!startZoomLngLat) {
5602
5671
  return this;
5603
5672
  }
5604
- const { maxZoom, minZoom } = this.getViewportProps();
5605
- let zoom = startZoom + Math.log2(scale);
5606
- zoom = (0, import_core15.clamp)(zoom, minZoom, maxZoom);
5673
+ const zoom = this._constrainZoom(startZoom + Math.log2(scale));
5607
5674
  const zoomedViewport = this.makeViewport({ ...this.getViewportProps(), zoom });
5608
5675
  return this._getUpdatedState({
5609
5676
  zoom,
@@ -5672,17 +5739,52 @@ var MapState = class extends ViewState {
5672
5739
  }
5673
5740
  // Apply any constraints (mathematical or defined by _viewportProps) to map state
5674
5741
  applyConstraints(props) {
5675
- const { maxZoom, minZoom, zoom } = props;
5676
- props.zoom = (0, import_core15.clamp)(zoom, minZoom, maxZoom);
5677
- const { maxPitch, minPitch, pitch } = props;
5678
- props.pitch = (0, import_core15.clamp)(pitch, minPitch, maxPitch);
5679
- const { normalize = true } = props;
5742
+ const { maxPitch, minPitch, pitch, longitude, bearing, normalize, maxBounds } = props;
5680
5743
  if (normalize) {
5681
- Object.assign(props, (0, import_web_mercator7.normalizeViewportProps)(props));
5744
+ if (longitude < -180 || longitude > 180) {
5745
+ props.longitude = mod(longitude + 180, 360) - 180;
5746
+ }
5747
+ if (bearing < -180 || bearing > 180) {
5748
+ props.bearing = mod(bearing + 180, 360) - 180;
5749
+ }
5750
+ }
5751
+ props.pitch = (0, import_core15.clamp)(pitch, minPitch, maxPitch);
5752
+ props.zoom = this._constrainZoom(props.zoom, props);
5753
+ if (maxBounds) {
5754
+ const bl = lngLatToWorld2(maxBounds[0]);
5755
+ const tr = lngLatToWorld2(maxBounds[1]);
5756
+ const scale = 2 ** props.zoom;
5757
+ const halfWidth = props.width / 2 / scale;
5758
+ const halfHeight = props.height / 2 / scale;
5759
+ const [minLng, minLat] = (0, import_web_mercator7.worldToLngLat)([bl[0] + halfWidth, bl[1] + halfHeight]);
5760
+ const [maxLng, maxLat] = (0, import_web_mercator7.worldToLngLat)([tr[0] - halfWidth, tr[1] - halfHeight]);
5761
+ props.longitude = (0, import_core15.clamp)(props.longitude, minLng, maxLng);
5762
+ props.latitude = (0, import_core15.clamp)(props.latitude, minLat, maxLat);
5682
5763
  }
5683
5764
  return props;
5684
5765
  }
5685
5766
  /* Private methods */
5767
+ _constrainZoom(zoom, props) {
5768
+ props || (props = this.getViewportProps());
5769
+ const { maxZoom, maxBounds } = props;
5770
+ const shouldApplyMaxBounds = maxBounds !== null && props.width > 0 && props.height > 0;
5771
+ let { minZoom } = props;
5772
+ if (shouldApplyMaxBounds) {
5773
+ const bl = lngLatToWorld2(maxBounds[0]);
5774
+ const tr = lngLatToWorld2(maxBounds[1]);
5775
+ const w = tr[0] - bl[0];
5776
+ const h = tr[1] - bl[1];
5777
+ if (Number.isFinite(w) && w > 0) {
5778
+ minZoom = Math.max(minZoom, Math.log2(props.width / w));
5779
+ }
5780
+ if (Number.isFinite(h) && h > 0) {
5781
+ minZoom = Math.max(minZoom, Math.log2(props.height / h));
5782
+ }
5783
+ if (minZoom > maxZoom)
5784
+ minZoom = maxZoom;
5785
+ }
5786
+ return (0, import_core15.clamp)(zoom, minZoom, maxZoom);
5787
+ }
5686
5788
  _zoomFromCenter(scale) {
5687
5789
  const { width, height } = this.getViewportProps();
5688
5790
  return this.zoom({
@@ -5746,8 +5848,8 @@ var MapState = class extends ViewState {
5746
5848
  }
5747
5849
  };
5748
5850
  var MapController = class extends Controller {
5749
- constructor(opts) {
5750
- super(opts);
5851
+ constructor() {
5852
+ super(...arguments);
5751
5853
  this.ControllerState = MapState;
5752
5854
  this.transition = {
5753
5855
  transitionDuration: 300,
@@ -5760,23 +5862,29 @@ var MapController = class extends Controller {
5760
5862
  };
5761
5863
  this.dragMode = "pan";
5762
5864
  this.rotationPivot = "center";
5763
- this.pickPosition = opts.pickPosition;
5865
+ this._getAltitude = (pos) => {
5866
+ if (this.rotationPivot === "2d") {
5867
+ return 0;
5868
+ } else if (this.rotationPivot === "3d") {
5869
+ if (this.pickPosition) {
5870
+ const { x, y } = this.props;
5871
+ const pickResult = this.pickPosition(x + pos[0], y + pos[1]);
5872
+ if (pickResult && pickResult.coordinate && pickResult.coordinate.length >= 3) {
5873
+ return pickResult.coordinate[2];
5874
+ }
5875
+ }
5876
+ }
5877
+ return void 0;
5878
+ };
5764
5879
  }
5765
5880
  setProps(props) {
5766
5881
  if ("rotationPivot" in props) {
5767
5882
  this.rotationPivot = props.rotationPivot || "center";
5768
5883
  }
5884
+ props.getAltitude = this._getAltitude;
5769
5885
  props.position = props.position || [0, 0, 0];
5770
- const oldProps = this.props;
5886
+ props.maxBounds = props.maxBounds || (props.normalize === false ? null : WEB_MERCATOR_MAX_BOUNDS);
5771
5887
  super.setProps(props);
5772
- const dimensionChanged = !oldProps || oldProps.height !== props.height;
5773
- if (dimensionChanged) {
5774
- this.updateViewport(new this.ControllerState({
5775
- makeViewport: this.makeViewport,
5776
- ...props,
5777
- ...this.state
5778
- }));
5779
- }
5780
5888
  }
5781
5889
  updateViewport(newControllerState, extraProps = null, interactionState = {}) {
5782
5890
  const state = newControllerState.getState();
@@ -5790,22 +5898,6 @@ var MapController = class extends Controller {
5790
5898
  }
5791
5899
  super.updateViewport(newControllerState, extraProps, interactionState);
5792
5900
  }
5793
- /** Add altitude to rotateStart params based on rotationPivot mode */
5794
- _getRotateStartParams(pos) {
5795
- let altitude;
5796
- if (this.rotationPivot === "2d") {
5797
- altitude = 0;
5798
- } else if (this.rotationPivot === "3d") {
5799
- if (this.pickPosition) {
5800
- const { x, y } = this.props;
5801
- const pickResult = this.pickPosition(x + pos[0], y + pos[1]);
5802
- if (pickResult && pickResult.coordinate && pickResult.coordinate.length >= 3) {
5803
- altitude = pickResult.coordinate[2];
5804
- }
5805
- }
5806
- }
5807
- return { pos, altitude };
5808
- }
5809
5901
  };
5810
5902
 
5811
5903
  // dist/views/map-view.js
@@ -5920,13 +6012,17 @@ var DrawLayersPass = class extends LayersPass {
5920
6012
  const { operation } = layer.props;
5921
6013
  return operation.includes("draw") || operation.includes("terrain");
5922
6014
  }
6015
+ render(options) {
6016
+ return this._render(options);
6017
+ }
5923
6018
  };
5924
6019
 
5925
6020
  // dist/lib/deck-renderer.js
5926
6021
  var TRACE_RENDER_LAYERS = "deckRenderer.renderLayers";
5927
6022
  var DeckRenderer = class {
5928
- constructor(device) {
6023
+ constructor(device, opts = {}) {
5929
6024
  this.device = device;
6025
+ this.stats = opts.stats;
5930
6026
  this.layerFilter = null;
5931
6027
  this.drawPickingColors = false;
5932
6028
  this.drawLayersPass = new DrawLayersPass(device);
@@ -5964,7 +6060,8 @@ var DeckRenderer = class {
5964
6060
  renderOpts.clearColor = [0, 0, 0, 0];
5965
6061
  renderOpts.clearCanvas = true;
5966
6062
  }
5967
- const renderStats = layerPass.render({ ...renderOpts, target: outputBuffer });
6063
+ const renderResult = layerPass.render({ ...renderOpts, target: outputBuffer });
6064
+ const renderStats = "stats" in renderResult ? renderResult.stats : renderResult;
5968
6065
  if (renderOpts.effects) {
5969
6066
  if (this.lastPostProcessEffect) {
5970
6067
  renderOpts.clearCanvas = opts.clearCanvas === void 0 ? true : opts.clearCanvas;
@@ -5973,6 +6070,7 @@ var DeckRenderer = class {
5973
6070
  }
5974
6071
  this.renderCount++;
5975
6072
  debug(TRACE_RENDER_LAYERS, this, renderStats, opts);
6073
+ this._updateStats(renderStats);
5976
6074
  }
5977
6075
  needsRedraw(opts = { clearRedrawFlags: false }) {
5978
6076
  const redraw = this._needsRedraw;
@@ -5988,6 +6086,15 @@ var DeckRenderer = class {
5988
6086
  }
5989
6087
  renderBuffers.length = 0;
5990
6088
  }
6089
+ _updateStats(source3) {
6090
+ if (!this.stats)
6091
+ return;
6092
+ let layersCount = 0;
6093
+ for (const { visibleCount } of source3) {
6094
+ layersCount += visibleCount;
6095
+ }
6096
+ this.stats.get("Layers rendered").addCount(layersCount);
6097
+ }
5991
6098
  _preRender(effects, opts) {
5992
6099
  this.lastPostProcessEffect = null;
5993
6100
  opts.preRenderStats = opts.preRenderStats || {};
@@ -6204,9 +6311,10 @@ function getViewportFromCoordinates(viewports, pixel) {
6204
6311
 
6205
6312
  // dist/lib/deck-picker.js
6206
6313
  var DeckPicker = class {
6207
- constructor(device) {
6314
+ constructor(device, opts = {}) {
6208
6315
  this._pickable = true;
6209
6316
  this.device = device;
6317
+ this.stats = opts.stats;
6210
6318
  this.pickLayersPass = new PickLayersPass(device);
6211
6319
  this.lastPickedInfo = {
6212
6320
  index: -1,
@@ -6690,7 +6798,8 @@ var DeckPicker = class {
6690
6798
  opts.preRenderStats[effect.id] = effect.preRender(opts);
6691
6799
  }
6692
6800
  }
6693
- const { decodePickingColor } = this.pickLayersPass.render(opts);
6801
+ const { decodePickingColor, stats } = this.pickLayersPass.render(opts);
6802
+ this._updateStats(stats);
6694
6803
  const { x, y, width, height } = deviceRect;
6695
6804
  const pickedColors = new (pickZ ? Float32Array : Uint8Array)(width * height * 4);
6696
6805
  this.device.readPixelsToArrayWebGL(pickingFBO, {
@@ -6725,7 +6834,8 @@ var DeckPicker = class {
6725
6834
  opts.preRenderStats[effect.id] = effect.preRender(opts);
6726
6835
  }
6727
6836
  }
6728
- const { decodePickingColor } = this.pickLayersPass.render(opts);
6837
+ const { decodePickingColor, stats } = this.pickLayersPass.render(opts);
6838
+ this._updateStats(stats);
6729
6839
  const { x, y, width, height } = deviceRect;
6730
6840
  const pickedColors = new (pickZ ? Float32Array : Uint8Array)(width * height * 4);
6731
6841
  this.device.readPixelsToArrayWebGL(pickingFBO, {
@@ -6737,6 +6847,15 @@ var DeckPicker = class {
6737
6847
  });
6738
6848
  return { pickedColors, decodePickingColor };
6739
6849
  }
6850
+ _updateStats(source3) {
6851
+ if (!this.stats)
6852
+ return;
6853
+ let layersCount = 0;
6854
+ for (const { visibleCount } of source3) {
6855
+ layersCount += visibleCount;
6856
+ }
6857
+ this.stats.get("Layers picked").addCount(layersCount);
6858
+ }
6740
6859
  /**
6741
6860
  * Determine which layers to use for the depth (pickZ) pass.
6742
6861
  * - If a non-draped layer was picked, use just that layer.
@@ -7276,10 +7395,15 @@ var Deck = class {
7276
7395
  this.metrics = {
7277
7396
  fps: 0,
7278
7397
  setPropsTime: 0,
7398
+ layersCount: 0,
7399
+ drawLayersCount: 0,
7400
+ updateLayersCount: 0,
7401
+ updateAttributesCount: 0,
7279
7402
  updateAttributesTime: 0,
7280
7403
  framesRedrawn: 0,
7281
7404
  pickTime: 0,
7282
7405
  pickCount: 0,
7406
+ pickLayersCount: 0,
7283
7407
  gpuTime: 0,
7284
7408
  gpuTimePerFrame: 0,
7285
7409
  cpuTime: 0,
@@ -7873,8 +7997,8 @@ var Deck = class {
7873
7997
  deck: this,
7874
7998
  device: this.device
7875
7999
  });
7876
- this.deckRenderer = new DeckRenderer(this.device);
7877
- this.deckPicker = new DeckPicker(this.device);
8000
+ this.deckRenderer = new DeckRenderer(this.device, { stats: this.stats });
8001
+ this.deckPicker = new DeckPicker(this.device, { stats: this.stats });
7878
8002
  const widgetParent = ((_c = this.props.parent) == null ? void 0 : _c.querySelector(".deck-widgets-root")) || ((_d = this.canvas) == null ? void 0 : _d.parentElement);
7879
8003
  this.widgetManager = new WidgetManager({
7880
8004
  deck: this,
@@ -7957,6 +8081,7 @@ var Deck = class {
7957
8081
  stats.get("CPU Time").addTime(animationLoopStats.get("CPU Time").lastTiming);
7958
8082
  }
7959
8083
  _getMetrics() {
8084
+ var _a;
7960
8085
  const { metrics, stats } = this;
7961
8086
  metrics.fps = stats.get("frameRate").getHz();
7962
8087
  metrics.setPropsTime = stats.get("setProps Time").time;
@@ -7964,11 +8089,16 @@ var Deck = class {
7964
8089
  metrics.framesRedrawn = stats.get("Redraw Count").count;
7965
8090
  metrics.pickTime = stats.get("pickObject Time").time + stats.get("pickMultipleObjects Time").time + stats.get("pickObjects Time").time;
7966
8091
  metrics.pickCount = stats.get("Pick Count").count;
8092
+ metrics.layersCount = ((_a = this.layerManager) == null ? void 0 : _a.layers.length) ?? 0;
8093
+ metrics.drawLayersCount = stats.get("Layers rendered").lastSampleCount;
8094
+ metrics.pickLayersCount = stats.get("Layers picked").lastSampleCount;
8095
+ metrics.updateAttributesCount = stats.get("Layers updated").count;
8096
+ metrics.updateAttributesCount = stats.get("Attributes updated").count;
7967
8097
  metrics.gpuTime = stats.get("GPU Time").time;
7968
8098
  metrics.cpuTime = stats.get("CPU Time").time;
7969
8099
  metrics.gpuTimePerFrame = stats.get("GPU Time").getAverageTime();
7970
8100
  metrics.cpuTimePerFrame = stats.get("CPU Time").getAverageTime();
7971
- const memoryStats = import_core16.luma.stats.get("Memory Usage");
8101
+ const memoryStats = import_core16.luma.stats.get("GPU Time and Memory");
7972
8102
  metrics.bufferMemory = memoryStats.get("Buffer Memory").count;
7973
8103
  metrics.textureMemory = memoryStats.get("Texture Memory").count;
7974
8104
  metrics.renderbufferMemory = memoryStats.get("Renderbuffer Memory").count;
@@ -9651,6 +9781,8 @@ var AttributeManager = class {
9651
9781
  }
9652
9782
  if (this.stats) {
9653
9783
  this.stats.get("Update Attributes").timeEnd();
9784
+ if (updated)
9785
+ this.stats.get("Attributes updated").incrementCount();
9654
9786
  }
9655
9787
  this.attributeTransitionManager.update({
9656
9788
  attributes: this.attributes,
@@ -11436,6 +11568,7 @@ var Layer = class extends component_default {
11436
11568
  if (!stateNeedsUpdate) {
11437
11569
  return;
11438
11570
  }
11571
+ this.context.stats.get("Layer updates").incrementCount();
11439
11572
  const currentProps = this.props;
11440
11573
  const context = this.context;
11441
11574
  const internalState = this.internalState;
@@ -11932,9 +12065,13 @@ var OrbitViewport = class extends viewport_default {
11932
12065
  }
11933
12066
  panByPosition(coords, pixel, startPixel) {
11934
12067
  const p0 = this.project(coords);
12068
+ const { near, far } = getProjectionParameters(this.projectionMatrix);
12069
+ const pz = near * far / (far - p0[2] * (far - near));
12070
+ const centerZ = near * far / (far - this.projectedCenter[2] * (far - near));
12071
+ const shiftScale = pz / centerZ;
11935
12072
  const nextCenter = [
11936
- this.width / 2 + p0[0] - pixel[0],
11937
- this.height / 2 + p0[1] - pixel[1],
12073
+ this.width / 2 + (p0[0] - pixel[0]) * shiftScale,
12074
+ this.height / 2 + (p0[1] - pixel[1]) * shiftScale,
11938
12075
  this.projectedCenter[2]
11939
12076
  ];
11940
12077
  return {
@@ -12085,6 +12222,7 @@ var FirstPersonState = class extends ViewState {
12085
12222
  latitude = null,
12086
12223
  maxPitch = 90,
12087
12224
  minPitch = -90,
12225
+ maxBounds = null,
12088
12226
  // Model state when the rotate operation first started
12089
12227
  startRotatePos,
12090
12228
  startBearing,
@@ -12102,7 +12240,8 @@ var FirstPersonState = class extends ViewState {
12102
12240
  longitude,
12103
12241
  latitude,
12104
12242
  maxPitch,
12105
- minPitch
12243
+ minPitch,
12244
+ maxBounds
12106
12245
  }, {
12107
12246
  startRotatePos,
12108
12247
  startBearing,
@@ -12110,8 +12249,7 @@ var FirstPersonState = class extends ViewState {
12110
12249
  startZoomPosition,
12111
12250
  startPanPos,
12112
12251
  startPanPosition
12113
- });
12114
- this.makeViewport = options.makeViewport;
12252
+ }, options.makeViewport);
12115
12253
  }
12116
12254
  /* Public API */
12117
12255
  /**
@@ -12321,7 +12459,7 @@ var FirstPersonState = class extends ViewState {
12321
12459
  }
12322
12460
  // Apply any constraints (mathematical or defined by _viewportProps) to map state
12323
12461
  applyConstraints(props) {
12324
- const { pitch, maxPitch, minPitch, longitude, bearing } = props;
12462
+ const { pitch, maxPitch, minPitch, longitude, position, bearing, maxBounds } = props;
12325
12463
  props.pitch = (0, import_core26.clamp)(pitch, minPitch, maxPitch);
12326
12464
  if (longitude !== null && (longitude < -180 || longitude > 180)) {
12327
12465
  props.longitude = mod(longitude + 180, 360) - 180;
@@ -12329,6 +12467,14 @@ var FirstPersonState = class extends ViewState {
12329
12467
  if (bearing < -180 || bearing > 180) {
12330
12468
  props.bearing = mod(bearing + 180, 360) - 180;
12331
12469
  }
12470
+ if (maxBounds) {
12471
+ const x = (0, import_core26.clamp)(position[0], maxBounds[0][0], maxBounds[1][0]);
12472
+ const y = (0, import_core26.clamp)(position[1], maxBounds[0][1], maxBounds[1][1]);
12473
+ const z = (0, import_core26.clamp)(position[2] ?? 0, maxBounds[0][2] ?? 0, maxBounds[1][2] ?? 0);
12474
+ if (x !== position[0] || y !== position[1] || z !== position[2]) {
12475
+ props.position = [x, y, z];
12476
+ }
12477
+ }
12332
12478
  return props;
12333
12479
  }
12334
12480
  };
@@ -12379,6 +12525,7 @@ var OrbitState = class extends ViewState {
12379
12525
  maxRotationX = 90,
12380
12526
  minZoom = -Infinity,
12381
12527
  maxZoom = Infinity,
12528
+ maxBounds = null,
12382
12529
  /** Interaction states, required to calculate change during transform */
12383
12530
  // Model state when the pan operation first started
12384
12531
  startPanPosition,
@@ -12400,7 +12547,8 @@ var OrbitState = class extends ViewState {
12400
12547
  minRotationX,
12401
12548
  maxRotationX,
12402
12549
  minZoom,
12403
- maxZoom
12550
+ maxZoom,
12551
+ maxBounds
12404
12552
  }, {
12405
12553
  startPanPosition,
12406
12554
  startRotatePos,
@@ -12408,8 +12556,8 @@ var OrbitState = class extends ViewState {
12408
12556
  startRotationOrbit,
12409
12557
  startZoomPosition,
12410
12558
  startZoom
12411
- });
12412
- this.makeViewport = options.makeViewport;
12559
+ }, options.makeViewport);
12560
+ this.unproject3D = options.unproject3D;
12413
12561
  }
12414
12562
  /**
12415
12563
  * Start panning
@@ -12594,17 +12742,20 @@ var OrbitState = class extends ViewState {
12594
12742
  return viewport.project(pos);
12595
12743
  }
12596
12744
  _unproject(pos) {
12745
+ var _a;
12746
+ const p = (_a = this.unproject3D) == null ? void 0 : _a.call(this, pos);
12747
+ if (p)
12748
+ return p;
12597
12749
  const viewport = this.makeViewport(this.getViewportProps());
12598
12750
  return viewport.unproject(pos);
12599
12751
  }
12600
12752
  // Calculates new zoom
12601
12753
  _calculateNewZoom({ scale, startZoom }) {
12602
- const { maxZoom, minZoom } = this.getViewportProps();
12603
12754
  if (startZoom === void 0) {
12604
12755
  startZoom = this.getViewportProps().zoom;
12605
12756
  }
12606
12757
  const zoom = startZoom + Math.log2(scale);
12607
- return (0, import_core27.clamp)(zoom, minZoom, maxZoom);
12758
+ return this._constrainZoom(zoom);
12608
12759
  }
12609
12760
  _panFromCenter(offset) {
12610
12761
  const { target } = this.getViewportProps();
@@ -12624,14 +12775,94 @@ var OrbitState = class extends ViewState {
12624
12775
  }
12625
12776
  // Apply any constraints (mathematical or defined by _viewportProps) to map state
12626
12777
  applyConstraints(props) {
12627
- const { maxZoom, minZoom, zoom, maxRotationX, minRotationX, rotationOrbit } = props;
12628
- props.zoom = Array.isArray(zoom) ? [(0, import_core27.clamp)(zoom[0], minZoom, maxZoom), (0, import_core27.clamp)(zoom[1], minZoom, maxZoom)] : (0, import_core27.clamp)(zoom, minZoom, maxZoom);
12778
+ const { maxRotationX, minRotationX, rotationOrbit } = props;
12779
+ props.zoom = this._constrainZoom(props.zoom, props);
12629
12780
  props.rotationX = (0, import_core27.clamp)(props.rotationX, minRotationX, maxRotationX);
12630
12781
  if (rotationOrbit < -180 || rotationOrbit > 180) {
12631
12782
  props.rotationOrbit = mod(rotationOrbit + 180, 360) - 180;
12632
12783
  }
12784
+ props.target = this._constrainTarget(props);
12633
12785
  return props;
12634
12786
  }
12787
+ _constrainZoom(zoom, props) {
12788
+ props || (props = this.getViewportProps());
12789
+ const { maxZoom, maxBounds } = props;
12790
+ let { minZoom } = props;
12791
+ if (maxBounds && props.width > 0 && props.height > 0) {
12792
+ const dx = maxBounds[1][0] - maxBounds[0][0];
12793
+ const dy = maxBounds[1][1] - maxBounds[0][1];
12794
+ const dz = (maxBounds[1][2] ?? 0) - (maxBounds[0][2] ?? 0);
12795
+ const maxDiameter = Math.sqrt(dx * dx + dy * dy + dz * dz);
12796
+ if (maxDiameter > 0) {
12797
+ minZoom = Math.max(minZoom, Math.log2(Math.min(props.width, props.height) / maxDiameter));
12798
+ if (minZoom > maxZoom)
12799
+ minZoom = maxZoom;
12800
+ }
12801
+ }
12802
+ return (0, import_core27.clamp)(zoom, minZoom, maxZoom);
12803
+ }
12804
+ _constrainTarget(props) {
12805
+ var _a;
12806
+ const { target, maxBounds } = props;
12807
+ if (!maxBounds)
12808
+ return target;
12809
+ const [[minX, minY, minZ = 0], [maxX, maxY, maxZ = 0]] = maxBounds;
12810
+ if (target[0] >= minX && target[0] <= maxX && target[1] >= minY && target[1] <= maxY && target[2] >= minZ && target[2] <= maxZ) {
12811
+ return target;
12812
+ }
12813
+ const vp = (_a = this.makeViewport) == null ? void 0 : _a.call(this, props);
12814
+ if (vp) {
12815
+ const { cameraPosition } = vp;
12816
+ const nx = cameraPosition[0] - target[0];
12817
+ const ny = cameraPosition[1] - target[1];
12818
+ const nz = cameraPosition[2] - target[2];
12819
+ const c = nx * target[0] + ny * target[1] + nz * target[2];
12820
+ const minDot = nx * (nx >= 0 ? minX : maxX) + ny * (ny >= 0 ? minY : maxY) + nz * (nz >= 0 ? minZ : maxZ);
12821
+ const maxDot = nx * (nx >= 0 ? maxX : minX) + ny * (ny >= 0 ? maxY : minY) + nz * (nz >= 0 ? maxZ : minZ);
12822
+ if ((nx || ny || nz) && c >= minDot && c <= maxDot) {
12823
+ const clampX = (value) => (0, import_core27.clamp)(value, minX, maxX);
12824
+ const clampY = (value) => (0, import_core27.clamp)(value, minY, maxY);
12825
+ const clampZ = (value) => (0, import_core27.clamp)(value, minZ, maxZ);
12826
+ const f = (lambda2) => nx * clampX(target[0] - lambda2 * nx) + ny * clampY(target[1] - lambda2 * ny) + nz * clampZ(target[2] - lambda2 * nz) - c;
12827
+ let lo = -1;
12828
+ let hi = 1;
12829
+ let flo = f(lo);
12830
+ let fhi = f(hi);
12831
+ while (flo < 0) {
12832
+ hi = lo;
12833
+ fhi = flo;
12834
+ lo *= 2;
12835
+ flo = f(lo);
12836
+ }
12837
+ while (fhi > 0) {
12838
+ lo = hi;
12839
+ flo = fhi;
12840
+ hi *= 2;
12841
+ fhi = f(hi);
12842
+ }
12843
+ for (let i = 0; i < 30; i++) {
12844
+ const mid = (lo + hi) / 2;
12845
+ const fm = f(mid);
12846
+ if (fm > 0) {
12847
+ lo = mid;
12848
+ } else {
12849
+ hi = mid;
12850
+ }
12851
+ }
12852
+ const lambda = (lo + hi) / 2;
12853
+ return [
12854
+ clampX(target[0] - lambda * nx),
12855
+ clampY(target[1] - lambda * ny),
12856
+ clampZ(target[2] - lambda * nz)
12857
+ ];
12858
+ }
12859
+ }
12860
+ return [
12861
+ (0, import_core27.clamp)(target[0], minX, maxX),
12862
+ (0, import_core27.clamp)(target[1], minY, maxY),
12863
+ (0, import_core27.clamp)(target[2], minZ, maxZ)
12864
+ ];
12865
+ }
12635
12866
  };
12636
12867
  var OrbitController = class extends Controller {
12637
12868
  constructor() {
@@ -12646,6 +12877,20 @@ var OrbitController = class extends Controller {
12646
12877
  }
12647
12878
  })
12648
12879
  };
12880
+ this._unproject3D = (pos) => {
12881
+ if (this.pickPosition) {
12882
+ const { x, y } = this.props;
12883
+ const pickResult = this.pickPosition(x + pos[0], y + pos[1]);
12884
+ if (pickResult && pickResult.coordinate) {
12885
+ return pickResult.coordinate;
12886
+ }
12887
+ }
12888
+ return null;
12889
+ };
12890
+ }
12891
+ setProps(props) {
12892
+ props.unproject3D = this._unproject3D;
12893
+ super.setProps(props);
12649
12894
  }
12650
12895
  };
12651
12896
 
@@ -12667,41 +12912,300 @@ var orbit_view_default = OrbitView;
12667
12912
 
12668
12913
  // dist/controllers/orthographic-controller.js
12669
12914
  var import_core28 = require("@math.gl/core");
12670
- var OrthographicState = class extends OrbitState {
12671
- constructor(props) {
12672
- super(props);
12673
- this.zoomAxis = props.zoomAxis || "all";
12915
+ function normalizeZoom({ zoom = 0, zoomX, zoomY }) {
12916
+ zoomX = zoomX ?? (Array.isArray(zoom) ? zoom[0] : zoom);
12917
+ zoomY = zoomY ?? (Array.isArray(zoom) ? zoom[1] : zoom);
12918
+ return { zoomX, zoomY };
12919
+ }
12920
+ var OrthographicState = class extends ViewState {
12921
+ constructor(options) {
12922
+ const {
12923
+ /* Viewport arguments */
12924
+ width,
12925
+ // Width of viewport
12926
+ height,
12927
+ // Height of viewport
12928
+ target = [0, 0, 0],
12929
+ zoom = 0,
12930
+ zoomAxis = "all",
12931
+ /* Viewport constraints */
12932
+ minZoom = -Infinity,
12933
+ maxZoom = Infinity,
12934
+ minZoomX = minZoom,
12935
+ maxZoomX = maxZoom,
12936
+ minZoomY = minZoom,
12937
+ maxZoomY = maxZoom,
12938
+ maxBounds = null,
12939
+ /** Interaction states, required to calculate change during transform */
12940
+ // Model state when the pan operation first started
12941
+ startPanPosition,
12942
+ // Model state when the zoom operation first started
12943
+ startZoomPosition,
12944
+ startZoom
12945
+ } = options;
12946
+ const { zoomX, zoomY } = normalizeZoom(options);
12947
+ super({
12948
+ width,
12949
+ height,
12950
+ target,
12951
+ zoom,
12952
+ zoomX,
12953
+ zoomY,
12954
+ zoomAxis,
12955
+ minZoomX,
12956
+ maxZoomX,
12957
+ minZoomY,
12958
+ maxZoomY,
12959
+ maxBounds
12960
+ }, {
12961
+ startPanPosition,
12962
+ startZoomPosition,
12963
+ startZoom
12964
+ }, options.makeViewport);
12965
+ }
12966
+ /**
12967
+ * Start panning
12968
+ * @param {[Number, Number]} pos - position on screen where the pointer grabs
12969
+ */
12970
+ panStart({ pos }) {
12971
+ return this._getUpdatedState({
12972
+ startPanPosition: this._unproject(pos)
12973
+ });
12974
+ }
12975
+ /**
12976
+ * Pan
12977
+ * @param {[Number, Number]} pos - position on screen where the pointer is
12978
+ */
12979
+ pan({ pos, startPosition }) {
12980
+ const startPanPosition = this.getState().startPanPosition || startPosition;
12981
+ if (!startPanPosition) {
12982
+ return this;
12983
+ }
12984
+ const viewport = this.makeViewport(this.getViewportProps());
12985
+ const newProps = viewport.panByPosition(startPanPosition, pos);
12986
+ return this._getUpdatedState(newProps);
12987
+ }
12988
+ /**
12989
+ * End panning
12990
+ * Must call if `panStart()` was called
12991
+ */
12992
+ panEnd() {
12993
+ return this._getUpdatedState({
12994
+ startPanPosition: null
12995
+ });
12996
+ }
12997
+ /**
12998
+ * Start rotating
12999
+ */
13000
+ rotateStart() {
13001
+ return this;
13002
+ }
13003
+ /**
13004
+ * Rotate
13005
+ */
13006
+ rotate() {
13007
+ return this;
13008
+ }
13009
+ /**
13010
+ * End rotating
13011
+ */
13012
+ rotateEnd() {
13013
+ return this;
13014
+ }
13015
+ // shortest path between two view states
13016
+ shortestPathFrom(viewState) {
13017
+ const fromProps = viewState.getViewportProps();
13018
+ const props = { ...this.getViewportProps() };
13019
+ return props;
13020
+ }
13021
+ /**
13022
+ * Start zooming
13023
+ * @param {[Number, Number]} pos - position on screen where the pointer grabs
13024
+ */
13025
+ zoomStart({ pos }) {
13026
+ const { zoomX, zoomY } = this.getViewportProps();
13027
+ return this._getUpdatedState({
13028
+ startZoomPosition: this._unproject(pos),
13029
+ startZoom: [zoomX, zoomY]
13030
+ });
13031
+ }
13032
+ /**
13033
+ * Zoom
13034
+ * @param {[Number, Number]} pos - position on screen where the current target is
13035
+ * @param {[Number, Number]} startPos - the target position at
13036
+ * the start of the operation. Must be supplied of `zoomStart()` was not called
13037
+ * @param {Number} scale - a number between [0, 1] specifying the accumulated
13038
+ * relative scale.
13039
+ */
13040
+ zoom({ pos, startPos, scale }) {
13041
+ let { startZoom, startZoomPosition } = this.getState();
13042
+ if (!startZoomPosition) {
13043
+ const { zoomX, zoomY } = this.getViewportProps();
13044
+ startZoom = [zoomX, zoomY];
13045
+ startZoomPosition = this._unproject(startPos || pos);
13046
+ }
13047
+ if (!startZoomPosition) {
13048
+ return this;
13049
+ }
13050
+ const newZoomProps = this._constrainZoom(this._calculateNewZoom({ scale, startZoom }));
13051
+ const zoomedViewport = this.makeViewport({ ...this.getViewportProps(), ...newZoomProps });
13052
+ return this._getUpdatedState({
13053
+ ...newZoomProps,
13054
+ ...zoomedViewport.panByPosition(startZoomPosition, pos)
13055
+ });
13056
+ }
13057
+ /**
13058
+ * End zooming
13059
+ * Must call if `zoomStart()` was called
13060
+ */
13061
+ zoomEnd() {
13062
+ return this._getUpdatedState({
13063
+ startZoomPosition: null,
13064
+ startZoom: null
13065
+ });
13066
+ }
13067
+ zoomIn(speed = 2) {
13068
+ return this._getUpdatedState(this._calculateNewZoom({ scale: speed }));
13069
+ }
13070
+ zoomOut(speed = 2) {
13071
+ return this._getUpdatedState(this._calculateNewZoom({ scale: 1 / speed }));
13072
+ }
13073
+ moveLeft(speed = 50) {
13074
+ return this._panFromCenter([-speed, 0]);
13075
+ }
13076
+ moveRight(speed = 50) {
13077
+ return this._panFromCenter([speed, 0]);
13078
+ }
13079
+ moveUp(speed = 50) {
13080
+ return this._panFromCenter([0, -speed]);
13081
+ }
13082
+ moveDown(speed = 50) {
13083
+ return this._panFromCenter([0, speed]);
12674
13084
  }
13085
+ rotateLeft(speed = 15) {
13086
+ return this;
13087
+ }
13088
+ rotateRight(speed = 15) {
13089
+ return this;
13090
+ }
13091
+ rotateUp(speed = 10) {
13092
+ return this;
13093
+ }
13094
+ rotateDown(speed = 10) {
13095
+ return this;
13096
+ }
13097
+ /* Private methods */
13098
+ _project(pos) {
13099
+ const viewport = this.makeViewport(this.getViewportProps());
13100
+ return viewport.project(pos);
13101
+ }
13102
+ _unproject(pos) {
13103
+ const viewport = this.makeViewport(this.getViewportProps());
13104
+ return viewport.unproject(pos);
13105
+ }
13106
+ // Calculates new zoom
12675
13107
  _calculateNewZoom({ scale, startZoom }) {
12676
- const { maxZoom, minZoom } = this.getViewportProps();
13108
+ const { zoomX, zoomY, zoomAxis } = this.getViewportProps();
12677
13109
  if (startZoom === void 0) {
12678
- startZoom = this.getViewportProps().zoom;
13110
+ startZoom = [zoomX, zoomY];
12679
13111
  }
12680
- let deltaZoom = Math.log2(scale);
12681
- if (Array.isArray(startZoom)) {
12682
- let [newZoomX, newZoomY] = startZoom;
12683
- switch (this.zoomAxis) {
12684
- case "X":
12685
- newZoomX = (0, import_core28.clamp)(newZoomX + deltaZoom, minZoom, maxZoom);
12686
- break;
12687
- case "Y":
12688
- newZoomY = (0, import_core28.clamp)(newZoomY + deltaZoom, minZoom, maxZoom);
12689
- break;
12690
- default:
12691
- let z = Math.min(newZoomX + deltaZoom, newZoomY + deltaZoom);
12692
- if (z < minZoom) {
12693
- deltaZoom += minZoom - z;
12694
- }
12695
- z = Math.max(newZoomX + deltaZoom, newZoomY + deltaZoom);
12696
- if (z > maxZoom) {
12697
- deltaZoom += maxZoom - z;
12698
- }
12699
- newZoomX += deltaZoom;
12700
- newZoomY += deltaZoom;
13112
+ const deltaZoom = Math.log2(scale);
13113
+ let [newZoomX, newZoomY] = startZoom;
13114
+ switch (zoomAxis) {
13115
+ case "X":
13116
+ newZoomX += deltaZoom;
13117
+ break;
13118
+ case "Y":
13119
+ newZoomY += deltaZoom;
13120
+ break;
13121
+ default:
13122
+ newZoomX += deltaZoom;
13123
+ newZoomY += deltaZoom;
13124
+ }
13125
+ return {
13126
+ zoomX: newZoomX,
13127
+ zoomY: newZoomY
13128
+ };
13129
+ }
13130
+ _panFromCenter(offset) {
13131
+ const { target } = this.getViewportProps();
13132
+ const center = this._project(target);
13133
+ return this.pan({
13134
+ startPosition: target,
13135
+ pos: [center[0] + offset[0], center[1] + offset[1]]
13136
+ });
13137
+ }
13138
+ _getUpdatedState(newProps) {
13139
+ return new this.constructor({
13140
+ makeViewport: this.makeViewport,
13141
+ ...this.getViewportProps(),
13142
+ ...this.getState(),
13143
+ ...newProps
13144
+ });
13145
+ }
13146
+ // Apply any constraints (mathematical or defined by _viewportProps) to map state
13147
+ applyConstraints(props) {
13148
+ const { zoomX, zoomY } = this._constrainZoom(props, props);
13149
+ props.zoomX = zoomX;
13150
+ props.zoomY = zoomY;
13151
+ props.zoom = Array.isArray(props.zoom) || props.zoomX !== props.zoomY ? [props.zoomX, props.zoomY] : props.zoomX;
13152
+ const { maxBounds, target } = props;
13153
+ if (maxBounds) {
13154
+ const halfWidth = props.width / 2 / 2 ** zoomX;
13155
+ const halfHeight = props.height / 2 / 2 ** zoomY;
13156
+ const minX = maxBounds[0][0] + halfWidth;
13157
+ const maxX = maxBounds[1][0] - halfWidth;
13158
+ const minY = maxBounds[0][1] + halfHeight;
13159
+ const maxY = maxBounds[1][1] - halfHeight;
13160
+ const x = (0, import_core28.clamp)(target[0], minX, maxX);
13161
+ const y = (0, import_core28.clamp)(target[1], minY, maxY);
13162
+ if (x !== target[0] || y !== target[1]) {
13163
+ props.target = target.slice();
13164
+ props.target[0] = x;
13165
+ props.target[1] = y;
12701
13166
  }
12702
- return [newZoomX, newZoomY];
12703
13167
  }
12704
- return (0, import_core28.clamp)(startZoom + deltaZoom, minZoom, maxZoom);
13168
+ return props;
13169
+ }
13170
+ _constrainZoom({ zoomX, zoomY }, props) {
13171
+ props || (props = this.getViewportProps());
13172
+ const { zoomAxis, maxZoomX, maxZoomY, maxBounds } = props;
13173
+ let { minZoomX, minZoomY } = props;
13174
+ const shouldApplyMaxBounds = maxBounds !== null && props.width > 0 && props.height > 0;
13175
+ if (shouldApplyMaxBounds) {
13176
+ const bl = maxBounds[0];
13177
+ const tr = maxBounds[1];
13178
+ const w = tr[0] - bl[0];
13179
+ const h = tr[1] - bl[1];
13180
+ if (Number.isFinite(w) && w > 0) {
13181
+ minZoomX = Math.max(minZoomX, Math.log2(props.width / w));
13182
+ if (minZoomX > maxZoomX)
13183
+ minZoomX = maxZoomX;
13184
+ }
13185
+ if (Number.isFinite(h) && h > 0) {
13186
+ minZoomY = Math.max(minZoomY, Math.log2(props.height / h));
13187
+ if (minZoomY > maxZoomY)
13188
+ minZoomY = maxZoomY;
13189
+ }
13190
+ }
13191
+ switch (zoomAxis) {
13192
+ case "X":
13193
+ zoomX = (0, import_core28.clamp)(zoomX, minZoomX, maxZoomX);
13194
+ break;
13195
+ case "Y":
13196
+ zoomY = (0, import_core28.clamp)(zoomY, minZoomY, maxZoomY);
13197
+ break;
13198
+ default:
13199
+ let delta = Math.min(maxZoomX - zoomX, maxZoomY - zoomY, 0);
13200
+ if (delta === 0) {
13201
+ delta = Math.max(minZoomX - zoomX, minZoomY - zoomY, 0);
13202
+ }
13203
+ if (delta !== 0) {
13204
+ zoomX += delta;
13205
+ zoomY += delta;
13206
+ }
13207
+ }
13208
+ return { zoomX, zoomY };
12705
13209
  }
12706
13210
  };
12707
13211
  var OrthographicController = class extends Controller {
@@ -12710,10 +13214,14 @@ var OrthographicController = class extends Controller {
12710
13214
  this.ControllerState = OrthographicState;
12711
13215
  this.transition = {
12712
13216
  transitionDuration: 300,
12713
- transitionInterpolator: new LinearInterpolator(["target", "zoom"])
13217
+ transitionInterpolator: new LinearInterpolator(["target", "zoomX", "zoomY"])
12714
13218
  };
12715
13219
  this.dragMode = "pan";
12716
13220
  }
13221
+ setProps(props) {
13222
+ Object.assign(props, normalizeZoom(props));
13223
+ super.setProps(props);
13224
+ }
12717
13225
  _onPanRotate() {
12718
13226
  return false;
12719
13227
  }
@@ -12737,9 +13245,22 @@ var orthographic_view_default = OrthographicView;
12737
13245
  // dist/controllers/globe-controller.js
12738
13246
  var import_core29 = require("@math.gl/core");
12739
13247
  var import_web_mercator12 = require("@math.gl/web-mercator");
13248
+ var DEGREES_TO_RADIANS4 = Math.PI / 180;
13249
+ var RADIANS_TO_DEGREES2 = 180 / Math.PI;
13250
+ function degreesToPixels(angle, zoom = 0) {
13251
+ const radians = Math.min(180, angle) * DEGREES_TO_RADIANS4;
13252
+ const size = GLOBE_RADIUS * 2 * Math.sin(radians / 2);
13253
+ return size * Math.pow(2, zoom);
13254
+ }
13255
+ function pixelsToDegrees(pixels, zoom = 0) {
13256
+ const size = pixels / Math.pow(2, zoom);
13257
+ const radians = Math.asin(Math.min(1, size / GLOBE_RADIUS / 2)) * 2;
13258
+ return radians * RADIANS_TO_DEGREES2;
13259
+ }
12740
13260
  var GlobeState = class extends MapState {
12741
13261
  constructor(options) {
12742
13262
  const { startPanPos, ...mapStateOptions } = options;
13263
+ mapStateOptions.normalize = false;
12743
13264
  super(mapStateOptions);
12744
13265
  if (startPanPos !== void 0) {
12745
13266
  this._state.startPanPos = startPanPos;
@@ -12778,16 +13299,58 @@ var GlobeState = class extends MapState {
12778
13299
  return this._getUpdatedState({ zoom });
12779
13300
  }
12780
13301
  applyConstraints(props) {
12781
- const { longitude, latitude, maxZoom, minZoom, zoom } = props;
12782
- const ZOOM0 = zoomAdjust(0);
12783
- const zoomAdjustment = zoomAdjust(latitude) - ZOOM0;
12784
- props.zoom = (0, import_core29.clamp)(zoom, minZoom + zoomAdjustment, maxZoom + zoomAdjustment);
13302
+ const { longitude, latitude, maxBounds } = props;
13303
+ props.zoom = this._constrainZoom(props.zoom, props);
12785
13304
  if (longitude < -180 || longitude > 180) {
12786
13305
  props.longitude = mod(longitude + 180, 360) - 180;
12787
13306
  }
12788
13307
  props.latitude = (0, import_core29.clamp)(latitude, -import_web_mercator12.MAX_LATITUDE, import_web_mercator12.MAX_LATITUDE);
13308
+ if (maxBounds) {
13309
+ props.longitude = (0, import_core29.clamp)(props.longitude, maxBounds[0][0], maxBounds[1][0]);
13310
+ props.latitude = (0, import_core29.clamp)(props.latitude, maxBounds[0][1], maxBounds[1][1]);
13311
+ }
13312
+ if (maxBounds) {
13313
+ const effectiveZoom = props.zoom - zoomAdjust(latitude);
13314
+ const lngSpan = maxBounds[1][0] - maxBounds[0][0];
13315
+ const latSpan = maxBounds[1][1] - maxBounds[0][1];
13316
+ if (latSpan > 0 && latSpan < import_web_mercator12.MAX_LATITUDE * 2) {
13317
+ const halfHeightDegrees = Math.min(pixelsToDegrees(props.height, effectiveZoom), latSpan) / 2;
13318
+ props.latitude = (0, import_core29.clamp)(props.latitude, maxBounds[0][1] + halfHeightDegrees, maxBounds[1][1] - halfHeightDegrees);
13319
+ }
13320
+ if (lngSpan > 0 && lngSpan < 360) {
13321
+ const halfWidthDegrees = Math.min(pixelsToDegrees(props.width / Math.cos(props.latitude * DEGREES_TO_RADIANS4), effectiveZoom), lngSpan) / 2;
13322
+ props.longitude = (0, import_core29.clamp)(props.longitude, maxBounds[0][0] + halfWidthDegrees, maxBounds[1][0] - halfWidthDegrees);
13323
+ }
13324
+ }
13325
+ if (props.latitude !== latitude) {
13326
+ props.zoom += zoomAdjust(props.latitude) - zoomAdjust(latitude);
13327
+ }
12789
13328
  return props;
12790
13329
  }
13330
+ _constrainZoom(zoom, props) {
13331
+ props || (props = this.getViewportProps());
13332
+ const { latitude, maxZoom, maxBounds } = props;
13333
+ let { minZoom } = props;
13334
+ const ZOOM0 = zoomAdjust(0);
13335
+ const zoomAdjustment = zoomAdjust(latitude) - ZOOM0;
13336
+ const shouldApplyMaxBounds = maxBounds !== null && props.width > 0 && props.height > 0;
13337
+ if (shouldApplyMaxBounds) {
13338
+ const minLatitude = maxBounds[0][1];
13339
+ const maxLatitude = maxBounds[1][1];
13340
+ const fitLatitude = Math.sign(minLatitude) === Math.sign(maxLatitude) ? Math.min(Math.abs(minLatitude), Math.abs(maxLatitude)) : 0;
13341
+ const w = degreesToPixels(maxBounds[1][0] - maxBounds[0][0]) * Math.cos(fitLatitude * DEGREES_TO_RADIANS4);
13342
+ const h = degreesToPixels(maxBounds[1][1] - maxBounds[0][1]);
13343
+ if (w > 0) {
13344
+ minZoom = Math.max(minZoom, Math.log2(props.width / w) + ZOOM0);
13345
+ }
13346
+ if (h > 0) {
13347
+ minZoom = Math.max(minZoom, Math.log2(props.height / h) + ZOOM0);
13348
+ }
13349
+ if (minZoom > maxZoom)
13350
+ minZoom = maxZoom;
13351
+ }
13352
+ return (0, import_core29.clamp)(zoom, minZoom + zoomAdjustment, maxZoom + zoomAdjustment);
13353
+ }
12791
13354
  };
12792
13355
  var GlobeController = class extends Controller {
12793
13356
  constructor() {
@@ -12821,6 +13384,95 @@ var GlobeView = class extends View {
12821
13384
  GlobeView.displayName = "GlobeView";
12822
13385
  var globe_view_default = GlobeView;
12823
13386
 
13387
+ // dist/controllers/terrain-controller.js
13388
+ var TerrainController = class extends MapController {
13389
+ constructor() {
13390
+ super(...arguments);
13391
+ this._terrainAltitude = void 0;
13392
+ this._terrainAltitudeTarget = void 0;
13393
+ }
13394
+ setProps(props) {
13395
+ super.setProps({ rotationPivot: "3d", ...props });
13396
+ if (this._terrainAltitude !== void 0 && this._terrainAltitudeTarget !== void 0 && Math.abs(this._terrainAltitudeTarget - this._terrainAltitude) > 0.01) {
13397
+ this.updateViewport(new this.ControllerState({
13398
+ makeViewport: this.makeViewport,
13399
+ ...this.props,
13400
+ ...this.state
13401
+ }));
13402
+ }
13403
+ }
13404
+ updateViewport(newControllerState, extraProps = null, interactionState = {}) {
13405
+ const SMOOTHING = 0.05;
13406
+ if (this._terrainAltitudeTarget === void 0)
13407
+ return;
13408
+ if (this._terrainAltitude === void 0) {
13409
+ this._terrainAltitude = this._terrainAltitudeTarget;
13410
+ extraProps = this._rebaseViewport(this._terrainAltitudeTarget, newControllerState, extraProps);
13411
+ } else {
13412
+ this._terrainAltitude += (this._terrainAltitudeTarget - this._terrainAltitude) * SMOOTHING;
13413
+ }
13414
+ const viewportProps = newControllerState.getViewportProps();
13415
+ const pos = viewportProps.position || [0, 0, 0];
13416
+ extraProps = {
13417
+ ...extraProps,
13418
+ position: [pos[0], pos[1], this._terrainAltitude]
13419
+ };
13420
+ super.updateViewport(newControllerState, extraProps, interactionState);
13421
+ }
13422
+ _onPanStart(event) {
13423
+ this._pickTerrainCenterAltitude();
13424
+ return super._onPanStart(event);
13425
+ }
13426
+ _onWheel(event) {
13427
+ this._pickTerrainCenterAltitude();
13428
+ return super._onWheel(event);
13429
+ }
13430
+ _onDoubleClick(event) {
13431
+ this._pickTerrainCenterAltitude();
13432
+ return super._onDoubleClick(event);
13433
+ }
13434
+ _pickTerrainCenterAltitude() {
13435
+ if (!this.pickPosition) {
13436
+ return;
13437
+ }
13438
+ const { x, y, width, height } = this.props;
13439
+ const pickResult = this.pickPosition(x + width / 2, y + height / 2);
13440
+ if ((pickResult == null ? void 0 : pickResult.coordinate) && pickResult.coordinate.length >= 3) {
13441
+ this._terrainAltitudeTarget = pickResult.coordinate[2];
13442
+ }
13443
+ }
13444
+ /**
13445
+ * Utility function to return viewport that looks the same, but with
13446
+ * a position shifted to [0, 0, altitude]
13447
+ */
13448
+ _rebaseViewport(altitude, newControllerState, extraProps) {
13449
+ const viewportProps = newControllerState.getViewportProps();
13450
+ const oldViewport = this.makeViewport({ ...viewportProps, position: [0, 0, 0] });
13451
+ const oldCameraPos = oldViewport.cameraPosition;
13452
+ const centerZOffset = altitude * oldViewport.distanceScales.unitsPerMeter[2];
13453
+ const cameraHeightAboveOldCenter = oldCameraPos[2];
13454
+ const newCameraHeightAboveCenter = cameraHeightAboveOldCenter - centerZOffset;
13455
+ if (newCameraHeightAboveCenter <= 0) {
13456
+ return extraProps;
13457
+ }
13458
+ const zoomDelta = Math.log2(cameraHeightAboveOldCenter / newCameraHeightAboveCenter);
13459
+ const newZoom = viewportProps.zoom + zoomDelta;
13460
+ const newViewport = this.makeViewport({
13461
+ ...viewportProps,
13462
+ zoom: newZoom,
13463
+ position: [0, 0, altitude]
13464
+ });
13465
+ const { width, height } = viewportProps;
13466
+ const screenCenter = [width / 2, height / 2];
13467
+ const worldPoint = oldViewport.unproject(screenCenter, { targetZ: altitude });
13468
+ if (worldPoint && "panByPosition3D" in newViewport && typeof newViewport.panByPosition3D === "function") {
13469
+ const adjusted = newViewport.panByPosition3D(worldPoint, screenCenter);
13470
+ return { ...extraProps, position: [0, 0, altitude], zoom: newZoom, ...adjusted };
13471
+ }
13472
+ return extraProps;
13473
+ }
13474
+ };
13475
+
12824
13476
  // dist/lib/layer-extension.js
12825
13477
  var LayerExtension = class {
12826
13478
  static get componentName() {