maplibre-gl 6.5.0 → 6.6.0

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 (76) hide show
  1. package/dist/maplibre-gl-dev.mjs +1296 -1165
  2. package/dist/maplibre-gl-dev.mjs.map +1 -1
  3. package/dist/maplibre-gl-shared-dev.mjs +232 -102
  4. package/dist/maplibre-gl-shared-dev.mjs.map +1 -1
  5. package/dist/maplibre-gl-shared.mjs +2 -2
  6. package/dist/maplibre-gl-shared.mjs.map +1 -1
  7. package/dist/maplibre-gl-worker-dev.mjs +2 -2
  8. package/dist/maplibre-gl-worker.mjs +2 -2
  9. package/dist/maplibre-gl-worker.mjs.map +1 -1
  10. package/dist/maplibre-gl.d.ts +104 -88
  11. package/dist/maplibre-gl.mjs +20 -20
  12. package/dist/maplibre-gl.mjs.map +1 -1
  13. package/package.json +8 -8
  14. package/src/data/array_types.g.ts +43 -32
  15. package/src/data/bucket/symbol_attributes.ts +6 -3
  16. package/src/data/bucket/symbol_bucket.ts +12 -8
  17. package/src/data/feature_index.ts +3 -3
  18. package/src/geo/projection/covering_tiles.test.ts +36 -0
  19. package/src/geo/projection/covering_tiles.ts +2 -3
  20. package/src/geo/projection/globe_transform.test.ts +4 -5
  21. package/src/geo/projection/globe_transform.ts +7 -2
  22. package/src/geo/projection/globe_utils.ts +57 -0
  23. package/src/geo/projection/mercator_transform.bench.ts +55 -0
  24. package/src/geo/projection/mercator_transform.test.ts +203 -13
  25. package/src/geo/projection/mercator_transform.ts +118 -27
  26. package/src/geo/projection/mercator_utils.test.ts +50 -1
  27. package/src/geo/projection/mercator_utils.ts +20 -1
  28. package/src/geo/projection/vertical_perspective_transform.test.ts +97 -0
  29. package/src/geo/projection/vertical_perspective_transform.ts +72 -68
  30. package/src/geo/transform_helper.ts +14 -5
  31. package/src/geo/transform_interface.ts +22 -1
  32. package/src/render/painter.test.ts +1 -3
  33. package/src/render/painter.ts +7 -20
  34. package/src/render/terrain.test.ts +73 -84
  35. package/src/render/terrain.ts +125 -114
  36. package/src/shaders/glsl/symbol_icon.vertex.glsl +4 -1
  37. package/src/shaders/glsl/symbol_icon.vertex.glsl.g.ts +1 -1
  38. package/src/shaders/glsl/symbol_sdf.vertex.glsl +4 -1
  39. package/src/shaders/glsl/symbol_sdf.vertex.glsl.g.ts +1 -1
  40. package/src/shaders/glsl/symbol_text_and_icon.vertex.glsl +4 -1
  41. package/src/shaders/glsl/symbol_text_and_icon.vertex.glsl.g.ts +1 -1
  42. package/src/shaders/shaders.ts +0 -4
  43. package/src/source/raster_dem_tile_source.ts +1 -0
  44. package/src/style/query_utils.ts +8 -8
  45. package/src/style/style_layer/symbol_style_layer_properties.g.ts +6 -0
  46. package/src/style/style_layer.ts +4 -6
  47. package/src/symbol/collision_index.ts +25 -19
  48. package/src/symbol/placement.test.ts +1 -1
  49. package/src/symbol/placement.ts +28 -13
  50. package/src/symbol/projection.test.ts +79 -15
  51. package/src/symbol/projection.ts +38 -20
  52. package/src/symbol/symbol_elevation.ts +28 -0
  53. package/src/symbol/symbol_layout.ts +14 -6
  54. package/src/symbol/symbol_size.test.ts +100 -0
  55. package/src/symbol/symbol_size.ts +78 -50
  56. package/src/tile/terrain_tile_manager.ts +2 -2
  57. package/src/tile/tile.ts +3 -2
  58. package/src/tile/tile_manager_raster_dem.ts +1 -0
  59. package/src/ui/handler_manager.test.ts +11 -23
  60. package/src/ui/handler_manager.ts +1 -1
  61. package/src/ui/map_tests/map_pitch.test.ts +12 -0
  62. package/src/ui/map_tests/map_style.test.ts +49 -27
  63. package/src/ui/map_tests/map_zoom.test.ts +12 -0
  64. package/src/util/test/util.ts +34 -3
  65. package/src/webgl/draw/draw_color_relief.ts +7 -10
  66. package/src/webgl/draw/draw_symbol.ts +16 -12
  67. package/src/webgl/draw/draw_terrain.ts +4 -39
  68. package/src/webgl/draw/index.ts +2 -4
  69. package/src/webgl/program/program_uniforms.ts +1 -3
  70. package/src/webgl/program/symbol_program.ts +15 -5
  71. package/src/webgl/program/terrain_program.ts +1 -22
  72. package/src/webgl/render_to_texture.test.ts +0 -1
  73. package/src/shaders/glsl/terrain_coords.fragment.glsl +0 -11
  74. package/src/shaders/glsl/terrain_coords.fragment.glsl.g.ts +0 -2
  75. package/src/shaders/glsl/terrain_coords.vertex.glsl +0 -12
  76. package/src/shaders/glsl/terrain_coords.vertex.glsl.g.ts +0 -2
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * MapLibre GL JS
3
- * @license 3-Clause BSD. Full text of license: https://github.com/maplibre/maplibre-gl-js/blob/v6.5.0/LICENSE.txt
3
+ * @license 3-Clause BSD. Full text of license: https://github.com/maplibre/maplibre-gl-js/blob/v6.6.0/LICENSE.txt
4
4
  */
5
- import { $ as tileCoordinatesToMercatorCoordinates, $n as deepEqual, $r as mul, $t as TRANSITION_SUFFIX, A as evaluateSizeForFeature, Ai as perspective, An as getVideo, Ar as remapSaturate, At as toEvaluationFeature, Bi as invert, Br as wrap, Bt as SegmentVector, C as isCustomStyleLayer, Ci as equals, Cn as Event, Cr as nextPowerOfTwo, Ct as HEATMAP_FULL_RENDER_FBO_KEY, D as SymbolBucket, Di as invert$1, Dn as getArrayBuffer, Dr as radiansToDegrees, Dt as RGBAImage, E as isSymbolStyleLayer, Ei as identity, En as GLOBAL_DISPATCHER_ID, Er as pointPlaneSignedDistance, Et as AlphaImage, F as ImagePosition, Fi as translate, Fn as removeProtocol, Fr as subscribe, Ft as Uniform4f, G as isFillExtrusionStyleLayer, Gn as bezier, Gr as length, Gt as PosArray, H as isLineStyleLayer, Hi as isOffscreenCanvasDistorted, Hn as angleToRotateBetweenVectors2D, Hr as pixelsToTileUnits, Ht as CollisionCircleLayoutArray, I as potpack, Ii as create$4, In as config, Ir as threePlaneIntersection, It as UniformColor, J as cameraDirectionFromPitchBearing, Jn as createIdentityMat4f32, Jr as zero, Jt as TriangleIndexArray, Kn as clamp, Kr as scale$1, Kt as QuadTriangleArray, L as isStyleImageWebGLData, Li as fromRotation, Ln as AbortError, Lr as translatePosition, Lt as UniformColorArray, Mi as rotateY$1, Mn as sameOrigin, Mr as rollPitchBearingToQuat, Mt as Uniform1i, N as getAnchorAlignment, Ni as rotateZ, Nn as addProtocol, Nr as scaleZoom, Nt as Uniform2f, O as addDynamicAttributes, Oi as multiply, On as getJSON, Or as rayPlaneIntersection, Ot as isCircleStyleLayer, Pi as scale, Pn as getProtocol, Pr as sphericalToCartesian, Pt as Uniform3f, Q as projectToWorldCoordinates, Qn as createVec4f64, Qr as slerp, Qt as Properties, R as renderStyleImage, Ri as create$2, Rn as isAbortError, Rr as uniqueId, Rt as UniformFloatArray, S as createStyleLayer, Si as create$1, Sn as ErrorEvent, Sr as mod, St as isHillshadeStyleLayer, T as isBackgroundStyleLayer, Ti as fromScaling, Tn as AJAXError, Tr as pick, Tt as renderColorRamp, Ui as offscreenCanvasSupported, Un as arrayBufferToImage, Ur as EXTENT, Ut as LineStripIndexArray, V as collisionCircleLayout, Vi as rotate, Vn as MAX_VALID_LATITUDE, Vr as zoomScale, Vt as CollisionBoxArray, Wi as Point, Wn as arrayBufferToImageBitmap, Wr as create$3, Wt as Pos3dArray, X as getMercatorHorizon, Xn as createMat4f64, Xr as fromValues, Xt as isRasterStyleLayer, Y as cameraMercatorCoordinateFromCenterAndRotation, Yn as createIdentityMat4f64, Yr as fromEuler, Yt as createLayout, Z as maxMercatorHorizonAngle, Zn as createVec3f64, Zr as multiply$1, Zt as DataConstantProperty, _ as UnwrappedTileID, _i as transformMat4$1, _n as emptyStyle, _r as isTouchableEvent, _t as SubdivisionGranularityExpression, a as clipLine, ai as dot, ar as evaluateZoomSnap, at as mercatorXfromLng, b as isInBoundsForZoomLngLat, bi as clone$1, bn as interpolateFactory, br as lerp, ci as negate, cn as validateAndEmit, cr as findLineIntersection, ct as LngLat, di as rotateY, dn as Color, dr as getEdgeTiles, dt as EXTENT_BOUNDS, ei as scale$3, en as Transitionable, er as defaultEasing, et as unprojectFromWorldCoordinates, f as GEOJSON_TILE_LAYER_NAME, fi as rotateZ$1, fn as ProjectionDefinition, ft as Bounds, g as OverscaledTileID, gi as transformMat3, gn as diff, gr as isSafari, gt as SOUTH_POLE_Y, h as CanonicalTileID, hi as sub, hn as derefLayers, hr as isPointableEvent, ht as NORTH_POLE_Y, ii as cross, in as ZoomHistory, ir as ensureError, it as lngFromMercatorX, j as evaluateSizeForZoom, ji as rotateX, jn as makeRequest, jr as rollPitchBearingEqual, jt as Uniform1f, k as getOverlapMode, ki as ortho, kn as getReferrer, kr as readImageUsingVideoFrame, kt as polygonIntersectsPolygon, li as normalize, ln as validateStyle, lr as getAABB, lt as earthRadius, mi as scaleAndAdd, mr as isImageBitmap, ni as add, nr as differenceOfAnglesDegrees, nt as altitudeFromMercatorZ, oi as len, on as SPEC_SOURCE_TYPES, or as extend, ot as mercatorYfromLat, pi as scale$2, pn as ValidationError, pr as getRollPitchBearing, pt as isFillStyleLayer, q as calculateTileMatrix, qn as clone$2, qr as sqrLen, qt as RasterBoundsArray, r as TextAnchorEnum, ri as clone, rn as codePointUsesLocalIdeographFontFamily, rr as distanceOfAnglesRadians, rt as latFromMercatorY, s as TileCache, si as length$1, sn as emitValidationErrors, sr as filterObject, st as mercatorZfromAltitude, t as getAnchorJustification, ti as transformMat4, tn as EvaluationParameters, tr as degreesToRadians, tt as MercatorCoordinate, u as GeoJSONFeature, ui as rotateX$1, un as validateStyleAndEmit, ur as getAngleDelta, v as calculateTileKey, vi as transformQuat, vn as featureFilter, vr as isTouchableOrPointableType, vt as SubdivisionGranularitySetting, w as validateCustomStyleLayer, wi as exactEquals, wn as Evented, wr as parseCacheControl, wt as isHeatmapStyleLayer, x as Actor, xi as copy, xn as latest, xr as mapObject, xt as Texture, y as compareTileId, yi as zero$1, yr as isWorker, yt as isColorReliefStyleLayer, z as parseGlyphPbf, zi as determinant, zn as throwIfAborted, zr as warnOnce, zt as UniformMatrix4f } from "./maplibre-gl-shared-dev.mjs";
5
+ import { $ as projectToWorldCoordinates, $n as createVec4f64, $r as slerp, $t as Properties, A as evaluateSizeForFeature, Ai as perspective, An as getReferrer, Ar as readImageUsingVideoFrame, At as polygonIntersectsPolygon, Bi as invert, Bn as throwIfAborted, Br as warnOnce, Bt as UniformMatrix4f, C as isCustomStyleLayer, Ci as create$1, Cn as ErrorEvent, Cr as mod, Ct as isHillshadeStyleLayer, D as SymbolBucket, Di as invert$1, Dn as GLOBAL_DISPATCHER_ID, Dr as pointPlaneSignedDistance, Dt as AlphaImage, E as isSymbolStyleLayer, Ei as identity, En as AJAXError, Er as pick, Et as renderColorRamp, F as ImagePosition, Fi as translate, Fn as getProtocol, Fr as sphericalToCartesian, Ft as Uniform3f, G as isFillExtrusionStyleLayer, Gn as arrayBufferToImageBitmap, Gr as create$3, Gt as Pos3dArray, H as isLineStyleLayer, Hi as isOffscreenCanvasDistorted, Hn as MAX_VALID_LATITUDE, Hr as zoomScale, Ht as CollisionBoxArray, I as potpack, Ii as create$4, In as removeProtocol, Ir as subscribe, It as Uniform4f, J as cameraDirectionFromPitchBearing, Jn as clone$2, Jr as sqrLen, Jt as RasterBoundsArray, Kn as bezier, Kr as length, Kt as PosArray, L as isStyleImageWebGLData, Li as fromRotation, Ln as config, Lr as threePlaneIntersection, Lt as UniformColor, Mi as rotateY$1, Mn as makeRequest, Mr as rollPitchBearingEqual, Mt as Uniform1f, N as getAnchorAlignment, Ni as rotateZ, Nn as sameOrigin, Nr as rollPitchBearingToQuat, Nt as Uniform1i, O as addDynamicAttributes, Oi as multiply, On as getArrayBuffer, Or as radiansToDegrees, Ot as RGBAImage, Pi as scale, Pn as addProtocol, Pr as scaleZoom, Pt as Uniform2f, Q as maxMercatorHorizonAngle, Qn as createVec3f64, Qr as multiply$1, Qt as DataConstantProperty, R as renderStyleImage, Ri as create$2, Rn as AbortError, Rr as translatePosition, Rt as UniformColorArray, S as createStyleLayer, Si as copy, Sn as latest, Sr as mapObject, St as Texture, T as isBackgroundStyleLayer, Ti as fromScaling, Tn as Evented, Tr as parseCacheControl, Tt as isHeatmapStyleLayer, Ui as offscreenCanvasSupported, Un as angleToRotateBetweenVectors2D, Ur as pixelsToTileUnits, Ut as CollisionCircleLayoutArray, V as collisionCircleLayout, Vi as rotate, Vr as wrap, Vt as SegmentVector, Wi as Point, Wn as arrayBufferToImage, Wr as EXTENT, Wt as LineStripIndexArray, X as cameraMercatorCoordinateFromCenterAndRotation, Xn as createIdentityMat4f64, Xr as fromEuler, Xt as createLayout, Y as cameraMercatorCoordinate, Yn as createIdentityMat4f32, Yr as zero, Yt as TriangleIndexArray, Z as getMercatorHorizon, Zn as createMat4f64, Zr as fromValues, Zt as isRasterStyleLayer, _ as UnwrappedTileID, _i as transformMat3, _n as diff, _r as isSafari, _t as SOUTH_POLE_Y, a as clipLine, ai as cross, an as ZoomHistory, ar as ensureError, at as lngFromMercatorX, b as isInBoundsForZoomLngLat, bi as zero$1, br as isWorker, bt as isColorReliefStyleLayer, ci as length$1, cn as emitValidationErrors, cr as filterObject, ct as mercatorZfromAltitude, di as rotateX$1, dn as validateStyleAndEmit, dr as getAngleDelta, ei as mul, en as TRANSITION_SUFFIX, er as deepEqual, et as tileCoordinatesToMercatorCoordinates, f as GEOJSON_TILE_LAYER_NAME, fi as rotateY, fn as Color, fr as getEdgeTiles, ft as EXTENT_BOUNDS, g as OverscaledTileID, gi as sub, gn as derefLayers, gr as isPointableEvent, gt as NORTH_POLE_Y, h as CanonicalTileID, hi as scaleAndAdd, hr as isImageBitmap, ii as clone, in as codePointUsesLocalIdeographFontFamily, ir as distanceOfAnglesRadians, it as latFromMercatorY, j as evaluateSizeForZoom, ji as rotateX, jn as getVideo, jr as remapSaturate, jt as toEvaluationFeature, k as getOverlapMode, ki as ortho, kn as getJSON, kr as rayPlaneIntersection, kt as isCircleStyleLayer, li as negate, ln as validateAndEmit, lr as findLineIntersection, lt as LngLat, mi as scale$2, mn as ValidationError, mr as getRollPitchBearing, mt as isFillStyleLayer, ni as transformMat4, nn as EvaluationParameters, nr as degreesToRadians, nt as MercatorCoordinate, oi as dot, or as evaluateZoomSnap, ot as mercatorXfromLng, pi as rotateZ$1, pn as ProjectionDefinition, pt as Bounds, q as calculateTileMatrix, qn as clamp, qr as scale$1, qt as QuadTriangleArray, r as TextAnchorEnum, ri as add, rr as differenceOfAnglesDegrees, rt as altitudeFromMercatorZ, s as TileCache, si as len, sn as SPEC_SOURCE_TYPES, sr as extend, st as mercatorYfromLat, t as getAnchorJustification, ti as scale$3, tn as Transitionable, tr as defaultEasing, tt as unprojectFromWorldCoordinates, u as GeoJSONFeature, ui as normalize, un as validateStyle, ur as getAABB, ut as earthRadius, v as calculateTileKey, vi as transformMat4$1, vn as emptyStyle, vr as isTouchableEvent, vt as SubdivisionGranularityExpression, w as validateCustomStyleLayer, wi as equals, wn as Event, wr as nextPowerOfTwo, wt as HEATMAP_FULL_RENDER_FBO_KEY, x as Actor, xi as clone$1, xn as interpolateFactory, xr as lerp, y as compareTileId, yi as transformQuat, yn as featureFilter, yr as isTouchableOrPointableType, yt as SubdivisionGranularitySetting, z as parseGlyphPbf, zi as determinant, zn as isAbortError, zr as uniqueId, zt as UniformFloatArray } from "./maplibre-gl-shared-dev.mjs";
6
6
  //#region package.json
7
- var version$2 = "6.5.0";
7
+ var version$2 = "6.6.0";
8
8
  //#endregion
9
9
  //#region src/util/browser.ts
10
10
  let linkEl;
@@ -3013,6 +3013,7 @@ var RasterDEMTileSource = class extends RasterTileSource {
3013
3013
  });
3014
3014
  tile.needsHillshadePrepare = true;
3015
3015
  tile.needsTerrainPrepare = true;
3016
+ tile.needsColorReliefPrepare = true;
3016
3017
  tile.state = "loaded";
3017
3018
  }
3018
3019
  } catch (err) {
@@ -5322,9 +5323,8 @@ function getElevationForTileCulling(transform) {
5322
5323
  function coveringTiles(transform, options) {
5323
5324
  const frustum = transform.getCameraFrustum();
5324
5325
  const plane = transform.getClippingPlane();
5325
- const cameraCoord = transform.screenPointToMercatorCoordinate(transform.getCameraPoint());
5326
+ const cameraCoord = cameraMercatorCoordinate(transform);
5326
5327
  const centerCoord = MercatorCoordinate.fromLngLat(transform.center, transform.elevation);
5327
- cameraCoord.z = centerCoord.z + Math.cos(transform.pitchInRadians) * transform.cameraToCenterDistance / transform.worldSize;
5328
5328
  const elevationForTileCulling = getElevationForTileCulling(transform);
5329
5329
  const detailsProvider = transform.getCoveringTilesDetailsProvider();
5330
5330
  const allowVariableZoom = detailsProvider.allowVariableZoom(transform, options);
@@ -5565,6 +5565,7 @@ function backfillDEM(tile, inViewTiles) {
5565
5565
  function fillBorder(tile, borderTile) {
5566
5566
  tile.needsHillshadePrepare = true;
5567
5567
  tile.needsTerrainPrepare = true;
5568
+ tile.needsColorReliefPrepare = true;
5568
5569
  let dx = borderTile.tileID.canonical.x - tile.tileID.canonical.x;
5569
5570
  const dy = borderTile.tileID.canonical.y - tile.tileID.canonical.y;
5570
5571
  const dim = Math.pow(2, tile.tileID.canonical.z);
@@ -6285,6 +6286,30 @@ var PathInterpolator = class {
6285
6286
  }
6286
6287
  };
6287
6288
  //#endregion
6289
+ //#region src/symbol/symbol_elevation.ts
6290
+ /**
6291
+ * Resolves the elevation of a symbol at a tile coordinate, combining the terrain elevation with the
6292
+ * symbol's `symbol-height-offset`. `symbol-height-anchor` selects the datum the offset is measured
6293
+ * from: the terrain surface below the symbol (`ground`) or the zero elevation datum (`absolute`),
6294
+ * in which case the terrain below the symbol is ignored.
6295
+ *
6296
+ * Call this where the elevation is needed, so that `getElevation` keeps returning the plain terrain
6297
+ * elevation and never has to be substituted with a wrapper.
6298
+ *
6299
+ * @param getElevation - the terrain elevation function, absent when the map has no terrain
6300
+ * @param x - the tile x coordinate to evaluate the elevation at
6301
+ * @param y - the tile y coordinate to evaluate the elevation at
6302
+ * @param heightOffset - the evaluated `symbol-height-offset` of the symbol, in meters
6303
+ * @param heightAnchorGround - whether `symbol-height-anchor` is `ground`
6304
+ * @returns the elevation in meters, or undefined when the symbol sits at the zero elevation datum,
6305
+ * which lets the projection take its fast path that ignores the z coordinate
6306
+ */
6307
+ function getSymbolElevation(getElevation, x, y, heightOffset, heightAnchorGround) {
6308
+ if (!heightAnchorGround) return heightOffset;
6309
+ if (!getElevation) return heightOffset !== 0 ? heightOffset : void 0;
6310
+ return getElevation(x, y) + heightOffset;
6311
+ }
6312
+ //#endregion
6288
6313
  //#region src/symbol/grid_index.ts
6289
6314
  function overlapAllowed(overlapA, overlapB) {
6290
6315
  let allowed = true;
@@ -6678,16 +6703,22 @@ function getTileSkewVectors(transform) {
6678
6703
  };
6679
6704
  }
6680
6705
  /**
6706
+ * Resolves the elevation of the symbol described by `projectionContext` at a tile coordinate.
6707
+ */
6708
+ function elevationAt(projectionContext, x, y) {
6709
+ return getSymbolElevation(projectionContext.getElevation, x, y, projectionContext.heightOffset ?? 0, projectionContext.heightAnchorGround ?? true);
6710
+ }
6711
+ /**
6681
6712
  * Projects a point using a specified matrix, including the perspective divide.
6682
- * Uses a fast path if `getElevation` is undefined.
6713
+ * Uses a fast path if `elevation` is undefined.
6683
6714
  */
6684
- function projectWithMatrix(x, y, matrix, getElevation) {
6715
+ function projectWithMatrix(x, y, matrix, elevation) {
6685
6716
  let pos;
6686
- if (getElevation) {
6717
+ if (elevation != null) {
6687
6718
  pos = [
6688
6719
  x,
6689
6720
  y,
6690
- getElevation(x, y),
6721
+ elevation,
6691
6722
  1
6692
6723
  ];
6693
6724
  transformMat4(pos, pos, matrix);
@@ -6901,10 +6932,10 @@ function projectTileCoordinatesToLabelPlane(x, y, projectionContext) {
6901
6932
  const translatedY = y + projectionContext.translation[1];
6902
6933
  let projection;
6903
6934
  if (projectionContext.pitchWithMap) {
6904
- projection = projectWithMatrix(translatedX, translatedY, projectionContext.pitchedLabelPlaneMatrix, projectionContext.getElevation);
6935
+ projection = projectWithMatrix(translatedX, translatedY, projectionContext.pitchedLabelPlaneMatrix, elevationAt(projectionContext, translatedX, translatedY));
6905
6936
  projection.isOccluded = false;
6906
6937
  } else {
6907
- projection = projectionContext.transform.projectTileCoordinates(translatedX, translatedY, projectionContext.unwrappedTileID, projectionContext.getElevation);
6938
+ projection = projectionContext.transform.projectTileCoordinates(translatedX, translatedY, projectionContext.unwrappedTileID, elevationAt(projectionContext, translatedX, translatedY));
6908
6939
  projection.point.x = (projection.point.x * .5 + .5) * projectionContext.width;
6909
6940
  projection.point.y = (-projection.point.y * .5 + .5) * projectionContext.height;
6910
6941
  }
@@ -6919,7 +6950,9 @@ function projectFromLabelPlaneToClipSpace(x, y, projectionContext, pitchedLabelP
6919
6950
  1
6920
6951
  ];
6921
6952
  transformMat4(pos, pos, pitchedLabelPlaneMatrixInverse);
6922
- return projectionContext.transform.projectTileCoordinates(pos[0] / pos[3], pos[1] / pos[3], projectionContext.unwrappedTileID, projectionContext.getElevation).point;
6953
+ const tileX = pos[0] / pos[3];
6954
+ const tileY = pos[1] / pos[3];
6955
+ return projectionContext.transform.projectTileCoordinates(tileX, tileY, projectionContext.unwrappedTileID, elevationAt(projectionContext, tileX, tileY)).point;
6923
6956
  } else return {
6924
6957
  x: x / projectionContext.width * 2 - 1,
6925
6958
  y: 1 - y / projectionContext.height * 2
@@ -6929,7 +6962,7 @@ function projectFromLabelPlaneToClipSpace(x, y, projectionContext, pitchedLabelP
6929
6962
  * Projects the given point in tile coordinates to the GL clip space (-1..1).
6930
6963
  */
6931
6964
  function projectTileCoordinatesToClipSpace(x, y, projectionContext) {
6932
- return projectionContext.transform.projectTileCoordinates(x, y, projectionContext.unwrappedTileID, projectionContext.getElevation);
6965
+ return projectionContext.transform.projectTileCoordinates(x, y, projectionContext.unwrappedTileID, elevationAt(projectionContext, x, y));
6933
6966
  }
6934
6967
  /**
6935
6968
  * Calculate the normal vector for a line segment
@@ -7070,8 +7103,8 @@ function projectPathSpecialProjection(projectedPath, projectionContext) {
7070
7103
  const inverseLabelPlaneMatrix = tmpMat4;
7071
7104
  fastInvertSkewMat4(inverseLabelPlaneMatrix, projectionContext.pitchedLabelPlaneMatrix);
7072
7105
  return projectedPath.map((p) => {
7073
- const backProjected = projectWithMatrix(p.x, p.y, inverseLabelPlaneMatrix, projectionContext.getElevation);
7074
- const projected = projectionContext.transform.projectTileCoordinates(backProjected.point.x, backProjected.point.y, projectionContext.unwrappedTileID, projectionContext.getElevation);
7106
+ const backProjected = projectWithMatrix(p.x, p.y, inverseLabelPlaneMatrix, elevationAt(projectionContext, p.x, p.y));
7107
+ const projected = projectionContext.transform.projectTileCoordinates(backProjected.point.x, backProjected.point.y, projectionContext.unwrappedTileID, elevationAt(projectionContext, backProjected.point.x, backProjected.point.y));
7075
7108
  projected.point.x = (projected.point.x * .5 + .5) * projectionContext.width;
7076
7109
  projected.point.y = (-projected.point.y * .5 + .5) * projectionContext.height;
7077
7110
  return projected;
@@ -7122,10 +7155,10 @@ var CollisionIndex = class {
7122
7155
  this.gridBottomBoundary = transform.height + 200;
7123
7156
  this.perspectiveRatioCutoff = .6;
7124
7157
  }
7125
- placeCollisionBox(collisionBox, overlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translation, collisionGroupPredicate, getElevation, shift, simpleProjectionMatrix) {
7158
+ placeCollisionBox(collisionBox, overlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translation, collisionGroupPredicate, getElevation, shift, simpleProjectionMatrix, heightOffset = 0, heightAnchorGround = true) {
7126
7159
  const x = collisionBox.anchorPointX + translation[0];
7127
7160
  const y = collisionBox.anchorPointY + translation[1];
7128
- const projectedPoint = this.projectAndGetPerspectiveRatio(x, y, unwrappedTileID, getElevation, simpleProjectionMatrix);
7161
+ const projectedPoint = this.projectAndGetPerspectiveRatio(x, y, unwrappedTileID, getSymbolElevation(getElevation, x, y, heightOffset, heightAnchorGround), simpleProjectionMatrix);
7129
7162
  const tileToViewport = textPixelRatio * projectedPoint.perspectiveRatio;
7130
7163
  let projectedBox;
7131
7164
  if (!pitchWithMap && !rotateWithMap) {
@@ -7140,7 +7173,7 @@ var CollisionIndex = class {
7140
7173
  pointY + collisionBox.y2 * tileToViewport
7141
7174
  ]
7142
7175
  };
7143
- } else projectedBox = this._projectCollisionBox(collisionBox, tileToViewport, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translation, projectedPoint, getElevation, shift, simpleProjectionMatrix);
7176
+ } else projectedBox = this._projectCollisionBox(collisionBox, tileToViewport, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translation, projectedPoint, getElevation, shift, simpleProjectionMatrix, heightOffset, heightAnchorGround);
7144
7177
  const [tlX, tlY, brX, brY] = projectedBox.box;
7145
7178
  const occluded = pitchWithMap ? projectedBox.allPointsOccluded : projectedPoint.isOccluded;
7146
7179
  let unplaceable = occluded;
@@ -7172,7 +7205,7 @@ var CollisionIndex = class {
7172
7205
  placeCollisionCircles(overlapMode, symbol, lineVertexArray, glyphOffsetArray, fontSize, unwrappedTileID, pitchedLabelPlaneMatrix, showCollisionCircles, pitchWithMap, collisionGroupPredicate, circlePixelDiameter, textPixelPadding, translation, getElevation) {
7173
7206
  const placedCollisionCircles = [];
7174
7207
  const tileUnitAnchorPoint = new Point(symbol.anchorX, symbol.anchorY);
7175
- const perspectiveRatio = this.getPerspectiveRatio(tileUnitAnchorPoint.x, tileUnitAnchorPoint.y, unwrappedTileID, getElevation);
7208
+ const perspectiveRatio = this.getPerspectiveRatio(tileUnitAnchorPoint.x, tileUnitAnchorPoint.y, unwrappedTileID, getElevation?.(tileUnitAnchorPoint.x, tileUnitAnchorPoint.y));
7176
7209
  const labelPlaneFontScale = (pitchWithMap ? fontSize * this.transform.getPitchedTextCorrection(symbol.anchorX, symbol.anchorY, unwrappedTileID) / perspectiveRatio : fontSize * perspectiveRatio) / 24;
7177
7210
  const projectionCache = {
7178
7211
  projections: {},
@@ -7321,14 +7354,14 @@ var CollisionIndex = class {
7321
7354
  };
7322
7355
  for (let k = 0; k < collisionCircles.length; k += 4) grid.insertCircle(key, collisionCircles[k], collisionCircles[k + 1], collisionCircles[k + 2]);
7323
7356
  }
7324
- projectAndGetPerspectiveRatio(x, y, unwrappedTileID, getElevation, simpleProjectionMatrix) {
7357
+ projectAndGetPerspectiveRatio(x, y, unwrappedTileID, elevation, simpleProjectionMatrix) {
7325
7358
  if (simpleProjectionMatrix) {
7326
7359
  let pos;
7327
- if (getElevation) {
7360
+ if (elevation != null) {
7328
7361
  pos = [
7329
7362
  x,
7330
7363
  y,
7331
- getElevation(x, y),
7364
+ elevation,
7332
7365
  1
7333
7366
  ];
7334
7367
  transformMat4(pos, pos, simpleProjectionMatrix);
@@ -7350,7 +7383,7 @@ var CollisionIndex = class {
7350
7383
  signedDistanceFromCamera: w
7351
7384
  };
7352
7385
  } else {
7353
- const projected = this.transform.projectTileCoordinates(x, y, unwrappedTileID, getElevation);
7386
+ const projected = this.transform.projectTileCoordinates(x, y, unwrappedTileID, elevation);
7354
7387
  return {
7355
7388
  x: (projected.point.x + 1) / 2 * this.transform.width + 100,
7356
7389
  y: (-projected.point.y + 1) / 2 * this.transform.height + 100,
@@ -7360,8 +7393,8 @@ var CollisionIndex = class {
7360
7393
  };
7361
7394
  }
7362
7395
  }
7363
- getPerspectiveRatio(x, y, unwrappedTileID, getElevation) {
7364
- const projected = this.transform.projectTileCoordinates(x, y, unwrappedTileID, getElevation);
7396
+ getPerspectiveRatio(x, y, unwrappedTileID, elevation) {
7397
+ const projected = this.transform.projectTileCoordinates(x, y, unwrappedTileID, elevation);
7365
7398
  return .5 + .5 * (this.transform.cameraToCenterDistance / projected.signedDistanceFromCamera);
7366
7399
  }
7367
7400
  isOffscreen(x1, y1, x2, y2) {
@@ -7382,7 +7415,7 @@ var CollisionIndex = class {
7382
7415
  /**
7383
7416
  * Applies all layout+paint properties of the given box in order to find as good approximation of its screen-space bounding box as possible.
7384
7417
  */
7385
- _projectCollisionBox(collisionBox, tileToViewport, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translation, projectedPoint, getElevation, shift, simpleProjectionMatrix) {
7418
+ _projectCollisionBox(collisionBox, tileToViewport, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translation, projectedPoint, getElevation, shift, simpleProjectionMatrix, heightOffset = 0, heightAnchorGround = true) {
7386
7419
  let vecEastX = 1;
7387
7420
  let vecEastY = 0;
7388
7421
  let vecSouthX = 0;
@@ -7390,7 +7423,7 @@ var CollisionIndex = class {
7390
7423
  const translatedAnchorX = collisionBox.anchorPointX + translation[0];
7391
7424
  const translatedAnchorY = collisionBox.anchorPointY + translation[1];
7392
7425
  if (rotateWithMap && !pitchWithMap) {
7393
- const projectedEast = this.projectAndGetPerspectiveRatio(translatedAnchorX + 1, translatedAnchorY, unwrappedTileID, getElevation, simpleProjectionMatrix);
7426
+ const projectedEast = this.projectAndGetPerspectiveRatio(translatedAnchorX + 1, translatedAnchorY, unwrappedTileID, getSymbolElevation(getElevation, translatedAnchorX + 1, translatedAnchorY, heightOffset, heightAnchorGround), simpleProjectionMatrix);
7394
7427
  const toEastX = projectedEast.x - projectedPoint.x;
7395
7428
  const toEastY = projectedEast.y - projectedPoint.y;
7396
7429
  const angle = Math.atan(toEastY / toEastX) + (toEastX < 0 ? Math.PI : 0);
@@ -7470,7 +7503,7 @@ var CollisionIndex = class {
7470
7503
  for (const { offsetX, offsetY } of offsetsArray) points.push(new Point(basePointX + vecEastX * offsetX + vecSouthX * offsetY, basePointY + vecEastY * offsetX + vecSouthY * offsetY));
7471
7504
  let anyPointVisible = false;
7472
7505
  if (pitchWithMap) {
7473
- const projected = points.map((p) => this.projectAndGetPerspectiveRatio(p.x, p.y, unwrappedTileID, getElevation, simpleProjectionMatrix));
7506
+ const projected = points.map((p) => this.projectAndGetPerspectiveRatio(p.x, p.y, unwrappedTileID, getSymbolElevation(getElevation, p.x, p.y, heightOffset, heightAnchorGround), simpleProjectionMatrix));
7474
7507
  anyPointVisible = projected.some((p) => !p.isOccluded);
7475
7508
  points = projected.map((p) => new Point(p.x, p.y));
7476
7509
  } else anyPointVisible = true;
@@ -7568,7 +7601,7 @@ var Placement = class {
7568
7601
  }
7569
7602
  _getTerrainElevationFunc(tileID) {
7570
7603
  const terrain = this.terrain;
7571
- if (!terrain) return null;
7604
+ if (!terrain) return void 0;
7572
7605
  return (x, y) => terrain.getElevation(tileID, x, y);
7573
7606
  }
7574
7607
  getBucketParts(results, styleLayer, tile, sortAcrossTiles) {
@@ -7616,13 +7649,13 @@ var Placement = class {
7616
7649
  parameters
7617
7650
  });
7618
7651
  }
7619
- attemptAnchorPlacement(textAnchorOffset, textBox, width, height, textBoxScale, rotateWithMap, pitchWithMap, textPixelRatio, tileID, unwrappedTileID, collisionGroup, textOverlapMode, symbolInstance, bucket, orientation, translationText, translationIcon, iconBox, getElevation, simpleProjectionMatrix) {
7652
+ attemptAnchorPlacement(textAnchorOffset, textBox, width, height, textBoxScale, rotateWithMap, pitchWithMap, textPixelRatio, tileID, unwrappedTileID, collisionGroup, textOverlapMode, symbolInstance, bucket, orientation, translationText, translationIcon, iconBox, getElevation, simpleProjectionMatrix, heightOffset, heightAnchorGround) {
7620
7653
  const anchor = TextAnchorEnum[textAnchorOffset.textAnchor];
7621
7654
  const textOffset = [textAnchorOffset.textOffset0, textAnchorOffset.textOffset1];
7622
7655
  const shift = calculateVariableLayoutShift(anchor, width, height, textOffset, textBoxScale);
7623
- const placedGlyphBoxes = this.collisionIndex.placeCollisionBox(textBox, textOverlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationText, collisionGroup.predicate, getElevation, shift, simpleProjectionMatrix);
7656
+ const placedGlyphBoxes = this.collisionIndex.placeCollisionBox(textBox, textOverlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationText, collisionGroup.predicate, getElevation, shift, simpleProjectionMatrix, heightOffset, heightAnchorGround);
7624
7657
  if (iconBox) {
7625
- if (!this.collisionIndex.placeCollisionBox(iconBox, textOverlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationIcon, collisionGroup.predicate, getElevation, shift, simpleProjectionMatrix).placeable) return;
7658
+ if (!this.collisionIndex.placeCollisionBox(iconBox, textOverlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationIcon, collisionGroup.predicate, getElevation, shift, simpleProjectionMatrix, heightOffset, heightAnchorGround).placeable) return;
7626
7659
  }
7627
7660
  if (placedGlyphBoxes.placeable) {
7628
7661
  let prevAnchor;
@@ -7659,6 +7692,7 @@ var Placement = class {
7659
7692
  const pitchWithMap = layout.get("text-pitch-alignment") === "map";
7660
7693
  const hasIconTextFit = layout.get("icon-text-fit") !== "none";
7661
7694
  const zOrderByViewportY = layout.get("symbol-z-order") === "viewport-y";
7695
+ const heightAnchorGround = layout.get("symbol-height-anchor") === "ground";
7662
7696
  const alwaysShowText = textAlwaysOverlap && (iconAlwaysOverlap || !bucket.hasIconData() || iconOptional);
7663
7697
  const alwaysShowIcon = iconAlwaysOverlap && (textAlwaysOverlap || !bucket.hasTextData() || textOptional);
7664
7698
  if (!bucket.collisionArrays && collisionBoxArray) bucket.deserializeCollisionBoxes(collisionBoxArray);
@@ -7671,6 +7705,7 @@ var Placement = class {
7671
7705
  this.placements[symbolInstance.crossTileID] = new JointPlacement(false, false, false);
7672
7706
  return;
7673
7707
  }
7708
+ const symbolHeightOffset = symbolInstance.heightOffset;
7674
7709
  let placeText = false;
7675
7710
  let placeIcon = false;
7676
7711
  let offscreen = true;
@@ -7723,7 +7758,7 @@ var Placement = class {
7723
7758
  const textAnchorOffsetEnd = symbolInstance.textAnchorOffsetEndIndex;
7724
7759
  if (textAnchorOffsetEnd === textAnchorOffsetStart) {
7725
7760
  const placeBox = (collisionTextBox, orientation) => {
7726
- const placedFeature = this.collisionIndex.placeCollisionBox(collisionTextBox, textOverlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationText, collisionGroup.predicate, getElevation, void 0, simpleProjectionMatrix);
7761
+ const placedFeature = this.collisionIndex.placeCollisionBox(collisionTextBox, textOverlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationText, collisionGroup.predicate, getElevation, void 0, simpleProjectionMatrix, symbolHeightOffset, heightAnchorGround);
7727
7762
  if (placedFeature?.placeable) {
7728
7763
  this.markUsedOrientation(bucket, orientation, symbolInstance);
7729
7764
  this.placedOrientations[symbolInstance.crossTileID] = orientation;
@@ -7758,7 +7793,7 @@ var Placement = class {
7758
7793
  for (let i = textAnchorOffsetStart; i < textAnchorOffsetEnd; i++) {
7759
7794
  const textAnchorOffset = bucket.textAnchorOffsets.get(i);
7760
7795
  if (prevAnchor && textAnchorOffset.textAnchor !== prevAnchor) continue;
7761
- const result = this.attemptAnchorPlacement(textAnchorOffset, collisionTextBox, width, height, textBoxScale, rotateWithMap, pitchWithMap, textPixelRatio, tileID, unwrappedTileID, collisionGroup, overlapMode, symbolInstance, bucket, orientation, translationText, translationIcon, variableIconBox, getElevation);
7796
+ const result = this.attemptAnchorPlacement(textAnchorOffset, collisionTextBox, width, height, textBoxScale, rotateWithMap, pitchWithMap, textPixelRatio, tileID, unwrappedTileID, collisionGroup, overlapMode, symbolInstance, bucket, orientation, translationText, translationIcon, variableIconBox, getElevation, simpleProjectionMatrix, symbolHeightOffset, heightAnchorGround);
7762
7797
  if (result) {
7763
7798
  placedBox = result.placedGlyphBoxes;
7764
7799
  if (placedBox?.placeable) {
@@ -7772,7 +7807,7 @@ var Placement = class {
7772
7807
  else overlapMode = textOverlapMode;
7773
7808
  }
7774
7809
  if (showCollisionBoxes && !placedBox) placedBox = {
7775
- box: this.collisionIndex.placeCollisionBox(textBox, "always", textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationText, collisionGroup.predicate, getElevation, void 0, simpleProjectionMatrix).box,
7810
+ box: this.collisionIndex.placeCollisionBox(textBox, "always", textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationText, collisionGroup.predicate, getElevation, void 0, simpleProjectionMatrix, symbolHeightOffset, heightAnchorGround).box,
7776
7811
  offscreen: false,
7777
7812
  placeable: false,
7778
7813
  occluded: false
@@ -7823,7 +7858,7 @@ var Placement = class {
7823
7858
  if (collisionArrays.iconFeatureIndex) iconFeatureIndex = collisionArrays.iconFeatureIndex;
7824
7859
  if (collisionArrays.iconBox) {
7825
7860
  const placeIconFeature = (iconBox) => {
7826
- return this.collisionIndex.placeCollisionBox(iconBox, iconOverlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationIcon, collisionGroup.predicate, getElevation, hasIconTextFit && shift ? shift : void 0, simpleProjectionMatrix);
7861
+ return this.collisionIndex.placeCollisionBox(iconBox, iconOverlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationIcon, collisionGroup.predicate, getElevation, hasIconTextFit && shift ? shift : void 0, simpleProjectionMatrix, symbolHeightOffset, heightAnchorGround);
7827
7862
  };
7828
7863
  if (placedVerticalText && placedVerticalText.placeable && collisionArrays.verticalIconBox) {
7829
7864
  placedIconBoxes = placeIconFeature(collisionArrays.verticalIconBox);
@@ -8778,12 +8813,11 @@ const shaders = {
8778
8813
  lineGradientSDF: prepare("uniform lowp float u_device_pixel_ratio;uniform sampler2D u_image;uniform sampler2D u_image_dash;uniform float u_mix;uniform lowp float u_lineatlas_width;in vec2 v_normal;flat in vec2 v_width2;in vec2 v_tex_a;in vec2 v_tex_b;in float v_gamma_scale;in highp vec2 v_uv;\n#ifdef GLOBE\nin float v_depth;\n#endif\n#pragma maplibre: define lowp float blur\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define mediump float width\n#pragma maplibre: define lowp float floorwidth\n#pragma maplibre: define mediump vec4 dasharray_from\n#pragma maplibre: define mediump vec4 dasharray_to\nvoid main() {\n#pragma maplibre: initialize lowp float blur\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize mediump float width\n#pragma maplibre: initialize lowp float floorwidth\n#pragma maplibre: initialize mediump vec4 dasharray_from\n#pragma maplibre: initialize mediump vec4 dasharray_to\nclipAntimeridian();float dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);vec4 color=texture(u_image,v_uv);float sdfdist_a=texture(u_image_dash,v_tex_a).a;float sdfdist_b=texture(u_image_dash,v_tex_b).a;float sdfdist=mix(sdfdist_a,sdfdist_b,u_mix);float sdfgamma=(u_lineatlas_width/256.0)/min(dasharray_from.w,dasharray_to.w);float dash_alpha=smoothstep(0.5-sdfgamma/floorwidth,0.5+sdfgamma/floorwidth,sdfdist);fragColor=color*(alpha*dash_alpha*opacity);\n#ifdef GLOBE\nif (v_depth > 1.0) {discard;}\n#endif\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "\n#define scale 0.015873016\n#define LINE_DISTANCE_SCALE 2.0\nlayout(location=0) in ivec2 a_pos_normal;layout(location=1) in uvec4 a_data;layout(location=2) in float a_uv_x;layout(location=3) in float a_split_index;uniform vec2 u_translation;uniform mediump float u_ratio;uniform lowp float u_device_pixel_ratio;uniform vec2 u_units_to_pixels;uniform float u_image_height;uniform float u_tileratio;uniform float u_crossfade_from;uniform float u_crossfade_to;uniform float u_lineatlas_height;out vec2 v_normal;flat out vec2 v_width2;out float v_gamma_scale;out highp vec2 v_uv;out vec2 v_tex_a;out vec2 v_tex_b;\n#ifdef GLOBE\nout float v_depth;\n#endif\n#pragma maplibre: define lowp float blur\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define mediump float gapwidth\n#pragma maplibre: define lowp float offset\n#pragma maplibre: define mediump float width\n#pragma maplibre: define lowp float floorwidth\n#pragma maplibre: define mediump vec4 dasharray_from\n#pragma maplibre: define mediump vec4 dasharray_to\nvoid main() {\n#pragma maplibre: initialize lowp float blur\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize mediump float gapwidth\n#pragma maplibre: initialize lowp float offset\n#pragma maplibre: initialize mediump float width\n#pragma maplibre: initialize lowp float floorwidth\n#pragma maplibre: initialize mediump vec4 dasharray_from\n#pragma maplibre: initialize mediump vec4 dasharray_to\nif (opacity < 0.01) {gl_Position=vec4(-2.0,-2.0,-2.0,1.0);return;}float ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=vec2(ivec2(a_data.xy)-128);float a_direction=float(int(a_data.z & 3u)-1);float a_linesofar=float((a_data.z >> 2u)+a_data.w*64u)*LINE_DISTANCE_SCALE;float texel_height=1.0/u_image_height;float half_texel_height=0.5*texel_height;v_uv=vec2(a_uv_x,a_split_index*texel_height-half_texel_height);vec2 pos=vec2(a_pos_normal >> 1);mediump vec2 normal=vec2(a_pos_normal & 1);normal.y=normal.y*2.0-1.0;v_normal=normal;gapwidth=gapwidth/2.0;float halfwidth=width/2.0;offset=-1.0*offset;float inset=gapwidth+(gapwidth > 0.0 ? ANTIALIASING : 0.0);float outset=gapwidth+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*scale;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*scale*normal.y*mat2(t,-u,u,t);float adjustedThickness=projectLineThickness(pos.y);vec4 projected_no_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation);vec4 projected_with_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation+dist/u_ratio*adjustedThickness);gl_Position=projected_with_extrude;\n#ifdef GLOBE\nv_depth=gl_Position.z/gl_Position.w;\n#endif\n#ifdef TERRAIN3D\nv_gamma_scale=1.0;\n#else\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length((projected_with_extrude.xy-projected_no_extrude.xy)/projected_with_extrude.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\n#endif\nfloat u_patternscale_a_x=u_tileratio/dasharray_from.w/u_crossfade_from;float u_patternscale_a_y=-dasharray_from.z/2.0/u_lineatlas_height;float u_patternscale_b_x=u_tileratio/dasharray_to.w/u_crossfade_to;float u_patternscale_b_y=-dasharray_to.z/2.0/u_lineatlas_height;v_tex_a=vec2(a_linesofar*u_patternscale_a_x/floorwidth,normal.y*u_patternscale_a_y+(float(dasharray_from.y)+0.5)/u_lineatlas_height);v_tex_b=vec2(a_linesofar*u_patternscale_b_x/floorwidth,normal.y*u_patternscale_b_y+(float(dasharray_to.y)+0.5)/u_lineatlas_height);v_width2=vec2(outset,inset);}"),
8779
8814
  layerOpacity: prepare("uniform sampler2D u_image;uniform float u_opacity;in vec2 v_pos;void main() {fragColor=texture(u_image,v_pos)*u_opacity;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(0.0);\n#endif\n}", "layout(location=0) in vec2 a_pos;out vec2 v_pos;void main() {gl_Position=vec4(a_pos.x*2.0-1.0,1.0-a_pos.y*2.0,0.0,1.0);v_pos.x=a_pos.x;v_pos.y=1.0-a_pos.y;}"),
8780
8815
  raster: prepare("uniform float u_fade_t;uniform float u_opacity;uniform sampler2D u_image0;uniform sampler2D u_image1;in vec3 v_pos0;in vec3 v_pos1;uniform float u_brightness_low;uniform float u_brightness_high;uniform float u_saturation_factor;uniform float u_contrast_factor;uniform vec3 u_spin_weights;void main() {vec4 color0=texture(u_image0,v_pos0.xy/v_pos0.z);vec4 color1=texture(u_image1,v_pos1.xy/v_pos1.z);if (color0.a > 0.0) {color0.rgb=color0.rgb/color0.a;}if (color1.a > 0.0) {color1.rgb=color1.rgb/color1.a;}vec4 color=mix(color0,color1,u_fade_t);color.a*=u_opacity;vec3 rgb=color.rgb;rgb=vec3(dot(rgb,u_spin_weights.xyz),dot(rgb,u_spin_weights.zxy),dot(rgb,u_spin_weights.yzx));float average=(color.r+color.g+color.b)/3.0;rgb+=(average-rgb)*u_saturation_factor;rgb=(rgb-0.5)*u_contrast_factor+0.5;vec3 u_high_vec=vec3(u_brightness_low,u_brightness_low,u_brightness_low);vec3 u_low_vec=vec3(u_brightness_high,u_brightness_high,u_brightness_high);fragColor=vec4(mix(u_high_vec,u_low_vec,rgb)*color.a,color.a);\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "uniform vec2 u_tl_parent;uniform float u_scale_parent;uniform float u_buffer_scale;uniform vec3 u_image_warp;uniform vec4 u_coords_top;uniform vec4 u_coords_bottom;layout(location=0) in vec2 a_pos;out vec3 v_pos0;out vec3 v_pos1;void main() {vec2 fractionalPos=a_pos/8192.0;vec2 topLeft=u_coords_top.xy;vec2 topRight=u_coords_top.zw;vec2 bottomLeft=u_coords_bottom.xy;vec2 bottomRight=u_coords_bottom.zw;vec2 bilinearPos=mix(mix(topLeft,topRight,fractionalPos.x),mix(bottomLeft,bottomRight,fractionalPos.x),fractionalPos.y);float denominator=dot(u_image_warp.xy,fractionalPos)+1.0;vec2 acrossTop=topRight-topLeft+u_image_warp.x*topRight;vec2 downLeft=bottomLeft-topLeft+u_image_warp.y*bottomLeft;vec2 projectivePos=(acrossTop*fractionalPos.x+downLeft*fractionalPos.y+topLeft)/denominator;vec2 position=mix(projectivePos,bilinearPos,u_image_warp.z);gl_Position=projectTile(position,position);vec2 texturePos=((fractionalPos-0.5)/u_buffer_scale)+0.5;\n#ifdef GLOBE\nif (a_pos.y <-32767.5) {texturePos.y=0.0;}if (a_pos.y > 32766.5) {texturePos.y=1.0;}\n#endif\nfloat perspectiveRatio=mix(1.0/denominator,1.0,u_image_warp.z);v_pos0=vec3(texturePos*perspectiveRatio,perspectiveRatio);vec2 parentPos=(texturePos*u_scale_parent)+u_tl_parent;v_pos1=vec3(parentPos*perspectiveRatio,perspectiveRatio);}"),
8781
- symbolIcon: prepare("uniform sampler2D u_texture;in vec2 v_tex;flat in float v_total_opacity;void main() {fragColor=texture(u_texture,v_tex)*v_total_opacity;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "layout(location=0) in vec4 a_pos_offset;layout(location=1) in uvec4 a_data;layout(location=2) in vec4 a_pixeloffset;layout(location=3) in vec3 a_projected_pos;layout(location=4) in uint a_fade_opacity;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform highp float u_camera_to_center_distance;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform float u_fade_change;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform vec2 u_texsize;uniform bool u_is_along_line;uniform bool u_is_variable_anchor;uniform vec2 u_translation;uniform float u_pitched_scale;uniform bool u_is_offset;out vec2 v_tex;flat out float v_total_opacity;\n#pragma maplibre: define lowp float opacity\nvoid main() {\n#pragma maplibre: initialize lowp float opacity\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=vec2(a_data.xy);vec2 a_size=vec2(a_data.zw);float a_size_min=float(a_data.z >> 1u);vec2 a_pxoffset=a_pixeloffset.xy;vec2 a_minFontScale=a_pixeloffset.zw/256.0;float ele=get_elevation(a_pos);highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec2 translated_a_pos=a_pos+u_translation;vec4 projectedPoint=projectTileWithElevation(translated_a_pos,ele);vec2 fade_opacity=unpack_opacity(a_fade_opacity);float fade_change=fade_opacity[1] > 0.5 ? u_fade_change :-u_fade_change;float visibility=calculate_visibility(projectedPoint);v_total_opacity=opacity*max(0.0,min(visibility,fade_opacity[0]+fade_change));if (v_total_opacity < 0.1){gl_Position=vec4(-2.,-2.,-2.,1.);return;}highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\ncamera_to_anchor_distance/u_camera_to_center_distance :\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(0.5+0.5*distance_ratio,0.0,4.0);if (!u_is_offset) {size*=perspective_ratio;}float fontScale=u_is_text ? size/24.0 : size;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=projectTileWithElevation(translated_a_pos+vec2(1,0),ele);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos;if (u_is_along_line || u_is_variable_anchor) {projected_pos=vec4(a_projected_pos.xy,ele,1.0);} else if (u_pitch_with_map) {projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy+u_translation,ele,1.0);} else {projected_pos=u_label_plane_matrix*projectTileWithElevation(a_projected_pos.xy+u_translation,ele);}float z=float(u_pitch_with_map)*projected_pos.z/projected_pos.w;float projectionScaling=1.0;\n#ifdef GLOBE\nif(u_pitch_with_map) {float anchor_pos_tile_y=(u_coord_matrix*vec4(projected_pos.xy/projected_pos.w,z,1.0)).y;projectionScaling=mix(projectionScaling,1.0/circumferenceRatioAtTileY(anchor_pos_tile_y)*u_pitched_scale,u_projection_transition);}\n#endif\nvec4 finalPos=u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+rotation_matrix*(a_offset/32.0*max(a_minFontScale,fontScale)+a_pxoffset/16.0)*projectionScaling,z,1.0);if(u_pitch_with_map) {finalPos=projectTileWithElevation(finalPos.xy,finalPos.z);}gl_Position=finalPos;v_tex=a_tex/u_texsize;}"),
8782
- symbolSDF: prepare("#define SDF_PX 8.0\nuniform bool u_is_halo;uniform bool u_is_plain;uniform sampler2D u_texture;uniform highp float u_gamma_scale;uniform lowp float u_device_pixel_ratio;uniform bool u_is_text;in vec2 v_data0;in vec3 v_data1;\n#pragma maplibre: define highp vec4 fill_color\n#pragma maplibre: define highp vec4 halo_color\n#pragma maplibre: define lowp float halo_width\n#pragma maplibre: define lowp float halo_blur\nvoid main() {\n#pragma maplibre: initialize highp vec4 fill_color\n#pragma maplibre: initialize highp vec4 halo_color\n#pragma maplibre: initialize lowp float halo_width\n#pragma maplibre: initialize lowp float halo_blur\nfloat EDGE_GAMMA=0.105/u_device_pixel_ratio;vec2 tex=v_data0.xy;float gamma_scale=v_data1.x;float size=v_data1.y;float total_opacity=v_data1[2];float fontScale=u_is_text ? size/24.0 : size;highp float gamma=EDGE_GAMMA/(fontScale*u_gamma_scale);lowp float inner_edge=(256.0-64.0)/256.0;lowp float dist=texture(u_texture,tex).a;lowp vec4 color_alpha_out_text,color_alpha_out_halo;if (u_is_plain){highp float gamma_scaled=gamma*gamma_scale;highp float alpha=smoothstep(inner_edge-gamma_scaled,inner_edge+gamma_scaled,dist);color_alpha_out_text=total_opacity*alpha*fill_color;}if (u_is_halo) {float gamma_halo=(halo_blur*1.19/SDF_PX+EDGE_GAMMA)/(fontScale*u_gamma_scale);float inner_edge_halo=inner_edge+gamma_halo*gamma_scale;highp float gamma_scaled_halo=gamma_halo*gamma_scale;highp float alpha_halo=smoothstep(inner_edge_halo-gamma_scaled_halo,inner_edge_halo+gamma_scaled_halo,dist);highp float halo_edge=(6.0-halo_width/fontScale)/SDF_PX;alpha_halo= min(smoothstep(halo_edge-gamma_scaled_halo,halo_edge+gamma_scaled_halo,dist),1.0-alpha_halo);color_alpha_out_halo=total_opacity*alpha_halo*halo_color;}if (u_is_plain && u_is_halo) {fragColor=color_alpha_out_text+(1.-color_alpha_out_text.a)*color_alpha_out_halo;} else if (u_is_halo){fragColor=color_alpha_out_halo;} else {fragColor=color_alpha_out_text;}\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "layout(location=0) in vec4 a_pos_offset;layout(location=1) in uvec4 a_data;layout(location=2) in vec4 a_pixeloffset;layout(location=3) in vec3 a_projected_pos;layout(location=4) in uint a_fade_opacity;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform bool u_is_along_line;uniform bool u_is_variable_anchor;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform highp float u_camera_to_center_distance;uniform float u_fade_change;uniform vec2 u_texsize;uniform vec2 u_translation;uniform float u_pitched_scale;uniform bool u_is_offset;out vec2 v_data0;out vec3 v_data1;\n#pragma maplibre: define highp vec4 fill_color\n#pragma maplibre: define highp vec4 halo_color\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define lowp float halo_width\n#pragma maplibre: define lowp float halo_blur\nvoid main() {\n#pragma maplibre: initialize highp vec4 fill_color\n#pragma maplibre: initialize highp vec4 halo_color\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize lowp float halo_width\n#pragma maplibre: initialize lowp float halo_blur\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=vec2(a_data.xy);vec2 a_size=vec2(a_data.zw);float a_size_min=float(a_data.z >> 1u);vec2 a_pxoffset=a_pixeloffset.xy/16.0;vec2 a_minFontScale=a_pixeloffset.zw/256.0;float ele=get_elevation(a_pos);highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec2 translated_a_pos=a_pos+u_translation;vec4 projectedPoint=projectTileWithElevation(translated_a_pos,ele);vec2 fade_opacity=unpack_opacity(a_fade_opacity);float visibility=calculate_visibility(projectedPoint);float fade_change=fade_opacity[1] > 0.5 ? u_fade_change :-u_fade_change;float interpolated_fade_opacity=max(0.0,min(visibility,fade_opacity[0]+fade_change));float total_opacity=opacity*interpolated_fade_opacity;if (total_opacity < 0.1){gl_Position=vec4(-2.,-2.,-2.,1.);return;}highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\ncamera_to_anchor_distance/u_camera_to_center_distance :\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(0.5+0.5*distance_ratio,0.0,4.0);if (!u_is_offset) {size*=perspective_ratio;}float fontScale=u_is_text ? size/24.0 : size;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=projectTileWithElevation(translated_a_pos+vec2(1,0),ele);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos;if (u_is_along_line || u_is_variable_anchor) {projected_pos=vec4(a_projected_pos.xy,ele,1.0);} else if (u_pitch_with_map) {projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy+u_translation,ele,1.0);} else {projected_pos=u_label_plane_matrix*projectTileWithElevation(a_projected_pos.xy+u_translation,ele);}float z=float(u_pitch_with_map)*projected_pos.z/projected_pos.w;float projectionScaling=1.0;\n#ifdef GLOBE\nif(u_pitch_with_map) {float anchor_pos_tile_y=(u_coord_matrix*vec4(projected_pos.xy/projected_pos.w,z,1.0)).y;projectionScaling=mix(projectionScaling,1.0/circumferenceRatioAtTileY(anchor_pos_tile_y)*u_pitched_scale,u_projection_transition);}\n#endif\nvec4 finalPos=u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+rotation_matrix*(a_offset/32.0*max(a_minFontScale,fontScale)+a_pxoffset)*projectionScaling,z,1.0);if(u_pitch_with_map) {finalPos=projectTileWithElevation(finalPos.xy,finalPos.z);}float gamma_scale=finalPos.w;gl_Position=finalPos;v_data0=a_tex/u_texsize;v_data1=vec3(gamma_scale,size,total_opacity);}"),
8783
- symbolTextAndIcon: prepare("#define SDF_PX 8.0\n#define SDF 1.0\n#define ICON 0.0\nuniform bool u_is_halo;uniform bool u_is_text;uniform sampler2D u_texture;uniform sampler2D u_texture_icon;uniform highp float u_gamma_scale;uniform lowp float u_device_pixel_ratio;in vec4 v_data0;in vec3 v_data1;flat in float v_is_sdf;\n#pragma maplibre: define highp vec4 fill_color\n#pragma maplibre: define highp vec4 halo_color\n#pragma maplibre: define lowp float halo_width\n#pragma maplibre: define lowp float halo_blur\nvoid main() {\n#pragma maplibre: initialize highp vec4 fill_color\n#pragma maplibre: initialize highp vec4 halo_color\n#pragma maplibre: initialize lowp float halo_width\n#pragma maplibre: initialize lowp float halo_blur\nfloat total_opacity=v_data1[2];if (v_is_sdf==ICON) {vec2 tex_icon=v_data0.zw;fragColor=texture(u_texture_icon,tex_icon)*total_opacity;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\nreturn;}vec2 tex=v_data0.xy;float EDGE_GAMMA=0.105/u_device_pixel_ratio;float gamma_scale=v_data1.x;float size=v_data1.y;float fontScale=size/24.0;highp float gamma=EDGE_GAMMA/(fontScale*u_gamma_scale);lowp float buff=(256.0-64.0)/256.0;lowp float dist=texture(u_texture,tex).a;lowp vec4 color_alpha_out,color_alpha_out_halo;if (u_is_text) {highp float gamma_scaled=gamma*gamma_scale;highp float alpha=smoothstep(buff-gamma_scaled,buff+gamma_scaled,dist);color_alpha_out=fill_color*(alpha*total_opacity);}if (u_is_halo) {highp float gamma_halo=(halo_blur*1.19/SDF_PX+EDGE_GAMMA)/(fontScale*u_gamma_scale);lowp float buff_halo=(6.0-halo_width/fontScale)/SDF_PX;highp float gamma_scaled_halo=gamma_halo*gamma_scale;highp float alpha_halo=smoothstep(buff_halo-gamma_scaled_halo,buff_halo+gamma_scaled_halo,dist);color_alpha_out_halo=halo_color*(alpha_halo*total_opacity);}if (u_is_text && u_is_halo) {fragColor=color_alpha_out+(1.-color_alpha_out.a)*color_alpha_out_halo;} else if (u_is_halo) {fragColor=color_alpha_out_halo;} else {fragColor=color_alpha_out;}\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "layout(location=0) in vec4 a_pos_offset;layout(location=1) in uvec4 a_data;layout(location=2) in vec3 a_projected_pos;layout(location=3) in uint a_fade_opacity;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform highp float u_camera_to_center_distance;uniform float u_fade_change;uniform vec2 u_texsize;uniform vec2 u_texsize_icon;uniform bool u_is_along_line;uniform bool u_is_variable_anchor;uniform vec2 u_translation;uniform float u_pitched_scale;uniform bool u_is_offset;out vec4 v_data0;out vec3 v_data1;flat out float v_is_sdf;\n#pragma maplibre: define highp vec4 fill_color\n#pragma maplibre: define highp vec4 halo_color\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define lowp float halo_width\n#pragma maplibre: define lowp float halo_blur\nvoid main() {\n#pragma maplibre: initialize highp vec4 fill_color\n#pragma maplibre: initialize highp vec4 halo_color\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize lowp float halo_width\n#pragma maplibre: initialize lowp float halo_blur\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=vec2(a_data.xy);vec2 a_size=vec2(a_data.zw);float a_size_min=float(a_data.z >> 1u);float is_sdf=float(a_data.z & 1u);float ele=get_elevation(a_pos);highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec2 translated_a_pos=a_pos+u_translation;vec4 projectedPoint=projectTileWithElevation(translated_a_pos,ele);vec2 fade_opacity=unpack_opacity(a_fade_opacity);float visibility=calculate_visibility(projectedPoint);float fade_change=fade_opacity[1] > 0.5 ? u_fade_change :-u_fade_change;float interpolated_fade_opacity=max(0.0,min(visibility,fade_opacity[0]+fade_change));float total_opacity=opacity*interpolated_fade_opacity;if (total_opacity < 0.1){gl_Position=vec4(-2.,-2.,-2.,1.);return;}highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\ncamera_to_anchor_distance/u_camera_to_center_distance :\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(0.5+0.5*distance_ratio,0.0,4.0);if (!u_is_offset) {size*=perspective_ratio;}float fontScale=size/24.0;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=projectTileWithElevation(translated_a_pos+vec2(1,0),ele);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos;if (u_is_along_line || u_is_variable_anchor) {projected_pos=vec4(a_projected_pos.xy,ele,1.0);} else if (u_pitch_with_map) {projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy+u_translation,ele,1.0);} else {projected_pos=u_label_plane_matrix*projectTileWithElevation(a_projected_pos.xy+u_translation,ele);}float z=float(u_pitch_with_map)*projected_pos.z/projected_pos.w;float projectionScaling=1.0;\n#ifdef GLOBE\nif(u_pitch_with_map && !u_is_along_line) {float anchor_pos_tile_y=(u_coord_matrix*vec4(projected_pos.xy/projected_pos.w,z,1.0)).y;projectionScaling=mix(projectionScaling,1.0/circumferenceRatioAtTileY(anchor_pos_tile_y)*u_pitched_scale,u_projection_transition);}\n#endif\nvec4 finalPos=u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+rotation_matrix*(a_offset/32.0*fontScale)*projectionScaling,z,1.0);if(u_pitch_with_map) {finalPos=projectTileWithElevation(finalPos.xy,finalPos.z);}float gamma_scale=finalPos.w;gl_Position=finalPos;v_data0.xy=a_tex/u_texsize;v_data0.zw=a_tex/u_texsize_icon;v_data1=vec3(gamma_scale,size,total_opacity);v_is_sdf=is_sdf;}"),
8816
+ symbolIcon: prepare("uniform sampler2D u_texture;in vec2 v_tex;flat in float v_total_opacity;void main() {fragColor=texture(u_texture,v_tex)*v_total_opacity;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "layout(location=0) in vec4 a_pos_offset;layout(location=1) in uvec4 a_data;layout(location=2) in vec4 a_pixeloffset;layout(location=3) in vec3 a_projected_pos;layout(location=4) in uint a_fade_opacity;layout(location=5) in float a_height_offset;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform highp float u_camera_to_center_distance;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform float u_fade_change;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform vec2 u_texsize;uniform bool u_is_along_line;uniform bool u_is_variable_anchor;uniform vec2 u_translation;uniform float u_pitched_scale;uniform bool u_is_offset;uniform bool u_height_anchor_ground;out vec2 v_tex;flat out float v_total_opacity;\n#pragma maplibre: define lowp float opacity\nvoid main() {\n#pragma maplibre: initialize lowp float opacity\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=vec2(a_data.xy);vec2 a_size=vec2(a_data.zw);float a_size_min=float(a_data.z >> 1u);vec2 a_pxoffset=a_pixeloffset.xy;vec2 a_minFontScale=a_pixeloffset.zw/256.0;float ele=a_height_offset+(u_height_anchor_ground ? get_elevation(a_pos) : 0.0);highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec2 translated_a_pos=a_pos+u_translation;vec4 projectedPoint=projectTileWithElevation(translated_a_pos,ele);vec2 fade_opacity=unpack_opacity(a_fade_opacity);float fade_change=fade_opacity[1] > 0.5 ? u_fade_change :-u_fade_change;float visibility=calculate_visibility(projectedPoint);v_total_opacity=opacity*max(0.0,min(visibility,fade_opacity[0]+fade_change));if (v_total_opacity < 0.1){gl_Position=vec4(-2.,-2.,-2.,1.);return;}highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\ncamera_to_anchor_distance/u_camera_to_center_distance :\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(0.5+0.5*distance_ratio,0.0,4.0);if (!u_is_offset) {size*=perspective_ratio;}float fontScale=u_is_text ? size/24.0 : size;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=projectTileWithElevation(translated_a_pos+vec2(1,0),ele);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos;if (u_is_along_line || u_is_variable_anchor) {projected_pos=vec4(a_projected_pos.xy,ele,1.0);} else if (u_pitch_with_map) {projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy+u_translation,ele,1.0);} else {projected_pos=u_label_plane_matrix*projectTileWithElevation(a_projected_pos.xy+u_translation,ele);}float z=float(u_pitch_with_map)*projected_pos.z/projected_pos.w;float projectionScaling=1.0;\n#ifdef GLOBE\nif(u_pitch_with_map) {float anchor_pos_tile_y=(u_coord_matrix*vec4(projected_pos.xy/projected_pos.w,z,1.0)).y;projectionScaling=mix(projectionScaling,1.0/circumferenceRatioAtTileY(anchor_pos_tile_y)*u_pitched_scale,u_projection_transition);}\n#endif\nvec4 finalPos=u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+rotation_matrix*(a_offset/32.0*max(a_minFontScale,fontScale)+a_pxoffset/16.0)*projectionScaling,z,1.0);if(u_pitch_with_map) {finalPos=projectTileWithElevation(finalPos.xy,finalPos.z);}gl_Position=finalPos;v_tex=a_tex/u_texsize;}"),
8817
+ symbolSDF: prepare("#define SDF_PX 8.0\nuniform bool u_is_halo;uniform bool u_is_plain;uniform sampler2D u_texture;uniform highp float u_gamma_scale;uniform lowp float u_device_pixel_ratio;uniform bool u_is_text;in vec2 v_data0;in vec3 v_data1;\n#pragma maplibre: define highp vec4 fill_color\n#pragma maplibre: define highp vec4 halo_color\n#pragma maplibre: define lowp float halo_width\n#pragma maplibre: define lowp float halo_blur\nvoid main() {\n#pragma maplibre: initialize highp vec4 fill_color\n#pragma maplibre: initialize highp vec4 halo_color\n#pragma maplibre: initialize lowp float halo_width\n#pragma maplibre: initialize lowp float halo_blur\nfloat EDGE_GAMMA=0.105/u_device_pixel_ratio;vec2 tex=v_data0.xy;float gamma_scale=v_data1.x;float size=v_data1.y;float total_opacity=v_data1[2];float fontScale=u_is_text ? size/24.0 : size;highp float gamma=EDGE_GAMMA/(fontScale*u_gamma_scale);lowp float inner_edge=(256.0-64.0)/256.0;lowp float dist=texture(u_texture,tex).a;lowp vec4 color_alpha_out_text,color_alpha_out_halo;if (u_is_plain){highp float gamma_scaled=gamma*gamma_scale;highp float alpha=smoothstep(inner_edge-gamma_scaled,inner_edge+gamma_scaled,dist);color_alpha_out_text=total_opacity*alpha*fill_color;}if (u_is_halo) {float gamma_halo=(halo_blur*1.19/SDF_PX+EDGE_GAMMA)/(fontScale*u_gamma_scale);float inner_edge_halo=inner_edge+gamma_halo*gamma_scale;highp float gamma_scaled_halo=gamma_halo*gamma_scale;highp float alpha_halo=smoothstep(inner_edge_halo-gamma_scaled_halo,inner_edge_halo+gamma_scaled_halo,dist);highp float halo_edge=(6.0-halo_width/fontScale)/SDF_PX;alpha_halo= min(smoothstep(halo_edge-gamma_scaled_halo,halo_edge+gamma_scaled_halo,dist),1.0-alpha_halo);color_alpha_out_halo=total_opacity*alpha_halo*halo_color;}if (u_is_plain && u_is_halo) {fragColor=color_alpha_out_text+(1.-color_alpha_out_text.a)*color_alpha_out_halo;} else if (u_is_halo){fragColor=color_alpha_out_halo;} else {fragColor=color_alpha_out_text;}\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "layout(location=0) in vec4 a_pos_offset;layout(location=1) in uvec4 a_data;layout(location=2) in vec4 a_pixeloffset;layout(location=3) in vec3 a_projected_pos;layout(location=4) in uint a_fade_opacity;layout(location=5) in float a_height_offset;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform bool u_is_along_line;uniform bool u_is_variable_anchor;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform highp float u_camera_to_center_distance;uniform float u_fade_change;uniform vec2 u_texsize;uniform vec2 u_translation;uniform float u_pitched_scale;uniform bool u_is_offset;uniform bool u_height_anchor_ground;out vec2 v_data0;out vec3 v_data1;\n#pragma maplibre: define highp vec4 fill_color\n#pragma maplibre: define highp vec4 halo_color\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define lowp float halo_width\n#pragma maplibre: define lowp float halo_blur\nvoid main() {\n#pragma maplibre: initialize highp vec4 fill_color\n#pragma maplibre: initialize highp vec4 halo_color\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize lowp float halo_width\n#pragma maplibre: initialize lowp float halo_blur\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=vec2(a_data.xy);vec2 a_size=vec2(a_data.zw);float a_size_min=float(a_data.z >> 1u);vec2 a_pxoffset=a_pixeloffset.xy/16.0;vec2 a_minFontScale=a_pixeloffset.zw/256.0;float ele=a_height_offset+(u_height_anchor_ground ? get_elevation(a_pos) : 0.0);highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec2 translated_a_pos=a_pos+u_translation;vec4 projectedPoint=projectTileWithElevation(translated_a_pos,ele);vec2 fade_opacity=unpack_opacity(a_fade_opacity);float visibility=calculate_visibility(projectedPoint);float fade_change=fade_opacity[1] > 0.5 ? u_fade_change :-u_fade_change;float interpolated_fade_opacity=max(0.0,min(visibility,fade_opacity[0]+fade_change));float total_opacity=opacity*interpolated_fade_opacity;if (total_opacity < 0.1){gl_Position=vec4(-2.,-2.,-2.,1.);return;}highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\ncamera_to_anchor_distance/u_camera_to_center_distance :\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(0.5+0.5*distance_ratio,0.0,4.0);if (!u_is_offset) {size*=perspective_ratio;}float fontScale=u_is_text ? size/24.0 : size;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=projectTileWithElevation(translated_a_pos+vec2(1,0),ele);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos;if (u_is_along_line || u_is_variable_anchor) {projected_pos=vec4(a_projected_pos.xy,ele,1.0);} else if (u_pitch_with_map) {projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy+u_translation,ele,1.0);} else {projected_pos=u_label_plane_matrix*projectTileWithElevation(a_projected_pos.xy+u_translation,ele);}float z=float(u_pitch_with_map)*projected_pos.z/projected_pos.w;float projectionScaling=1.0;\n#ifdef GLOBE\nif(u_pitch_with_map) {float anchor_pos_tile_y=(u_coord_matrix*vec4(projected_pos.xy/projected_pos.w,z,1.0)).y;projectionScaling=mix(projectionScaling,1.0/circumferenceRatioAtTileY(anchor_pos_tile_y)*u_pitched_scale,u_projection_transition);}\n#endif\nvec4 finalPos=u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+rotation_matrix*(a_offset/32.0*max(a_minFontScale,fontScale)+a_pxoffset)*projectionScaling,z,1.0);if(u_pitch_with_map) {finalPos=projectTileWithElevation(finalPos.xy,finalPos.z);}float gamma_scale=finalPos.w;gl_Position=finalPos;v_data0=a_tex/u_texsize;v_data1=vec3(gamma_scale,size,total_opacity);}"),
8818
+ symbolTextAndIcon: prepare("#define SDF_PX 8.0\n#define SDF 1.0\n#define ICON 0.0\nuniform bool u_is_halo;uniform bool u_is_text;uniform sampler2D u_texture;uniform sampler2D u_texture_icon;uniform highp float u_gamma_scale;uniform lowp float u_device_pixel_ratio;in vec4 v_data0;in vec3 v_data1;flat in float v_is_sdf;\n#pragma maplibre: define highp vec4 fill_color\n#pragma maplibre: define highp vec4 halo_color\n#pragma maplibre: define lowp float halo_width\n#pragma maplibre: define lowp float halo_blur\nvoid main() {\n#pragma maplibre: initialize highp vec4 fill_color\n#pragma maplibre: initialize highp vec4 halo_color\n#pragma maplibre: initialize lowp float halo_width\n#pragma maplibre: initialize lowp float halo_blur\nfloat total_opacity=v_data1[2];if (v_is_sdf==ICON) {vec2 tex_icon=v_data0.zw;fragColor=texture(u_texture_icon,tex_icon)*total_opacity;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\nreturn;}vec2 tex=v_data0.xy;float EDGE_GAMMA=0.105/u_device_pixel_ratio;float gamma_scale=v_data1.x;float size=v_data1.y;float fontScale=size/24.0;highp float gamma=EDGE_GAMMA/(fontScale*u_gamma_scale);lowp float buff=(256.0-64.0)/256.0;lowp float dist=texture(u_texture,tex).a;lowp vec4 color_alpha_out,color_alpha_out_halo;if (u_is_text) {highp float gamma_scaled=gamma*gamma_scale;highp float alpha=smoothstep(buff-gamma_scaled,buff+gamma_scaled,dist);color_alpha_out=fill_color*(alpha*total_opacity);}if (u_is_halo) {highp float gamma_halo=(halo_blur*1.19/SDF_PX+EDGE_GAMMA)/(fontScale*u_gamma_scale);lowp float buff_halo=(6.0-halo_width/fontScale)/SDF_PX;highp float gamma_scaled_halo=gamma_halo*gamma_scale;highp float alpha_halo=smoothstep(buff_halo-gamma_scaled_halo,buff_halo+gamma_scaled_halo,dist);color_alpha_out_halo=halo_color*(alpha_halo*total_opacity);}if (u_is_text && u_is_halo) {fragColor=color_alpha_out+(1.-color_alpha_out.a)*color_alpha_out_halo;} else if (u_is_halo) {fragColor=color_alpha_out_halo;} else {fragColor=color_alpha_out;}\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "layout(location=0) in vec4 a_pos_offset;layout(location=1) in uvec4 a_data;layout(location=2) in vec3 a_projected_pos;layout(location=3) in uint a_fade_opacity;layout(location=4) in float a_height_offset;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform highp float u_camera_to_center_distance;uniform float u_fade_change;uniform vec2 u_texsize;uniform vec2 u_texsize_icon;uniform bool u_is_along_line;uniform bool u_is_variable_anchor;uniform vec2 u_translation;uniform float u_pitched_scale;uniform bool u_is_offset;uniform bool u_height_anchor_ground;out vec4 v_data0;out vec3 v_data1;flat out float v_is_sdf;\n#pragma maplibre: define highp vec4 fill_color\n#pragma maplibre: define highp vec4 halo_color\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define lowp float halo_width\n#pragma maplibre: define lowp float halo_blur\nvoid main() {\n#pragma maplibre: initialize highp vec4 fill_color\n#pragma maplibre: initialize highp vec4 halo_color\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize lowp float halo_width\n#pragma maplibre: initialize lowp float halo_blur\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=vec2(a_data.xy);vec2 a_size=vec2(a_data.zw);float a_size_min=float(a_data.z >> 1u);float is_sdf=float(a_data.z & 1u);float ele=a_height_offset+(u_height_anchor_ground ? get_elevation(a_pos) : 0.0);highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec2 translated_a_pos=a_pos+u_translation;vec4 projectedPoint=projectTileWithElevation(translated_a_pos,ele);vec2 fade_opacity=unpack_opacity(a_fade_opacity);float visibility=calculate_visibility(projectedPoint);float fade_change=fade_opacity[1] > 0.5 ? u_fade_change :-u_fade_change;float interpolated_fade_opacity=max(0.0,min(visibility,fade_opacity[0]+fade_change));float total_opacity=opacity*interpolated_fade_opacity;if (total_opacity < 0.1){gl_Position=vec4(-2.,-2.,-2.,1.);return;}highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\ncamera_to_anchor_distance/u_camera_to_center_distance :\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(0.5+0.5*distance_ratio,0.0,4.0);if (!u_is_offset) {size*=perspective_ratio;}float fontScale=size/24.0;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=projectTileWithElevation(translated_a_pos+vec2(1,0),ele);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos;if (u_is_along_line || u_is_variable_anchor) {projected_pos=vec4(a_projected_pos.xy,ele,1.0);} else if (u_pitch_with_map) {projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy+u_translation,ele,1.0);} else {projected_pos=u_label_plane_matrix*projectTileWithElevation(a_projected_pos.xy+u_translation,ele);}float z=float(u_pitch_with_map)*projected_pos.z/projected_pos.w;float projectionScaling=1.0;\n#ifdef GLOBE\nif(u_pitch_with_map && !u_is_along_line) {float anchor_pos_tile_y=(u_coord_matrix*vec4(projected_pos.xy/projected_pos.w,z,1.0)).y;projectionScaling=mix(projectionScaling,1.0/circumferenceRatioAtTileY(anchor_pos_tile_y)*u_pitched_scale,u_projection_transition);}\n#endif\nvec4 finalPos=u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+rotation_matrix*(a_offset/32.0*fontScale)*projectionScaling,z,1.0);if(u_pitch_with_map) {finalPos=projectTileWithElevation(finalPos.xy,finalPos.z);}float gamma_scale=finalPos.w;gl_Position=finalPos;v_data0.xy=a_tex/u_texsize;v_data0.zw=a_tex/u_texsize_icon;v_data1=vec3(gamma_scale,size,total_opacity);v_is_sdf=is_sdf;}"),
8784
8819
  terrain: prepare("uniform sampler2D u_texture;uniform vec4 u_fog_color;uniform vec4 u_horizon_color;uniform float u_fog_ground_blend;uniform float u_fog_ground_blend_opacity;uniform float u_horizon_fog_blend;uniform bool u_is_globe_mode;in vec2 v_texture_pos;in float v_fog_depth;const float gamma=2.2;vec4 gammaToLinear(vec4 color) {return pow(color,vec4(gamma));}vec4 linearToGamma(vec4 color) {return pow(color,vec4(1.0/gamma));}void main() {vec4 surface_color=texture(u_texture,vec2(v_texture_pos.x,1.0-v_texture_pos.y));if (!u_is_globe_mode && u_fog_ground_blend_opacity > 0.0 && v_fog_depth > u_fog_ground_blend) {vec4 surface_color_linear=gammaToLinear(surface_color);float blend_color=smoothstep(0.0,1.0,max((v_fog_depth-u_horizon_fog_blend)/(1.0-u_horizon_fog_blend),0.0));vec4 fog_horizon_color_linear=mix(gammaToLinear(u_fog_color),gammaToLinear(u_horizon_color),blend_color);float factor_fog=max(v_fog_depth-u_fog_ground_blend,0.0)/(1.0-u_fog_ground_blend);fragColor=linearToGamma(mix(surface_color_linear,fog_horizon_color_linear,pow(factor_fog,2.0)*u_fog_ground_blend_opacity));} else {fragColor=surface_color;}}", "layout(location=0) in vec3 a_pos3d;uniform mat4 u_fog_matrix;uniform float u_ele_delta;out vec2 v_texture_pos;out float v_fog_depth;void main() {float ele=get_elevation(a_pos3d.xy);float ele_delta=a_pos3d.z==1.0 ? u_ele_delta : 0.0;v_texture_pos=a_pos3d.xy/8192.0;gl_Position=projectTileFor3D(a_pos3d.xy,ele-ele_delta);vec4 pos=u_fog_matrix*vec4(a_pos3d.xy,ele,1.0);v_fog_depth=pos.z/pos.w*0.5+0.5;}"),
8785
8820
  terrainDepth: prepare("in float v_depth;const highp vec4 bitSh=vec4(256.*256.*256.,256.*256.,256.,1.);const highp vec4 bitMsk=vec4(0.,vec3(1./256.0));highp vec4 pack(highp float value) {highp vec4 comp=fract(value*bitSh);comp-=comp.xxyz*bitMsk;return comp;}void main() {fragColor=pack(v_depth);}", "layout(location=0) in vec3 a_pos3d;uniform float u_ele_delta;out float v_depth;void main() {float ele=get_elevation(a_pos3d.xy);float ele_delta=a_pos3d.z==1.0 ? u_ele_delta : 0.0;gl_Position=projectTileFor3D(a_pos3d.xy,ele-ele_delta);v_depth=gl_Position.z/gl_Position.w;}"),
8786
- terrainCoords: prepare("precision mediump float;uniform sampler2D u_texture;uniform float u_terrain_coords_id;in vec2 v_texture_pos;void main() {vec4 rgba=texture(u_texture,v_texture_pos);fragColor=vec4(rgba.r,rgba.g,rgba.b,u_terrain_coords_id);}", "layout(location=0) in vec3 a_pos3d;uniform float u_ele_delta;out vec2 v_texture_pos;void main() {float ele=get_elevation(a_pos3d.xy);float ele_delta=a_pos3d.z==1.0 ? u_ele_delta : 0.0;v_texture_pos=a_pos3d.xy/8192.0;gl_Position=projectTileFor3D(a_pos3d.xy,ele-ele_delta);}"),
8787
8821
  atmosphere: prepare("#ifdef GL_ES\nprecision highp float;\n#endif\nin vec3 view_direction;uniform vec3 u_sun_pos;uniform vec3 u_globe_position;uniform float u_globe_radius;uniform float u_atmosphere_blend;/**Shader use from https:*Made some change to adapt to MapLibre Globe geometry*/const float PI=3.141592653589793;const int iSteps=5;const int jSteps=3;/*radius of the planet*/const float EARTH_RADIUS=6371e3;/*radius of the atmosphere*/const float ATMOS_RADIUS=6471e3;vec2 rsi(vec3 r0,vec3 rd,float sr) {float a=dot(rd,rd);float b=2.0*dot(rd,r0);float c=dot(r0,r0)-(sr*sr);float d=(b*b)-4.0*a*c;if (d < 0.0) return vec2(1e5,-1e5);return vec2((-b-sqrt(d))/(2.0*a),(-b+sqrt(d))/(2.0*a));}vec4 atmosphere(vec3 r,vec3 r0,vec3 pSun,float iSun,float rPlanet,float rAtmos,vec3 kRlh,float kMie,float shRlh,float shMie,float g) {pSun=normalize(pSun);r=normalize(r);vec2 p=rsi(r0,r,rAtmos);if (p.x > p.y) {return vec4(0.0,0.0,0.0,1.0);}if (p.x < 0.0) {p.x=0.0;}vec3 pos=r0+r*p.x;vec2 p2=rsi(r0,r,rPlanet);if (p2.x <=p2.y && p2.x > 0.0) {p.y=min(p.y,p2.x);}float iStepSize=(p.y-p.x)/float(iSteps);float iTime=p.x+iStepSize*0.5;vec3 totalRlh=vec3(0,0,0);vec3 totalMie=vec3(0,0,0);float iOdRlh=0.0;float iOdMie=0.0;float mu=dot(r,pSun);float mumu=mu*mu;float gg=g*g;float pRlh=3.0/(16.0*PI)*(1.0+mumu);float pMie=3.0/(8.0*PI)*((1.0-gg)*(mumu+1.0))/(pow(1.0+gg-2.0*mu*g,1.5)*(2.0+gg));for (int i=0; i < iSteps; i++) {vec3 iPos=r0+r*iTime;float iHeight=length(iPos)-rPlanet;float odStepRlh=exp(-iHeight/shRlh)*iStepSize;float odStepMie=exp(-iHeight/shMie)*iStepSize;iOdRlh+=odStepRlh;iOdMie+=odStepMie;float jStepSize=rsi(iPos,pSun,rAtmos).y/float(jSteps);float jTime=jStepSize*0.5;float jOdRlh=0.0;float jOdMie=0.0;for (int j=0; j < jSteps; j++) {vec3 jPos=iPos+pSun*jTime;float jHeight=length(jPos)-rPlanet;jOdRlh+=exp(-jHeight/shRlh)*jStepSize;jOdMie+=exp(-jHeight/shMie)*jStepSize;jTime+=jStepSize;}vec3 attn=exp(-(kMie*(iOdMie+jOdMie)+kRlh*(iOdRlh+jOdRlh)));totalRlh+=odStepRlh*attn;totalMie+=odStepMie*attn;iTime+=iStepSize;}float opacity=exp(-(length(kRlh)*length(totalRlh)+kMie*length(totalMie)));vec3 color=iSun*(pRlh*kRlh*totalRlh+pMie*kMie*totalMie);return vec4(color,opacity);}void main() {vec3 scale_camera_pos=-u_globe_position*EARTH_RADIUS/u_globe_radius;vec4 color=atmosphere(normalize(view_direction),scale_camera_pos,u_sun_pos,22.0,EARTH_RADIUS,ATMOS_RADIUS,vec3(5.5e-6,13.0e-6,22.4e-6),21e-6,8e3,1.2e3,0.758\n);color.rgb=1.0-exp(-1.0*color.rgb);color=pow(color,vec4(1.0/2.2));fragColor=vec4(color.rgb,1.0-color.a)*u_atmosphere_blend;}", "layout(location=0) in vec2 a_pos;uniform mat4 u_inv_proj_matrix;out vec3 view_direction;void main() {view_direction=(u_inv_proj_matrix*vec4(a_pos,0.0,1.0)).xyz;gl_Position=vec4(a_pos,0.0,1.0);}"),
8788
8822
  sky: prepare("uniform vec4 u_sky_color;uniform vec4 u_horizon_color;uniform vec2 u_horizon;uniform vec2 u_horizon_normal;uniform float u_sky_horizon_blend;uniform float u_sky_blend;void main() {float x=gl_FragCoord.x;float y=gl_FragCoord.y;float blend=(y-u_horizon.y)*u_horizon_normal.y+(x-u_horizon.x)*u_horizon_normal.x;if (blend > 0.0) {if (blend < u_sky_horizon_blend) {fragColor=mix(u_sky_color,u_horizon_color,pow(1.0-blend/u_sky_horizon_blend,2.0));} else {fragColor=u_sky_color;}}fragColor=mix(fragColor,vec4(vec3(0.0),0.0),u_sky_blend);}", "layout(location=0) in vec2 a_pos;void main() {gl_Position=vec4(a_pos,1.0,1.0);}")
8789
8823
  };
@@ -9127,7 +9161,9 @@ var TransformHelper = class {
9127
9161
  setMinZoom(zoom) {
9128
9162
  if (this._minZoom === zoom) return;
9129
9163
  this._minZoom = zoom;
9164
+ const unmodified = this._unmodified;
9130
9165
  this.setZoom(this.applyConstrain(this._center, this.zoom).zoom);
9166
+ this._unmodified = unmodified;
9131
9167
  }
9132
9168
  get maxZoom() {
9133
9169
  return this._maxZoom;
@@ -9135,7 +9171,9 @@ var TransformHelper = class {
9135
9171
  setMaxZoom(zoom) {
9136
9172
  if (this._maxZoom === zoom) return;
9137
9173
  this._maxZoom = zoom;
9174
+ const unmodified = this._unmodified;
9138
9175
  this.setZoom(this.applyConstrain(this._center, this.zoom).zoom);
9176
+ this._unmodified = unmodified;
9139
9177
  }
9140
9178
  get minPitch() {
9141
9179
  return this._minPitch;
@@ -9143,7 +9181,9 @@ var TransformHelper = class {
9143
9181
  setMinPitch(pitch) {
9144
9182
  if (this._minPitch === pitch) return;
9145
9183
  this._minPitch = pitch;
9184
+ const unmodified = this._unmodified;
9146
9185
  this.setPitch(Math.max(this.pitch, pitch));
9186
+ this._unmodified = unmodified;
9147
9187
  }
9148
9188
  get maxPitch() {
9149
9189
  return this._maxPitch;
@@ -9151,7 +9191,9 @@ var TransformHelper = class {
9151
9191
  setMaxPitch(pitch) {
9152
9192
  if (this._maxPitch === pitch) return;
9153
9193
  this._maxPitch = pitch;
9194
+ const unmodified = this._unmodified;
9154
9195
  this.setPitch(Math.min(this.pitch, pitch));
9196
+ this._unmodified = unmodified;
9155
9197
  }
9156
9198
  get renderWorldCopies() {
9157
9199
  return this._renderWorldCopies;
@@ -9523,9 +9565,7 @@ var TransformHelper = class {
9523
9565
  return Math.cos(this.pitchInRadians) * this._cameraToCenterDistance / this._pixelPerMeter + this.elevation;
9524
9566
  }
9525
9567
  getCameraLngLat() {
9526
- const pixelPerMeter = mercatorZfromAltitude(1, this.center.lat) * this.worldSize;
9527
- const cameraToCenterDistanceMeters = this.cameraToCenterDistance / pixelPerMeter;
9528
- return cameraMercatorCoordinateFromCenterAndRotation(this.center, this.elevation, this.pitch, this.bearing, cameraToCenterDistanceMeters).toLngLat();
9568
+ return cameraMercatorCoordinate(this).toLngLat();
9529
9569
  }
9530
9570
  getMercatorTileCoordinates(overscaledTileID) {
9531
9571
  if (!overscaledTileID) return [
@@ -9883,180 +9923,903 @@ function getIdealNearFarPlaneDistance(horizonPlane, nearPlaneNormalized) {
9883
9923
  } else return null;
9884
9924
  }
9885
9925
  //#endregion
9886
- //#region src/geo/projection/mercator_transform.ts
9887
- var MercatorTransform = class MercatorTransform {
9888
- get pixelsToClipSpaceMatrix() {
9889
- return this._helper.pixelsToClipSpaceMatrix;
9890
- }
9891
- get clipSpaceToPixelsMatrix() {
9892
- return this._helper.clipSpaceToPixelsMatrix;
9893
- }
9894
- get pixelsToGLUnits() {
9895
- return this._helper.pixelsToGLUnits;
9896
- }
9897
- get centerOffset() {
9898
- return this._helper.centerOffset;
9926
+ //#region src/data/pos3d_attributes.ts
9927
+ const pos3dAttributes = createLayout([{
9928
+ name: "a_pos3d",
9929
+ type: "Int16",
9930
+ components: 3
9931
+ }]);
9932
+ //#endregion
9933
+ //#region src/tile/terrain_tile_manager.ts
9934
+ /**
9935
+ * @internal
9936
+ * This class is a helper for the Terrain-class, it:
9937
+ *
9938
+ * - loads raster-dem tiles
9939
+ * - manages all renderToTexture tiles.
9940
+ * - caches previous rendered tiles.
9941
+ * - finds all necessary renderToTexture tiles for a OverscaledTileID area
9942
+ * - finds the corresponding raster-dem tile for OverscaledTileID
9943
+ */
9944
+ var TerrainTileManager = class extends Evented {
9945
+ constructor(tileManager) {
9946
+ super();
9947
+ this._lastTilesetChange = now();
9948
+ this.tileManager = tileManager;
9949
+ this._tiles = {};
9950
+ this._renderableTilesKeys = [];
9951
+ this._sourceTileCache = {};
9952
+ this.minzoom = 0;
9953
+ this.maxzoom = 22;
9954
+ this.deltaZoom = 1;
9955
+ this.tileSize = tileManager._source.tileSize * 2 ** this.deltaZoom;
9956
+ tileManager.usedForTerrain = true;
9957
+ tileManager.tileSize = this.tileSize;
9899
9958
  }
9900
- get size() {
9901
- return this._helper.size;
9959
+ destruct() {
9960
+ this.tileManager.usedForTerrain = false;
9961
+ this.tileManager.tileSize = null;
9962
+ this.releaseAllRTT();
9902
9963
  }
9903
- get rotationMatrix() {
9904
- return this._helper.rotationMatrix;
9964
+ getSource() {
9965
+ return this.tileManager._source;
9905
9966
  }
9906
- get centerPoint() {
9907
- return this._helper.centerPoint;
9967
+ /**
9968
+ * Load Terrain Tiles, create internal render-to-texture tiles, free GPU memory.
9969
+ * @param transform - the operation to do
9970
+ * @param terrain - the terrain
9971
+ */
9972
+ update(transform, terrain) {
9973
+ this.tileManager.update(transform, terrain);
9974
+ this._renderableTilesKeys = [];
9975
+ const keys = {};
9976
+ for (const tileID of coveringTiles(transform, {
9977
+ tileSize: this.tileSize,
9978
+ minzoom: this.minzoom,
9979
+ maxzoom: this.maxzoom,
9980
+ reparseOverscaled: false,
9981
+ terrain,
9982
+ calculateTileZoom: this.tileManager._source.calculateTileZoom
9983
+ })) {
9984
+ keys[tileID.key] = true;
9985
+ this._renderableTilesKeys.push(tileID.key);
9986
+ if (!this._tiles[tileID.key]) {
9987
+ tileID.terrainRttPosMatrix32f = /* @__PURE__ */ new Float32Array(16);
9988
+ ortho(tileID.terrainRttPosMatrix32f, 0, EXTENT, EXTENT, 0, 0, 1);
9989
+ this._tiles[tileID.key] = new Tile(tileID, this.tileSize);
9990
+ this._lastTilesetChange = now();
9991
+ }
9992
+ }
9993
+ for (const key in this._tiles) if (!keys[key]) {
9994
+ this._tiles[key].releaseRTT(this.tileManager.map.painter);
9995
+ delete this._tiles[key];
9996
+ }
9908
9997
  }
9909
- get pixelsPerMeter() {
9910
- return this._helper.pixelsPerMeter;
9998
+ /**
9999
+ * Release the RTT objects for `tileID` (and its ancestors/descendants),
10000
+ */
10001
+ releaseRTT(tileID) {
10002
+ for (const key in this._tiles) {
10003
+ const tile = this._tiles[key];
10004
+ if (tile.tileID.equals(tileID) || tile.tileID.isChildOf(tileID) || tileID.isChildOf(tile.tileID)) tile.releaseRTT(this.tileManager.map.painter);
10005
+ }
9911
10006
  }
9912
- setMinZoom(zoom) {
9913
- this._helper.setMinZoom(zoom);
10007
+ /**
10008
+ * Release the A RTT objects for all tiles.
10009
+ */
10010
+ releaseAllRTT() {
10011
+ for (const key in this._tiles) this._tiles[key].releaseRTT(this.tileManager.map.painter);
9914
10012
  }
9915
- setMaxZoom(zoom) {
9916
- this._helper.setMaxZoom(zoom);
10013
+ /**
10014
+ * get a list of tiles, which are loaded and should be rendered in the current scene
10015
+ * @returns the renderable tiles
10016
+ */
10017
+ getRenderableTiles() {
10018
+ return this._renderableTilesKeys.map((key) => this.getTileByID(key));
9917
10019
  }
9918
- setMinPitch(pitch) {
9919
- this._helper.setMinPitch(pitch);
10020
+ /**
10021
+ * get terrain tile by the TileID key
10022
+ * @param id - the tile id
10023
+ * @returns the tile
10024
+ */
10025
+ getTileByID(id) {
10026
+ return this._tiles[id];
9920
10027
  }
9921
- setMaxPitch(pitch) {
9922
- this._helper.setMaxPitch(pitch);
10028
+ /**
10029
+ * Searches for the corresponding current renderable terrain-tiles
10030
+ * @param tileID - the tile to look for
10031
+ * @returns the tiles that were found
10032
+ */
10033
+ getTerrainCoords(tileID, terrainTileRanges) {
10034
+ if (terrainTileRanges) return this._getTerrainCoordsForTileRanges(tileID, terrainTileRanges);
10035
+ else return this._getTerrainCoordsForRegularTile(tileID);
9923
10036
  }
9924
- setRenderWorldCopies(renderWorldCopies) {
9925
- this._helper.setRenderWorldCopies(renderWorldCopies);
10037
+ /**
10038
+ * Searches for the corresponding current renderable terrain-tiles.
10039
+ * Includes terrain tiles that are either:
10040
+ * - the same as the tileID
10041
+ * - a parent of the tileID
10042
+ * - a child of the tileID
10043
+ * @param tileID - the tile to look for
10044
+ * @returns the tiles that were found
10045
+ */
10046
+ _getTerrainCoordsForRegularTile(tileID) {
10047
+ const coords = {};
10048
+ for (const key of this._renderableTilesKeys) {
10049
+ const terrainTileID = this._tiles[key].tileID;
10050
+ const coord = tileID.clone();
10051
+ const mat = createMat4f64();
10052
+ if (terrainTileID.canonical.equals(tileID.canonical)) ortho(mat, 0, EXTENT, EXTENT, 0, 0, 1);
10053
+ else if (terrainTileID.canonical.isChildOf(tileID.canonical)) {
10054
+ const dz = terrainTileID.canonical.z - tileID.canonical.z;
10055
+ const dx = terrainTileID.canonical.x - (terrainTileID.canonical.x >> dz << dz);
10056
+ const dy = terrainTileID.canonical.y - (terrainTileID.canonical.y >> dz << dz);
10057
+ const size = EXTENT >> dz;
10058
+ ortho(mat, 0, size, size, 0, 0, 1);
10059
+ translate(mat, mat, [
10060
+ -dx * size,
10061
+ -dy * size,
10062
+ 0
10063
+ ]);
10064
+ } else if (tileID.canonical.isChildOf(terrainTileID.canonical)) {
10065
+ const dz = tileID.canonical.z - terrainTileID.canonical.z;
10066
+ const dx = tileID.canonical.x - (tileID.canonical.x >> dz << dz);
10067
+ const dy = tileID.canonical.y - (tileID.canonical.y >> dz << dz);
10068
+ const size = EXTENT >> dz;
10069
+ ortho(mat, 0, EXTENT, EXTENT, 0, 0, 1);
10070
+ translate(mat, mat, [
10071
+ dx * size,
10072
+ dy * size,
10073
+ 0
10074
+ ]);
10075
+ scale(mat, mat, [
10076
+ 1 / 2 ** dz,
10077
+ 1 / 2 ** dz,
10078
+ 0
10079
+ ]);
10080
+ } else continue;
10081
+ coord.terrainRttPosMatrix32f = new Float32Array(mat);
10082
+ coords[key] = coord;
10083
+ }
10084
+ return coords;
9926
10085
  }
9927
- setBearing(bearing) {
9928
- this._helper.setBearing(bearing);
10086
+ /**
10087
+ * Searches for the corresponding current renderable terrain-tiles.
10088
+ * Includes terrain tiles that are within terrain tile ranges.
10089
+ * @param tileID - the tile to look for
10090
+ * @returns the tiles that were found
10091
+ */
10092
+ _getTerrainCoordsForTileRanges(tileID, terrainTileRanges) {
10093
+ const coords = {};
10094
+ for (const key of this._renderableTilesKeys) {
10095
+ const terrainTileID = this._tiles[key].tileID;
10096
+ if (!this._isWithinTileRanges(terrainTileID, terrainTileRanges)) continue;
10097
+ const coord = tileID.clone();
10098
+ const mat = createMat4f64();
10099
+ if (terrainTileID.canonical.z === tileID.canonical.z) {
10100
+ const dx = tileID.canonical.x - terrainTileID.canonical.x + tileID.wrap * (1 << tileID.canonical.z);
10101
+ const dy = tileID.canonical.y - terrainTileID.canonical.y;
10102
+ ortho(mat, 0, EXTENT, EXTENT, 0, 0, 1);
10103
+ translate(mat, mat, [
10104
+ dx * EXTENT,
10105
+ dy * EXTENT,
10106
+ 0
10107
+ ]);
10108
+ } else if (terrainTileID.canonical.z > tileID.canonical.z) {
10109
+ const dz = terrainTileID.canonical.z - tileID.canonical.z;
10110
+ const dx = terrainTileID.canonical.x - (terrainTileID.canonical.x >> dz << dz) + tileID.wrap * (1 << terrainTileID.canonical.z);
10111
+ const dy = terrainTileID.canonical.y - (terrainTileID.canonical.y >> dz << dz);
10112
+ const dx2 = tileID.canonical.x - (terrainTileID.canonical.x >> dz);
10113
+ const dy2 = tileID.canonical.y - (terrainTileID.canonical.y >> dz);
10114
+ const size = EXTENT >> dz;
10115
+ ortho(mat, 0, size, size, 0, 0, 1);
10116
+ translate(mat, mat, [
10117
+ -dx * size + dx2 * EXTENT,
10118
+ -dy * size + dy2 * EXTENT,
10119
+ 0
10120
+ ]);
10121
+ } else {
10122
+ const dz = tileID.canonical.z - terrainTileID.canonical.z;
10123
+ const dx = tileID.canonical.x - (tileID.canonical.x >> dz << dz) + tileID.wrap * (1 << tileID.canonical.z);
10124
+ const dy = tileID.canonical.y - (tileID.canonical.y >> dz << dz);
10125
+ const dx2 = (tileID.canonical.x >> dz) - terrainTileID.canonical.x;
10126
+ const dy2 = (tileID.canonical.y >> dz) - terrainTileID.canonical.y;
10127
+ const size = EXTENT << dz;
10128
+ ortho(mat, 0, size, size, 0, 0, 1);
10129
+ translate(mat, mat, [
10130
+ dx * EXTENT + dx2 * size,
10131
+ dy * EXTENT + dy2 * size,
10132
+ 0
10133
+ ]);
10134
+ }
10135
+ coord.terrainRttPosMatrix32f = new Float32Array(mat);
10136
+ coords[key] = coord;
10137
+ }
10138
+ return coords;
9929
10139
  }
9930
- setPitch(pitch) {
9931
- this._helper.setPitch(pitch);
9932
- }
9933
- setRoll(roll) {
9934
- this._helper.setRoll(roll);
9935
- }
9936
- setFov(fov) {
9937
- this._helper.setFov(fov);
9938
- }
9939
- setZoom(zoom) {
9940
- this._helper.setZoom(zoom);
9941
- }
9942
- setCenter(center) {
9943
- this._helper.setCenter(center);
9944
- }
9945
- setElevation(elevation) {
9946
- this._helper.setElevation(elevation);
9947
- }
9948
- setMinElevationForCurrentTile(elevation) {
9949
- this._helper.setMinElevationForCurrentTile(elevation);
9950
- }
9951
- setPadding(padding) {
9952
- this._helper.setPadding(padding);
9953
- }
9954
- interpolatePadding(start, target, t) {
9955
- this._helper.interpolatePadding(start, target, t);
9956
- }
9957
- isPaddingEqual(padding) {
9958
- return this._helper.isPaddingEqual(padding);
9959
- }
9960
- resize(width, height, constrain = true) {
9961
- this._helper.resize(width, height, constrain);
10140
+ /**
10141
+ * find the covering raster-dem tile
10142
+ * @param tileID - the tile to look for
10143
+ * @param searchForDEM - Optional parameter to search for (parent) source tiles with loaded dem.
10144
+ * @returns the tile
10145
+ */
10146
+ getSourceTile(tileID, searchForDEM) {
10147
+ const source = this.tileManager._source;
10148
+ let z = tileID.overscaledZ - this.deltaZoom;
10149
+ if (z > source.maxzoom) z = source.maxzoom;
10150
+ if (z < source.minzoom) return void 0;
10151
+ this._sourceTileCache[tileID.key] ||= tileID.scaledTo(z).key;
10152
+ let tile = this.findTileInCaches(this._sourceTileCache[tileID.key]);
10153
+ if (!tile?.dem && searchForDEM) while (z >= source.minzoom && !tile?.dem) tile = this.findTileInCaches(tileID.scaledTo(z--).key);
10154
+ return tile;
9962
10155
  }
9963
- getMaxBounds() {
9964
- return this._helper.getMaxBounds();
10156
+ findTileInCaches(key) {
10157
+ let tile = this.tileManager.getTileByID(key);
10158
+ if (tile) return tile;
10159
+ tile = this.tileManager._outOfViewCache.getByKey(key);
10160
+ return tile;
9965
10161
  }
9966
- setMaxBounds(bounds) {
9967
- this._helper.setMaxBounds(bounds);
10162
+ /**
10163
+ * gets whether any tiles were loaded after a specific time. This is used to update the depth framebuffer.
10164
+ * @param time - the time
10165
+ * @returns true if any tiles came into view at or after the specified time
10166
+ */
10167
+ anyTilesAfterTime(time = now()) {
10168
+ return this._lastTilesetChange >= time;
9968
10169
  }
9969
- setConstrainOverride(constrain) {
9970
- this._helper.setConstrainOverride(constrain);
10170
+ /**
10171
+ * Checks whether a tile is within the canonical tile ranges.
10172
+ * @param tileID - Tile to check
10173
+ * @param canonicalTileRanges - Canonical tile ranges
10174
+ * @returns
10175
+ */
10176
+ _isWithinTileRanges(tileID, canonicalTileRanges) {
10177
+ const range = canonicalTileRanges[tileID.canonical.z];
10178
+ return !!range && (tileID.wrap > range.minWrap || tileID.wrap < range.maxWrap || tileID.canonical.x >= range.minTileXWrapped && tileID.canonical.x <= range.maxTileXWrapped && tileID.canonical.y >= range.minTileY && tileID.canonical.y <= range.maxTileY);
9971
10179
  }
9972
- overrideNearFarZ(nearZ, farZ) {
9973
- this._helper.overrideNearFarZ(nearZ, farZ);
10180
+ };
10181
+ //#endregion
10182
+ //#region src/render/terrain.ts
10183
+ const MAX_BISECTIONS = 40;
10184
+ const HIT_EPSILON_M = 1e-6;
10185
+ /** Keeps the elevation bracket non-degenerate when the terrain is entirely flat, such as unloaded DEMs. */
10186
+ const BRACKET_PADDING_M = 10;
10187
+ /** `DEMData.sampleBilinear` throws on the far tile edge, so samples stop just short of it. */
10188
+ const MAX_TILE_COORD = EXTENT * (1 - 1e-12);
10189
+ /**
10190
+ * @internal
10191
+ * This is the main class which handles most of the 3D Terrain logic. It has the following topics:
10192
+ *
10193
+ * 1. loads raster-dem tiles via the internal tileManager this.tileManager
10194
+ * 2. creates a depth-framebuffer, which is used to calculate the visibility of coordinates
10195
+ * 3. stores all render-to-texture tiles in the this.tileManager._tiles
10196
+ * 4. calculates the elevation for a specific tile-coordinate
10197
+ * 5. creates a terrain-mesh
10198
+ *
10199
+ * A note about the GPU resource-usage:
10200
+ *
10201
+ * Framebuffers:
10202
+ *
10203
+ * - one for the depth framebuffer with the size of the map-div.
10204
+ * - one for rendering a tile to texture with the size of tileSize (= 512x512).
10205
+ *
10206
+ * Textures:
10207
+ *
10208
+ * - one texture for an empty raster-dem tile with size 1x1
10209
+ * - one texture for an empty depth-buffer, when terrain is disabled with size 1x1
10210
+ * - one texture for an each loaded raster-dem with size of the source.tileSize
10211
+ * - one texture for the depth-framebuffer with the size of the map-div.
10212
+ * - finally for each render-to-texture tile (= this._tiles) a set of textures
10213
+ * for each render stack (The stack-concept is documented in painter.ts).
10214
+ *
10215
+ * Normally there exists 1-3 Textures per tile, depending on the stylesheet.
10216
+ * Each Textures has the size 2*tileSize (= 1024x1024). Also there exists a
10217
+ * cache of the last 150 newest rendered tiles.
10218
+ *
10219
+ */
10220
+ var Terrain = class {
10221
+ constructor(painter, tileManager, options, terrainSkirtLength = "auto") {
10222
+ this._meshCache = {};
10223
+ this.painter = painter;
10224
+ this.tileManager = new TerrainTileManager(tileManager);
10225
+ this.options = options;
10226
+ this.exaggeration = typeof options.exaggeration === "number" ? options.exaggeration : 1;
10227
+ this._terrainSkirtLength = terrainSkirtLength;
10228
+ this.qualityFactor = 2;
10229
+ this.meshSize = 128;
10230
+ this._demMatrixCache = /* @__PURE__ */ new Map();
10231
+ this._elevationSamplerCache = /* @__PURE__ */ new Map();
9974
10232
  }
9975
- clearNearFarZOverride() {
9976
- this._helper.clearNearFarZOverride();
10233
+ destroy() {
10234
+ if (this._fbo) {
10235
+ this._fbo.destroy();
10236
+ this._fbo = null;
10237
+ }
10238
+ if (this._fboDepthTexture) {
10239
+ this._fboDepthTexture.destroy();
10240
+ this._fboDepthTexture = null;
10241
+ }
10242
+ if (this._emptyDemTexture) {
10243
+ this._emptyDemTexture.destroy();
10244
+ this._emptyDemTexture = null;
10245
+ }
10246
+ if (this._emptyDepthTexture) {
10247
+ this._emptyDepthTexture.destroy();
10248
+ this._emptyDepthTexture = null;
10249
+ }
10250
+ for (const key in this._meshCache) this._meshCache[key].destroy();
10251
+ this._meshCache = {};
10252
+ this.tileManager.destruct();
9977
10253
  }
9978
- getCameraQueryGeometry(queryGeometry) {
9979
- return this._helper.getCameraQueryGeometry(this.getCameraPoint(), queryGeometry);
10254
+ /**
10255
+ * Get the elevation-value from original dem-data for a given tile-coordinate.
10256
+ * Coordinates that fall outside `[0, extent)` are normalized to the
10257
+ * appropriate neighbor tile before lookup.
10258
+ * @param tileID - the tile to get the elevation for
10259
+ * @param x - x coordinate relative to the tile, may be outside `[0, extent)`
10260
+ * @param y - y coordinate relative to the tile, may be outside `[0, extent)`
10261
+ * @param extent - optional, default 8192
10262
+ * @returns the elevation
10263
+ */
10264
+ getDEMElevation(tileID, x, y, extent = EXTENT) {
10265
+ const normalized = tileID.normalizeCoordinates(x, y, extent);
10266
+ if (!normalized) return 0;
10267
+ const sampler = this.getElevationSampler(normalized.tileID);
10268
+ return sampler ? sampler(normalized.x, normalized.y, extent) : 0;
9980
10269
  }
9981
- get tileSize() {
9982
- return this._helper.tileSize;
10270
+ /**
10271
+ * Get the elevation for given {@link LngLat} in respect of exaggeration.
10272
+ * @param lnglat - the location
10273
+ * @param zoom - the zoom, use {@link getElevationForLngLat} if you don't want a specific zoom level, but more accurate results.
10274
+ * @returns the elevation
10275
+ */
10276
+ getElevationForLngLatZoom(lnglat, zoom) {
10277
+ if (!isInBoundsForZoomLngLat(zoom, lnglat.wrap())) return 0;
10278
+ const { tileID, mercatorX, mercatorY } = this._getOverscaledTileIDFromLngLatZoom(lnglat, zoom);
10279
+ return this.getElevation(tileID, mercatorX % EXTENT, mercatorY % EXTENT, EXTENT);
9983
10280
  }
9984
- get tileZoom() {
9985
- return this._helper.tileZoom;
10281
+ /**
10282
+ * Get the elevation for given {@link LngLat} in respect of exaggeration.
10283
+ * This will traverse up the zoom levels to find the first tile with data to return.
10284
+ * @param lnglat - the location
10285
+ * @returns the elevation
10286
+ */
10287
+ getElevationForLngLat(lnglat, transform) {
10288
+ const terrainCoveringTiles = coveringTiles(transform, {
10289
+ maxzoom: this.tileManager.maxzoom,
10290
+ minzoom: this.tileManager.minzoom,
10291
+ tileSize: 512,
10292
+ terrain: this
10293
+ });
10294
+ let zoom = 0;
10295
+ for (const tile of terrainCoveringTiles) if (tile.canonical.z > zoom) zoom = Math.min(tile.canonical.z, this.tileManager.maxzoom);
10296
+ return this.getElevationForLngLatZoom(lnglat, zoom);
9986
10297
  }
9987
- get scale() {
9988
- return this._helper.scale;
10298
+ /**
10299
+ * Get the elevation for given coordinate in respect of exaggeration.
10300
+ * @param tileID - the tile id
10301
+ * @param x - x coordinate relative to the tile, may be outside `[0, extent)`
10302
+ * @param y - y coordinate relative to the tile, may be outside `[0, extent)`
10303
+ * @param extent - optional, default 8192
10304
+ * @returns the elevation
10305
+ */
10306
+ getElevation(tileID, x, y, extent = EXTENT) {
10307
+ return this.getDEMElevation(tileID, x, y, extent) * this.exaggeration;
9989
10308
  }
9990
- get worldSize() {
9991
- return this._helper.worldSize;
10309
+ /**
10310
+ * Clear CPU elevation samplers that may retain a previously selected DEM tile.
10311
+ * @internal
10312
+ */
10313
+ resetElevationCache() {
10314
+ this._elevationSamplerCache.clear();
10315
+ this._coverageIndex = void 0;
10316
+ }
10317
+ /**
10318
+ * Index of the tiles the terrain currently renders, for sampling the terrain surface on the CPU.
10319
+ * Built on first use and kept until {@link resetElevationCache}.
10320
+ * @returns the index, or null when no terrain tile is renderable
10321
+ */
10322
+ getCoverageIndex() {
10323
+ if (this._coverageIndex === void 0) this._coverageIndex = this._buildCoverageIndex();
10324
+ return this._coverageIndex;
10325
+ }
10326
+ _buildCoverageIndex() {
10327
+ const zooms = [];
10328
+ const samplerPerTile = /* @__PURE__ */ new Map();
10329
+ let minElevation = 0;
10330
+ let maxElevation = 0;
10331
+ for (const tile of this.tileManager.getRenderableTiles()) {
10332
+ if (!tile) continue;
10333
+ const { canonical, wrap } = tile.tileID;
10334
+ if (!zooms.includes(canonical.z)) zooms.push(canonical.z);
10335
+ const sampler = this.getElevationSampler(tile.tileID);
10336
+ samplerPerTile.set(`${wrap}/${canonical.z}/${canonical.x}/${canonical.y}`, sampler);
10337
+ const { minElevation: tileMin, maxElevation: tileMax } = this.getMinMaxElevation(tile.tileID);
10338
+ minElevation = Math.min(minElevation, tileMin ?? 0);
10339
+ maxElevation = Math.max(maxElevation, tileMax ?? 0);
10340
+ }
10341
+ if (samplerPerTile.size === 0) return null;
10342
+ zooms.sort((a, b) => b - a);
10343
+ return {
10344
+ zooms,
10345
+ samplerPerTile,
10346
+ minElevation: minElevation - BRACKET_PADDING_M,
10347
+ maxElevation: maxElevation + BRACKET_PADDING_M
10348
+ };
9992
10349
  }
9993
- get width() {
9994
- return this._helper.width;
10350
+ /**
10351
+ * Get a function that samples the raw DEM elevation of a tile, without exaggeration.
10352
+ * @param tileID - the tile id
10353
+ * @returns the sampler, or null when the tile's DEM data is not loaded
10354
+ */
10355
+ getElevationSampler(tileID) {
10356
+ const key = tileID.key;
10357
+ const cachedSampler = this._elevationSamplerCache.get(key);
10358
+ if (cachedSampler) return cachedSampler;
10359
+ const sourceTile = this.tileManager.getSourceTile(tileID, true);
10360
+ const dem = sourceTile?.dem;
10361
+ if (!sourceTile || !dem) return null;
10362
+ const matrix = this._getDEMTileMatrix(tileID, sourceTile);
10363
+ const demPixelScaleX = matrix[0] * dem.dim;
10364
+ const demPixelScaleY = matrix[5] * dem.dim;
10365
+ const demPixelOffsetX = matrix[12] * dem.dim;
10366
+ const demPixelOffsetY = matrix[13] * dem.dim;
10367
+ const sampler = (x, y, extent) => {
10368
+ const extentScale = extent === 8192 ? 1 : EXTENT / extent;
10369
+ return dem.sampleBilinear(x * extentScale * demPixelScaleX + demPixelOffsetX, y * extentScale * demPixelScaleY + demPixelOffsetY);
10370
+ };
10371
+ this._elevationSamplerCache.set(key, sampler);
10372
+ return sampler;
9995
10373
  }
9996
- get height() {
9997
- return this._helper.height;
10374
+ _getDEMTileMatrix(tileID, sourceTile) {
10375
+ const matrixKey = `${sourceTile.tileID.key}/${tileID.key}`;
10376
+ const cachedMatrix = this._demMatrixCache.get(matrixKey);
10377
+ if (cachedMatrix) return cachedMatrix;
10378
+ const maxzoom = this.tileManager.getSource().maxzoom;
10379
+ let dz = tileID.canonical.z - sourceTile.tileID.canonical.z;
10380
+ if (tileID.overscaledZ > tileID.canonical.z) {
10381
+ if (tileID.canonical.z >= maxzoom) dz = tileID.canonical.z - maxzoom;
10382
+ else warnOnce("cannot calculate elevation if elevation maxzoom > source.maxzoom");
10383
+ }
10384
+ const dx = tileID.canonical.x - (tileID.canonical.x >> dz << dz);
10385
+ const dy = tileID.canonical.y - (tileID.canonical.y >> dz << dz);
10386
+ const demMatrix = fromScaling(/* @__PURE__ */ new Float64Array(16), [
10387
+ 1 / (EXTENT << dz),
10388
+ 1 / (EXTENT << dz),
10389
+ 0
10390
+ ]);
10391
+ translate(demMatrix, demMatrix, [
10392
+ dx * EXTENT,
10393
+ dy * EXTENT,
10394
+ 0
10395
+ ]);
10396
+ this._demMatrixCache.set(matrixKey, demMatrix);
10397
+ return demMatrix;
9998
10398
  }
9999
- get lngRange() {
10000
- return this._helper.lngRange;
10399
+ /**
10400
+ * returns a Terrain Object for a tile. Unless the tile corresponds to data (e.g. tile is loading), return a flat dem object
10401
+ * @param tileID - the tile to get the terrain for
10402
+ * @returns the terrain data to use in the program
10403
+ */
10404
+ getTerrainData(tileID) {
10405
+ if (!this._emptyDemTexture) {
10406
+ const context = this.painter.context;
10407
+ const image = new RGBAImage({
10408
+ width: 1,
10409
+ height: 1
10410
+ }, /* @__PURE__ */ new Uint8Array(4));
10411
+ this._emptyDepthTexture = new Texture(context, image, context.gl.RGBA, { premultiply: false });
10412
+ this._emptyDemUnpack = [
10413
+ 0,
10414
+ 0,
10415
+ 0,
10416
+ 0
10417
+ ];
10418
+ this._emptyDemTexture = new Texture(context, new RGBAImage({
10419
+ width: 1,
10420
+ height: 1
10421
+ }), context.gl.RGBA, { premultiply: false });
10422
+ this._emptyDemTexture.bind(context.gl.NEAREST, context.gl.CLAMP_TO_EDGE);
10423
+ this._emptyDemMatrix = identity([]);
10424
+ }
10425
+ const sourceTile = this.tileManager.getSourceTile(tileID, true);
10426
+ if (sourceTile?.dem && (!sourceTile.demTexture || sourceTile.needsTerrainPrepare)) {
10427
+ const context = this.painter.context;
10428
+ sourceTile.demTexture ||= this.painter.getTileTexture(sourceTile.dem.stride);
10429
+ if (sourceTile.demTexture) sourceTile.demTexture.update(sourceTile.dem.getPixels(), { premultiply: false });
10430
+ else sourceTile.demTexture = new Texture(context, sourceTile.dem.getPixels(), context.gl.RGBA, { premultiply: false });
10431
+ sourceTile.demTexture.bind(context.gl.NEAREST, context.gl.CLAMP_TO_EDGE);
10432
+ sourceTile.needsTerrainPrepare = false;
10433
+ }
10434
+ const terrainMatrix = sourceTile ? this._getDEMTileMatrix(tileID, sourceTile) : this._emptyDemMatrix;
10435
+ return {
10436
+ "u_depth": 2,
10437
+ "u_terrain": 3,
10438
+ "u_terrain_dim": sourceTile?.dem?.dim || 1,
10439
+ "u_terrain_matrix": terrainMatrix,
10440
+ "u_terrain_unpack": sourceTile?.dem?.getUnpackVector() || this._emptyDemUnpack,
10441
+ "u_terrain_exaggeration": this.exaggeration,
10442
+ texture: (sourceTile?.demTexture || this._emptyDemTexture).texture,
10443
+ depthTexture: (this._fboDepthTexture || this._emptyDepthTexture).texture,
10444
+ tile: sourceTile
10445
+ };
10001
10446
  }
10002
- get latRange() {
10003
- return this._helper.latRange;
10447
+ /**
10448
+ * get a framebuffer as big as the map-div, which will be used to render depth into a texture
10449
+ * @returns the frame buffer
10450
+ */
10451
+ getFramebuffer() {
10452
+ const painter = this.painter;
10453
+ const width = painter.width / devicePixelRatio;
10454
+ const height = painter.height / devicePixelRatio;
10455
+ if (this._fbo && (this._fbo.width !== width || this._fbo.height !== height)) {
10456
+ this._fbo.destroy();
10457
+ this._fboDepthTexture.destroy();
10458
+ delete this._fbo;
10459
+ delete this._fboDepthTexture;
10460
+ }
10461
+ if (!this._fboDepthTexture) {
10462
+ this._fboDepthTexture = new Texture(painter.context, {
10463
+ width,
10464
+ height,
10465
+ data: null
10466
+ }, painter.context.gl.RGBA, { premultiply: false });
10467
+ this._fboDepthTexture.bind(painter.context.gl.NEAREST, painter.context.gl.CLAMP_TO_EDGE);
10468
+ }
10469
+ if (!this._fbo) {
10470
+ this._fbo = painter.context.createFramebuffer(width, height, true, false);
10471
+ this._fbo.depthAttachment.set(painter.context.createRenderbuffer(painter.context.gl.DEPTH_COMPONENT16, width, height));
10472
+ }
10473
+ this._fbo.colorAttachment.set(this._fboDepthTexture.texture);
10474
+ return this._fbo;
10004
10475
  }
10005
- get minZoom() {
10006
- return this._helper.minZoom;
10476
+ /**
10477
+ * Reads the depth value from the depth-framebuffer at a given screen pixel
10478
+ * @param p - Screen coordinate
10479
+ * @returns depth value in clip space (between 0 and 1)
10480
+ */
10481
+ depthAtPoint(p) {
10482
+ const rgba = /* @__PURE__ */ new Uint8Array(4);
10483
+ const context = this.painter.context, gl = context.gl;
10484
+ context.bindFramebuffer.set(this.getFramebuffer().framebuffer);
10485
+ gl.readPixels(p.x, this.painter.height / devicePixelRatio - p.y - 1, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, rgba);
10486
+ context.bindFramebuffer.set(null);
10487
+ return (rgba[0] / 16777216 + rgba[1] / 65536 + rgba[2] / 256 + rgba[3]) / 256;
10007
10488
  }
10008
- get maxZoom() {
10009
- return this._helper.maxZoom;
10489
+ /**
10490
+ * create a regular mesh which will be used by all terrain-tiles
10491
+ * @returns the created regular mesh
10492
+ */
10493
+ getTerrainMesh(tileId) {
10494
+ const globeEnabled = this.painter.style.projection?.transitionState > 0;
10495
+ const northPole = globeEnabled && tileId.canonical.y === 0;
10496
+ const southPole = globeEnabled && tileId.canonical.y === (1 << tileId.canonical.z) - 1;
10497
+ const key = `m_${northPole ? "n" : ""}_${southPole ? "s" : ""}`;
10498
+ if (this._meshCache[key]) return this._meshCache[key];
10499
+ const context = this.painter.context;
10500
+ const vertexArray = new Pos3dArray();
10501
+ const indexArray = new TriangleIndexArray();
10502
+ const meshSize = this.meshSize;
10503
+ const delta = EXTENT / meshSize;
10504
+ const meshSize2 = meshSize * meshSize;
10505
+ for (let y = 0; y <= meshSize; y++) for (let x = 0; x <= meshSize; x++) vertexArray.emplaceBack(x * delta, y * delta, 0);
10506
+ for (let y = 0; y < meshSize2; y += meshSize + 1) for (let x = 0; x < meshSize; x++) {
10507
+ indexArray.emplaceBack(x + y, meshSize + x + y + 1, meshSize + x + y + 2);
10508
+ indexArray.emplaceBack(x + y, meshSize + x + y + 2, x + y + 1);
10509
+ }
10510
+ if (this._terrainSkirtLength !== "none") this._buildSkirts(vertexArray, indexArray, meshSize, delta, northPole, southPole);
10511
+ const mesh = new Mesh(context.createVertexBuffer(vertexArray, pos3dAttributes.members), context.createIndexBuffer(indexArray), SegmentVector.simpleSegment(0, 0, vertexArray.length, indexArray.length));
10512
+ this._meshCache[key] = mesh;
10513
+ return mesh;
10010
10514
  }
10011
- get zoom() {
10012
- return this._helper.zoom;
10515
+ /**
10516
+ * Calculates the height of the tile skirts for the "auto" strategy.
10517
+ * @see {@link MapOptions.terrainSkirtLength}
10518
+ * @param zoom - current zoomlevel
10519
+ * @returns the elevation delta in meters
10520
+ */
10521
+ getSkirtLength(zoom) {
10522
+ return 2 * Math.PI * earthRadius / Math.pow(2, Math.max(zoom, 0)) / 5;
10013
10523
  }
10014
- get center() {
10015
- return this._helper.center;
10524
+ getMinTileElevationForLngLatZoom(lnglat, zoom) {
10525
+ if (!isInBoundsForZoomLngLat(zoom, lnglat.wrap())) return 0;
10526
+ const { tileID } = this._getOverscaledTileIDFromLngLatZoom(lnglat, zoom);
10527
+ return this.getMinMaxElevation(tileID).minElevation ?? 0;
10016
10528
  }
10017
- get minPitch() {
10018
- return this._helper.minPitch;
10529
+ /**
10530
+ * Get the minimum and maximum elevation contained in a tile. This includes any
10531
+ * exaggeration included in the terrain.
10532
+ *
10533
+ * @param tileID - ID of the tile to be used as a source for the min/max elevation
10534
+ * @returns the minimum and maximum elevation found in the tile, including the terrain's
10535
+ * exaggeration
10536
+ */
10537
+ getMinMaxElevation(tileID) {
10538
+ const tile = this.tileManager.getSourceTile(tileID, true);
10539
+ const minMax = {
10540
+ minElevation: null,
10541
+ maxElevation: null
10542
+ };
10543
+ if (tile?.dem) {
10544
+ minMax.minElevation = tile.dem.min * this.exaggeration;
10545
+ minMax.maxElevation = tile.dem.max * this.exaggeration;
10546
+ }
10547
+ return minMax;
10019
10548
  }
10020
- get maxPitch() {
10021
- return this._helper.maxPitch;
10549
+ _getOverscaledTileIDFromLngLatZoom(lnglat, zoom) {
10550
+ const mercatorCoordinate = MercatorCoordinate.fromLngLat(lnglat.wrap());
10551
+ const worldSize = (1 << zoom) * EXTENT;
10552
+ const mercatorX = mercatorCoordinate.x * worldSize;
10553
+ const mercatorY = mercatorCoordinate.y * worldSize;
10554
+ const tileX = Math.floor(mercatorX / EXTENT), tileY = Math.floor(mercatorY / EXTENT);
10555
+ return {
10556
+ tileID: new OverscaledTileID(zoom, 0, zoom, tileX, tileY),
10557
+ mercatorX,
10558
+ mercatorY
10559
+ };
10022
10560
  }
10023
- get pitch() {
10024
- return this._helper.pitch;
10561
+ /** Add an extra frame around the mesh to avoid hairline gaps (stitching) on tile boundaries with different zoomlevels.
10562
+ * @see {@link MapOptions.terrainSkirtLength}
10563
+ */
10564
+ _buildSkirts(vertexArray, indexArray, meshSize, delta, northPole, southPole) {
10565
+ const offsetTop = vertexArray.length;
10566
+ const offsetTopEdge = 0;
10567
+ const offsetBottom = offsetTop + (meshSize + 1);
10568
+ const offsetBottomEdge = (meshSize + 1) * meshSize;
10569
+ const northY = northPole ? NORTH_POLE_Y : 0;
10570
+ const northZ = northPole ? 0 : 1;
10571
+ const southY = southPole ? SOUTH_POLE_Y : EXTENT;
10572
+ const southZ = southPole ? 0 : 1;
10573
+ for (let x = 0; x <= meshSize; x++) vertexArray.emplaceBack(x * delta, northY, northZ);
10574
+ for (let x = 0; x <= meshSize; x++) vertexArray.emplaceBack(x * delta, southY, southZ);
10575
+ for (let x = 0; x < meshSize; x++) {
10576
+ indexArray.emplaceBack(offsetBottomEdge + x, offsetBottom + x, offsetBottom + x + 1);
10577
+ indexArray.emplaceBack(offsetBottomEdge + x, offsetBottom + x + 1, offsetBottomEdge + x + 1);
10578
+ indexArray.emplaceBack(offsetTopEdge + x, offsetTop + x + 1, offsetTop + x);
10579
+ indexArray.emplaceBack(offsetTopEdge + x, offsetTopEdge + x + 1, offsetTop + x + 1);
10580
+ }
10581
+ const offsetLeft = vertexArray.length;
10582
+ const offsetRight = offsetLeft + (meshSize + 1) * 2;
10583
+ for (const x of [0, 1]) for (let y = 0; y <= meshSize; y++) for (const z of [0, 1]) vertexArray.emplaceBack(x * EXTENT, y * delta, z);
10584
+ for (let y = 0; y < meshSize * 2; y += 2) {
10585
+ indexArray.emplaceBack(offsetLeft + y, offsetLeft + y + 1, offsetLeft + y + 3);
10586
+ indexArray.emplaceBack(offsetLeft + y, offsetLeft + y + 3, offsetLeft + y + 2);
10587
+ indexArray.emplaceBack(offsetRight + y, offsetRight + y + 3, offsetRight + y + 1);
10588
+ indexArray.emplaceBack(offsetRight + y, offsetRight + y + 2, offsetRight + y + 3);
10589
+ }
10025
10590
  }
10026
- get pitchInRadians() {
10027
- return this._helper.pitchInRadians;
10591
+ };
10592
+ const NOT_COVERED = {
10593
+ covered: false,
10594
+ elevation: 0
10595
+ };
10596
+ /**
10597
+ * Elevation of the rendered terrain surface at a mercator position, and whether it is covered at all.
10598
+ * A covered tile whose DEM has not loaded yet is flat at zero, which is what the terrain mesh renders.
10599
+ */
10600
+ function sampleAt(index, exaggeration, mercatorX, mercatorY) {
10601
+ if (mercatorY < 0 || mercatorY >= 1) return NOT_COVERED;
10602
+ const wrap = Math.floor(mercatorX);
10603
+ const wrappedX = mercatorX - wrap;
10604
+ for (const z of index.zooms) {
10605
+ const scale = 1 << z;
10606
+ const scaledX = wrappedX * scale;
10607
+ const scaledY = mercatorY * scale;
10608
+ const tileX = Math.floor(scaledX);
10609
+ const tileY = Math.floor(scaledY);
10610
+ const key = `${wrap}/${z}/${tileX}/${tileY}`;
10611
+ if (!index.samplerPerTile.has(key)) continue;
10612
+ const sampler = index.samplerPerTile.get(key);
10613
+ if (!sampler) return {
10614
+ covered: true,
10615
+ elevation: 0
10616
+ };
10617
+ return {
10618
+ covered: true,
10619
+ elevation: sampler(Math.min((scaledX - tileX) * EXTENT, MAX_TILE_COORD), Math.min((scaledY - tileY) * EXTENT, MAX_TILE_COORD), EXTENT) * exaggeration
10620
+ };
10028
10621
  }
10029
- get roll() {
10030
- return this._helper.roll;
10622
+ return NOT_COVERED;
10623
+ }
10624
+ /**
10625
+ * Whether a height in meters is at or below the sampled terrain surface.
10626
+ * The epsilon absorbs rounding when a bracket endpoint lands exactly on the surface.
10627
+ */
10628
+ function isBelowTerrainSample(sample, height) {
10629
+ return sample.covered && height <= sample.elevation + HIT_EPSILON_M;
10630
+ }
10631
+ /**
10632
+ * Narrows the bracket `[lo, hi]` around the surface crossing until it is shorter than `tolerance` in ray parameter units.
10633
+ */
10634
+ function bisect(ray, isBelowTerrain, lo, hi, tolerance) {
10635
+ for (let j = 0; j < MAX_BISECTIONS && hi - lo > tolerance; j++) {
10636
+ const mid = (lo + hi) / 2;
10637
+ if (isBelowTerrain(ray, mid)) hi = mid;
10638
+ else lo = mid;
10031
10639
  }
10032
- get rollInRadians() {
10033
- return this._helper.rollInRadians;
10640
+ return {
10641
+ lo,
10642
+ hi
10643
+ };
10644
+ }
10645
+ //#endregion
10646
+ //#region src/geo/projection/mercator_transform.ts
10647
+ const TARGET_WORLD_STEP_PX = 4;
10648
+ const MAX_SAMPLES = 512;
10649
+ const MERCATOR_BISECT_EPSILON_WORLD_PX = .001;
10650
+ var MercatorTransform = class MercatorTransform {
10651
+ get pixelsToClipSpaceMatrix() {
10652
+ return this._helper.pixelsToClipSpaceMatrix;
10034
10653
  }
10035
- get bearing() {
10036
- return this._helper.bearing;
10654
+ get clipSpaceToPixelsMatrix() {
10655
+ return this._helper.clipSpaceToPixelsMatrix;
10037
10656
  }
10038
- get bearingInRadians() {
10039
- return this._helper.bearingInRadians;
10657
+ get pixelsToGLUnits() {
10658
+ return this._helper.pixelsToGLUnits;
10040
10659
  }
10041
- get fov() {
10042
- return this._helper.fov;
10660
+ get centerOffset() {
10661
+ return this._helper.centerOffset;
10043
10662
  }
10044
- get fovInRadians() {
10045
- return this._helper.fovInRadians;
10663
+ get size() {
10664
+ return this._helper.size;
10046
10665
  }
10047
- get elevation() {
10048
- return this._helper.elevation;
10666
+ get rotationMatrix() {
10667
+ return this._helper.rotationMatrix;
10049
10668
  }
10050
- get minElevationForCurrentTile() {
10051
- return this._helper.minElevationForCurrentTile;
10669
+ get centerPoint() {
10670
+ return this._helper.centerPoint;
10052
10671
  }
10053
- get padding() {
10054
- return this._helper.padding;
10672
+ get pixelsPerMeter() {
10673
+ return this._helper.pixelsPerMeter;
10055
10674
  }
10056
- get unmodified() {
10057
- return this._helper.unmodified;
10675
+ setMinZoom(zoom) {
10676
+ this._helper.setMinZoom(zoom);
10058
10677
  }
10059
- get renderWorldCopies() {
10678
+ setMaxZoom(zoom) {
10679
+ this._helper.setMaxZoom(zoom);
10680
+ }
10681
+ setMinPitch(pitch) {
10682
+ this._helper.setMinPitch(pitch);
10683
+ }
10684
+ setMaxPitch(pitch) {
10685
+ this._helper.setMaxPitch(pitch);
10686
+ }
10687
+ setRenderWorldCopies(renderWorldCopies) {
10688
+ this._helper.setRenderWorldCopies(renderWorldCopies);
10689
+ }
10690
+ setBearing(bearing) {
10691
+ this._helper.setBearing(bearing);
10692
+ }
10693
+ setPitch(pitch) {
10694
+ this._helper.setPitch(pitch);
10695
+ }
10696
+ setRoll(roll) {
10697
+ this._helper.setRoll(roll);
10698
+ }
10699
+ setFov(fov) {
10700
+ this._helper.setFov(fov);
10701
+ }
10702
+ setZoom(zoom) {
10703
+ this._helper.setZoom(zoom);
10704
+ }
10705
+ setCenter(center) {
10706
+ this._helper.setCenter(center);
10707
+ }
10708
+ setElevation(elevation) {
10709
+ this._helper.setElevation(elevation);
10710
+ }
10711
+ setMinElevationForCurrentTile(elevation) {
10712
+ this._helper.setMinElevationForCurrentTile(elevation);
10713
+ }
10714
+ setPadding(padding) {
10715
+ this._helper.setPadding(padding);
10716
+ }
10717
+ interpolatePadding(start, target, t) {
10718
+ this._helper.interpolatePadding(start, target, t);
10719
+ }
10720
+ isPaddingEqual(padding) {
10721
+ return this._helper.isPaddingEqual(padding);
10722
+ }
10723
+ resize(width, height, constrain = true) {
10724
+ this._helper.resize(width, height, constrain);
10725
+ }
10726
+ getMaxBounds() {
10727
+ return this._helper.getMaxBounds();
10728
+ }
10729
+ setMaxBounds(bounds) {
10730
+ this._helper.setMaxBounds(bounds);
10731
+ }
10732
+ setConstrainOverride(constrain) {
10733
+ this._helper.setConstrainOverride(constrain);
10734
+ }
10735
+ overrideNearFarZ(nearZ, farZ) {
10736
+ this._helper.overrideNearFarZ(nearZ, farZ);
10737
+ }
10738
+ clearNearFarZOverride() {
10739
+ this._helper.clearNearFarZOverride();
10740
+ }
10741
+ getCameraQueryGeometry(queryGeometry) {
10742
+ return this._helper.getCameraQueryGeometry(this.getCameraPoint(), queryGeometry);
10743
+ }
10744
+ get tileSize() {
10745
+ return this._helper.tileSize;
10746
+ }
10747
+ get tileZoom() {
10748
+ return this._helper.tileZoom;
10749
+ }
10750
+ get scale() {
10751
+ return this._helper.scale;
10752
+ }
10753
+ get worldSize() {
10754
+ return this._helper.worldSize;
10755
+ }
10756
+ get width() {
10757
+ return this._helper.width;
10758
+ }
10759
+ get height() {
10760
+ return this._helper.height;
10761
+ }
10762
+ get lngRange() {
10763
+ return this._helper.lngRange;
10764
+ }
10765
+ get latRange() {
10766
+ return this._helper.latRange;
10767
+ }
10768
+ get minZoom() {
10769
+ return this._helper.minZoom;
10770
+ }
10771
+ get maxZoom() {
10772
+ return this._helper.maxZoom;
10773
+ }
10774
+ get zoom() {
10775
+ return this._helper.zoom;
10776
+ }
10777
+ get center() {
10778
+ return this._helper.center;
10779
+ }
10780
+ get minPitch() {
10781
+ return this._helper.minPitch;
10782
+ }
10783
+ get maxPitch() {
10784
+ return this._helper.maxPitch;
10785
+ }
10786
+ get pitch() {
10787
+ return this._helper.pitch;
10788
+ }
10789
+ get pitchInRadians() {
10790
+ return this._helper.pitchInRadians;
10791
+ }
10792
+ get roll() {
10793
+ return this._helper.roll;
10794
+ }
10795
+ get rollInRadians() {
10796
+ return this._helper.rollInRadians;
10797
+ }
10798
+ get bearing() {
10799
+ return this._helper.bearing;
10800
+ }
10801
+ get bearingInRadians() {
10802
+ return this._helper.bearingInRadians;
10803
+ }
10804
+ get fov() {
10805
+ return this._helper.fov;
10806
+ }
10807
+ get fovInRadians() {
10808
+ return this._helper.fovInRadians;
10809
+ }
10810
+ get elevation() {
10811
+ return this._helper.elevation;
10812
+ }
10813
+ get minElevationForCurrentTile() {
10814
+ return this._helper.minElevationForCurrentTile;
10815
+ }
10816
+ get padding() {
10817
+ return this._helper.padding;
10818
+ }
10819
+ get unmodified() {
10820
+ return this._helper.unmodified;
10821
+ }
10822
+ get renderWorldCopies() {
10060
10823
  return this._helper.renderWorldCopies;
10061
10824
  }
10062
10825
  get cameraToCenterDistance() {
@@ -10228,17 +10991,64 @@ var MercatorTransform = class MercatorTransform {
10228
10991
  }
10229
10992
  screenPointToMercatorCoordinate(p, terrain) {
10230
10993
  if (terrain) {
10231
- const coordinate = terrain.pointCoordinate(p);
10994
+ const coordinate = this.screenTerrainPointToMercatorCoordinate(p, terrain);
10232
10995
  if (coordinate != null) return coordinate;
10233
10996
  }
10234
10997
  return this.screenPointToMercatorCoordinateAtZ(p);
10235
10998
  }
10999
+ /** {@inheritDoc ITransform.screenTerrainPointToMercatorCoordinate} */
11000
+ screenTerrainPointToMercatorCoordinate(p, terrain) {
11001
+ const index = terrain.getCoverageIndex();
11002
+ if (!index) return null;
11003
+ const { near, far } = this.getRaySegmentFromPixel(p);
11004
+ const worldSize = this.worldSize;
11005
+ const dx = far[0] - near[0];
11006
+ const dy = far[1] - near[1];
11007
+ const dz = far[2] - near[2];
11008
+ const ray = {
11009
+ index,
11010
+ exaggeration: terrain.exaggeration,
11011
+ near,
11012
+ dx,
11013
+ dy,
11014
+ dz,
11015
+ worldSize
11016
+ };
11017
+ let tStart = 0;
11018
+ let tEnd = 1;
11019
+ if (dz === 0) {
11020
+ if (near[2] > index.maxElevation || near[2] < index.minElevation) return null;
11021
+ } else {
11022
+ const tHigh = (index.maxElevation - near[2]) / dz;
11023
+ const tLow = (index.minElevation - near[2]) / dz;
11024
+ tStart = Math.max(tStart, Math.min(tHigh, tLow));
11025
+ tEnd = Math.min(tEnd, Math.max(tHigh, tLow));
11026
+ if (tStart > tEnd) return null;
11027
+ }
11028
+ const horizontalLength = Math.hypot(dx, dy);
11029
+ const samples = clamp(Math.ceil(horizontalLength * (tEnd - tStart) / TARGET_WORLD_STEP_PX), 1, MAX_SAMPLES);
11030
+ let previousT = 0;
11031
+ let aboveTerrain = !mercatorIsBelowTerrain(ray, 0);
11032
+ for (let i = 0; i <= samples; i++) {
11033
+ const t = tStart + (tEnd - tStart) * i / samples;
11034
+ if (!aboveTerrain) aboveTerrain = !mercatorIsBelowTerrain(ray, t);
11035
+ else if (mercatorIsBelowTerrain(ray, t)) {
11036
+ const { lo, hi } = bisect(ray, mercatorIsBelowTerrain, previousT, t, MERCATOR_BISECT_EPSILON_WORLD_PX / horizontalLength);
11037
+ const sampleLo = mercatorSampleAt(ray, lo);
11038
+ const sampleHi = mercatorSampleAt(ray, hi);
11039
+ const fLo = near[2] + lo * dz - sampleLo.elevation;
11040
+ const fHi = near[2] + hi * dz - sampleHi.elevation;
11041
+ const hit = sampleLo.covered && fLo > fHi ? clamp(lo + fLo * (hi - lo) / (fLo - fHi), lo, hi) : hi;
11042
+ return new MercatorCoordinate((near[0] + hit * dx) / worldSize, (near[1] + hit * dy) / worldSize, mercatorSampleAt(ray, hit).elevation);
11043
+ }
11044
+ previousT = t;
11045
+ }
11046
+ return null;
11047
+ }
10236
11048
  /**
10237
- * Intersects the ray through a screen point with the horizontal plane at `z`,
10238
- * given in meters relative to the plane at the center's elevation (not mercator units).
11049
+ * Returns the segment of the ray through the given screen pixel that lies inside the view frustum.
10239
11050
  */
10240
- screenPointToMercatorCoordinateAtZ(p, z) {
10241
- const targetZ = z ? z : 0;
11051
+ getRaySegmentFromPixel(p) {
10242
11052
  const coord0 = [
10243
11053
  p.x,
10244
11054
  p.y,
@@ -10255,14 +11065,29 @@ var MercatorTransform = class MercatorTransform {
10255
11065
  transformMat4(coord1, coord1, this._pixelMatrixInverse);
10256
11066
  const w0 = coord0[3];
10257
11067
  const w1 = coord1[3];
10258
- const x0 = coord0[0] / w0;
10259
- const x1 = coord1[0] / w1;
10260
- const y0 = coord0[1] / w0;
10261
- const y1 = coord1[1] / w1;
10262
- const z0 = coord0[2] / w0;
10263
- const z1 = coord1[2] / w1;
10264
- const t = z0 === z1 ? 0 : (targetZ - z0) / (z1 - z0);
10265
- return new MercatorCoordinate(interpolateFactory.number(x0, x1, t) / this.worldSize, interpolateFactory.number(y0, y1, t) / this.worldSize, targetZ);
11068
+ const elevation = this.elevation;
11069
+ return {
11070
+ near: [
11071
+ coord0[0] / w0,
11072
+ coord0[1] / w0,
11073
+ coord0[2] / w0 + elevation
11074
+ ],
11075
+ far: [
11076
+ coord1[0] / w1,
11077
+ coord1[1] / w1,
11078
+ coord1[2] / w1 + elevation
11079
+ ]
11080
+ };
11081
+ }
11082
+ /**
11083
+ * Intersects the ray through a screen point with the horizontal plane at `z`,
11084
+ * given in meters relative to the plane at the center's elevation (not mercator units).
11085
+ */
11086
+ screenPointToMercatorCoordinateAtZ(p, z) {
11087
+ const targetZ = z ? z : 0;
11088
+ const { near, far } = this.getRaySegmentFromPixel(p);
11089
+ const t = near[2] === far[2] ? 0 : (targetZ + this.elevation - near[2]) / (far[2] - near[2]);
11090
+ return new MercatorCoordinate(interpolateFactory.number(near[0], far[0], t) / this.worldSize, interpolateFactory.number(near[1], far[1], t) / this.worldSize, targetZ);
10266
11091
  }
10267
11092
  /**
10268
11093
  * Given a coordinate, return the screen point that corresponds to it
@@ -10286,7 +11111,7 @@ var MercatorTransform = class MercatorTransform {
10286
11111
  return new LngLatBounds().extend(this.screenPointToLocation(new Point(0, top))).extend(this.screenPointToLocation(new Point(this._helper._width, top))).extend(this.screenPointToLocation(new Point(this._helper._width, this._helper._height))).extend(this.screenPointToLocation(new Point(0, this._helper._height)));
10287
11112
  }
10288
11113
  isPointOnMapSurface(p, terrain) {
10289
- if (terrain) return terrain.pointCoordinate(p) != null;
11114
+ if (terrain) return this.screenTerrainPointToMercatorCoordinate(p, terrain) != null;
10290
11115
  return p.y > this.height / 2 - getMercatorHorizon(this);
10291
11116
  }
10292
11117
  calculatePosMatrix(tileID, aligned = false, useFloat32 = false) {
@@ -10523,14 +11348,14 @@ var MercatorTransform = class MercatorTransform {
10523
11348
  getRayDirectionFromPixel(_p) {
10524
11349
  throw new Error("Not implemented.");
10525
11350
  }
10526
- projectTileCoordinates(x, y, unwrappedTileID, getElevation) {
11351
+ projectTileCoordinates(x, y, unwrappedTileID, elevation) {
10527
11352
  const matrix = this.calculatePosMatrix(unwrappedTileID);
10528
11353
  let pos;
10529
- if (getElevation) {
11354
+ if (elevation != null) {
10530
11355
  pos = [
10531
11356
  x,
10532
11357
  y,
10533
- getElevation(x, y),
11358
+ elevation,
10534
11359
  1
10535
11360
  ];
10536
11361
  transformMat4(pos, pos, matrix);
@@ -10584,6 +11409,12 @@ var MercatorTransform = class MercatorTransform {
10584
11409
  return this.calculatePosMatrix(tileID);
10585
11410
  }
10586
11411
  };
11412
+ function mercatorSampleAt(ray, t) {
11413
+ return sampleAt(ray.index, ray.exaggeration, (ray.near[0] + t * ray.dx) / ray.worldSize, (ray.near[1] + t * ray.dy) / ray.worldSize);
11414
+ }
11415
+ function mercatorIsBelowTerrain(ray, t) {
11416
+ return isBelowTerrainSample(mercatorSampleAt(ray, t), ray.near[2] + t * ray.dz);
11417
+ }
10587
11418
  //#endregion
10588
11419
  //#region src/geo/projection/camera_helper.ts
10589
11420
  /**
@@ -11234,6 +12065,36 @@ function interpolateLngLatForGlobe(start, deltaLng, deltaLat, t) {
11234
12065
  return new LngLat(interpolatedLng, interpolatedLat);
11235
12066
  }
11236
12067
  }
12068
+ /**
12069
+ * Returns the two intersection points of the ray and a sphere centered at the origin,
12070
+ * or null if no intersection occurs.
12071
+ * The intersections are encoded as the parameter for parametric ray equation,
12072
+ * with `tMin` being the first intersection and `tMax` being the second.
12073
+ * The quadratic is solved as suggested in Ray Tracing Gems, chapter 7, since the
12074
+ * schoolbook approach leads to floating point precision issues:
12075
+ * https://www.realtimerendering.com/raytracinggems/rtg/index.html
12076
+ * @param origin - The ray origin.
12077
+ * @param direction - The normalized ray direction.
12078
+ * @param radius - The sphere radius, defaults to the unit sphere of the planet.
12079
+ */
12080
+ function raySphereIntersection(origin, direction, radius = 1) {
12081
+ const originDotDirection = dot(origin, direction);
12082
+ const radiusSquared = radius * radius;
12083
+ const inner = createVec3f64();
12084
+ const scaledDir = createVec3f64();
12085
+ scale$2(scaledDir, direction, originDotDirection);
12086
+ sub(inner, origin, scaledDir);
12087
+ const discriminant = radiusSquared - dot(inner, inner);
12088
+ if (discriminant < 0) return null;
12089
+ const c = dot(origin, origin) - radiusSquared;
12090
+ const q = -originDotDirection + (originDotDirection < 0 ? 1 : -1) * Math.sqrt(discriminant);
12091
+ const t0 = c / q;
12092
+ const t1 = q;
12093
+ return {
12094
+ tMin: Math.min(t0, t1),
12095
+ tMax: Math.max(t0, t1)
12096
+ };
12097
+ }
11237
12098
  //#endregion
11238
12099
  //#region src/util/primitives/bounding_volume_cache.ts
11239
12100
  var BoundingVolumeCache = class {
@@ -11669,6 +12530,10 @@ function findAxisMinMax(axis, points) {
11669
12530
  }
11670
12531
  //#endregion
11671
12532
  //#region src/geo/projection/vertical_perspective_transform.ts
12533
+ const GLOBE_SAMPLES = 256;
12534
+ const GLOBE_BISECT_EPSILON_T = 1e-12;
12535
+ /** Latitudes outside the mercator range project past the world edge; the globe mesh still covers them. */
12536
+ const MAX_MERCATOR_Y = 1 - 1e-9;
11672
12537
  var VerticalPerspectiveTransform = class VerticalPerspectiveTransform {
11673
12538
  get pixelsToClipSpaceMatrix() {
11674
12539
  return this._helper.pixelsToClipSpaceMatrix;
@@ -12008,10 +12873,10 @@ var VerticalPerspectiveTransform = class VerticalPerspectiveTransform {
12008
12873
  const angular = mercatorCoordinatesToAngularCoordinatesRadians(mercator.x, mercator.y);
12009
12874
  return this.getCircleRadiusCorrection() / Math.cos(angular[1]);
12010
12875
  }
12011
- projectTileCoordinates(x, y, unwrappedTileID, getElevation) {
12876
+ projectTileCoordinates(x, y, unwrappedTileID, elevation) {
12012
12877
  const canonical = unwrappedTileID.canonical;
12013
12878
  const spherePos = projectTileCoordinatesToSphere(x, y, canonical.x, canonical.y, canonical.z);
12014
- const vectorMultiplier = 1 + (getElevation ? getElevation(x, y) : 0) / earthRadius;
12879
+ const vectorMultiplier = 1 + (elevation ?? 0) / earthRadius;
12015
12880
  const pos = [
12016
12881
  spherePos[0] * vectorMultiplier,
12017
12882
  spherePos[1] * vectorMultiplier,
@@ -12271,11 +13136,41 @@ var VerticalPerspectiveTransform = class VerticalPerspectiveTransform {
12271
13136
  }
12272
13137
  screenPointToMercatorCoordinate(p, terrain) {
12273
13138
  if (terrain) {
12274
- const coordinate = terrain.pointCoordinate(p);
13139
+ const coordinate = this.screenTerrainPointToMercatorCoordinate(p, terrain);
12275
13140
  if (coordinate) return coordinate;
12276
13141
  }
12277
13142
  return MercatorCoordinate.fromLngLat(this.unprojectScreenPoint(p));
12278
13143
  }
13144
+ /** {@inheritDoc ITransform.screenTerrainPointToMercatorCoordinate} */
13145
+ screenTerrainPointToMercatorCoordinate(p, terrain) {
13146
+ const index = terrain.getCoverageIndex();
13147
+ if (!index) return null;
13148
+ const origin = this.cameraPosition;
13149
+ const direction = this.getRayDirectionFromPixel(p);
13150
+ const outer = raySphereIntersection(origin, direction, 1 + index.maxElevation / earthRadius);
13151
+ if (!outer) return null;
13152
+ const inner = raySphereIntersection(origin, direction, 1 + index.minElevation / earthRadius);
13153
+ const tStart = Math.max(outer.tMin, 0);
13154
+ const tEnd = inner ? inner.tMin : outer.tMax;
13155
+ if (tEnd <= tStart) return null;
13156
+ const ray = {
13157
+ index,
13158
+ exaggeration: terrain.exaggeration,
13159
+ origin,
13160
+ direction
13161
+ };
13162
+ let previousT = 0;
13163
+ for (let i = 0; i <= GLOBE_SAMPLES; i++) {
13164
+ const t = tStart + (tEnd - tStart) * i / GLOBE_SAMPLES;
13165
+ if (globeIsBelowTerrain(ray, t)) {
13166
+ const { hi } = bisect(ray, globeIsBelowTerrain, previousT, t, GLOBE_BISECT_EPSILON_T);
13167
+ const { sample, mercator } = globeSampleAt(ray, hi);
13168
+ return new MercatorCoordinate(mercator.x, mercator.y, sample.elevation);
13169
+ }
13170
+ previousT = t;
13171
+ }
13172
+ return null;
13173
+ }
12279
13174
  screenPointToLocation(p, terrain) {
12280
13175
  return this.screenPointToMercatorCoordinate(p, terrain)?.toLngLat();
12281
13176
  }
@@ -12284,8 +13179,7 @@ var VerticalPerspectiveTransform = class VerticalPerspectiveTransform {
12284
13179
  }
12285
13180
  isPointOnMapSurface(p, _terrain) {
12286
13181
  const rayOrigin = this._cameraPosition;
12287
- const rayDirection = this.getRayDirectionFromPixel(p);
12288
- return !!this.rayPlanetIntersection(rayOrigin, rayDirection);
13182
+ return !!raySphereIntersection(rayOrigin, this.getRayDirectionFromPixel(p));
12289
13183
  }
12290
13184
  /**
12291
13185
  * Computes normalized direction of a ray from the camera to the given screen pixel.
@@ -12330,33 +13224,6 @@ var VerticalPerspectiveTransform = class VerticalPerspectiveTransform {
12330
13224
  return projected[0] > -1 && projected[0] < 1 && projected[1] > -1 && projected[1] < 1 && projected[2] > -1 && projected[2] < 1;
12331
13225
  }
12332
13226
  /**
12333
- * Returns the two intersection points of the ray and the planet's sphere,
12334
- * or null if no intersection occurs.
12335
- * The intersections are encoded as the parameter for parametric ray equation,
12336
- * with `tMin` being the first intersection and `tMax` being the second.
12337
- * Eg. the nearer intersection point can then be computed as `origin + direction * tMin`.
12338
- * @param origin - The ray origin.
12339
- * @param direction - The normalized ray direction.
12340
- */
12341
- rayPlanetIntersection(origin, direction) {
12342
- const originDotDirection = dot(origin, direction);
12343
- const planetRadiusSquared = 1;
12344
- const inner = createVec3f64();
12345
- const scaledDir = createVec3f64();
12346
- scale$2(scaledDir, direction, originDotDirection);
12347
- sub(inner, origin, scaledDir);
12348
- const discriminant = planetRadiusSquared - dot(inner, inner);
12349
- if (discriminant < 0) return null;
12350
- const c = dot(origin, origin) - planetRadiusSquared;
12351
- const q = -originDotDirection + (originDotDirection < 0 ? 1 : -1) * Math.sqrt(discriminant);
12352
- const t0 = c / q;
12353
- const t1 = q;
12354
- return {
12355
- tMin: Math.min(t0, t1),
12356
- tMax: Math.max(t0, t1)
12357
- };
12358
- }
12359
- /**
12360
13227
  * @internal
12361
13228
  * Returns a {@link LngLat} representing geographical coordinates that correspond to the specified pixel coordinates.
12362
13229
  * Note: if the point does not lie on the globe, returns a location on the visible globe horizon (edge) that is
@@ -12367,7 +13234,7 @@ var VerticalPerspectiveTransform = class VerticalPerspectiveTransform {
12367
13234
  unprojectScreenPoint(p) {
12368
13235
  const rayOrigin = this._cameraPosition;
12369
13236
  const rayDirection = this.getRayDirectionFromPixel(p);
12370
- const intersection = this.rayPlanetIntersection(rayOrigin, rayDirection);
13237
+ const intersection = raySphereIntersection(rayOrigin, rayDirection);
12371
13238
  if (intersection) {
12372
13239
  const intersectionPoint = createVec3f64();
12373
13240
  add(intersectionPoint, rayOrigin, [
@@ -12421,6 +13288,30 @@ var VerticalPerspectiveTransform = class VerticalPerspectiveTransform {
12421
13288
  }
12422
13289
  getFastPathSimpleProjectionMatrix(_tileID) {}
12423
13290
  };
13291
+ function globeSampleAt(ray, t) {
13292
+ const position = createVec3f64();
13293
+ scaleAndAdd(position, ray.origin, ray.direction, t);
13294
+ const radius = length$1(position);
13295
+ const surface = createVec3f64();
13296
+ scale$2(surface, position, 1 / radius);
13297
+ const lngLat = sphereSurfacePointToCoordinates(surface);
13298
+ const projected = MercatorCoordinate.fromLngLat(lngLat);
13299
+ const mercator = new MercatorCoordinate(projected.x, clamp(projected.y, 0, MAX_MERCATOR_Y));
13300
+ const sample = sampleAt(ray.index, ray.exaggeration, mercator.x, mercator.y);
13301
+ const elevation = Math.abs(lngLat.lat) > 85.051129 ? 0 : sample.elevation;
13302
+ return {
13303
+ sample: {
13304
+ covered: sample.covered,
13305
+ elevation
13306
+ },
13307
+ radius,
13308
+ mercator
13309
+ };
13310
+ }
13311
+ function globeIsBelowTerrain(ray, t) {
13312
+ const { sample, radius } = globeSampleAt(ray, t);
13313
+ return isBelowTerrainSample(sample, (radius - 1) * earthRadius);
13314
+ }
12424
13315
  //#endregion
12425
13316
  //#region src/geo/projection/globe_transform.ts
12426
13317
  /**
@@ -12703,8 +13594,8 @@ var GlobeTransform = class GlobeTransform {
12703
13594
  const verticalCorrection = this._verticalPerspectiveTransform.getPitchedTextCorrection(textAnchorX, textAnchorY, tileID);
12704
13595
  return lerp(mercatorCorrection, verticalCorrection, this._globeness);
12705
13596
  }
12706
- projectTileCoordinates(x, y, unwrappedTileID, getElevation) {
12707
- return this.currentTransform.projectTileCoordinates(x, y, unwrappedTileID, getElevation);
13597
+ projectTileCoordinates(x, y, unwrappedTileID, elevation) {
13598
+ return this.currentTransform.projectTileCoordinates(x, y, unwrappedTileID, elevation);
12708
13599
  }
12709
13600
  _calcMatrices() {
12710
13601
  if (!this._helper._width || !this._helper._height) return;
@@ -12777,6 +13668,10 @@ var GlobeTransform = class GlobeTransform {
12777
13668
  screenPointToMercatorCoordinate(p, terrain) {
12778
13669
  return this.currentTransform.screenPointToMercatorCoordinate(p, terrain);
12779
13670
  }
13671
+ /** {@inheritDoc ITransform.screenTerrainPointToMercatorCoordinate} */
13672
+ screenTerrainPointToMercatorCoordinate(p, terrain) {
13673
+ return this.currentTransform.screenTerrainPointToMercatorCoordinate(p, terrain);
13674
+ }
12780
13675
  screenPointToLocation(p, terrain) {
12781
13676
  return this.currentTransform.screenPointToLocation(p, terrain);
12782
13677
  }
@@ -14556,11 +15451,6 @@ const terrainUniforms = (context, locations) => ({
14556
15451
  "u_is_globe_mode": new Uniform1f(context, locations.u_is_globe_mode)
14557
15452
  });
14558
15453
  const terrainDepthUniforms = (context, locations) => ({ "u_ele_delta": new Uniform1f(context, locations.u_ele_delta) });
14559
- const terrainCoordsUniforms = (context, locations) => ({
14560
- "u_texture": new Uniform1i(context, locations.u_texture),
14561
- "u_terrain_coords_id": new Uniform1f(context, locations.u_terrain_coords_id),
14562
- "u_ele_delta": new Uniform1f(context, locations.u_ele_delta)
14563
- });
14564
15454
  const terrainUniformValues = (eleDelta, fogMatrix, sky, pitch, isGlobeMode) => ({
14565
15455
  "u_texture": 0,
14566
15456
  "u_ele_delta": eleDelta,
@@ -14573,11 +15463,6 @@ const terrainUniformValues = (eleDelta, fogMatrix, sky, pitch, isGlobeMode) => (
14573
15463
  "u_is_globe_mode": isGlobeMode ? 1 : 0
14574
15464
  });
14575
15465
  const terrainDepthUniformValues = (eleDelta) => ({ "u_ele_delta": eleDelta });
14576
- const terrainCoordsUniformValues = (coordsId, eleDelta) => ({
14577
- "u_terrain_coords_id": coordsId / 255,
14578
- "u_texture": 0,
14579
- "u_ele_delta": eleDelta
14580
- });
14581
15466
  //#endregion
14582
15467
  //#region src/webgl/program/projection_program.ts
14583
15468
  const projectionUniforms = (context, locations) => ({
@@ -15207,7 +16092,8 @@ const symbolIconUniforms = (context, locations) => ({
15207
16092
  "u_texture": new Uniform1i(context, locations.u_texture),
15208
16093
  "u_translation": new Uniform2f(context, locations.u_translation),
15209
16094
  "u_pitched_scale": new Uniform1f(context, locations.u_pitched_scale),
15210
- "u_is_offset": new Uniform1i(context, locations.u_is_offset)
16095
+ "u_is_offset": new Uniform1i(context, locations.u_is_offset),
16096
+ "u_height_anchor_ground": new Uniform1i(context, locations.u_height_anchor_ground)
15211
16097
  });
15212
16098
  const symbolSDFUniforms = (context, locations) => ({
15213
16099
  "u_is_size_zoom_constant": new Uniform1i(context, locations.u_is_size_zoom_constant),
@@ -15233,7 +16119,8 @@ const symbolSDFUniforms = (context, locations) => ({
15233
16119
  "u_is_plain": new Uniform1i(context, locations.u_is_plain),
15234
16120
  "u_translation": new Uniform2f(context, locations.u_translation),
15235
16121
  "u_pitched_scale": new Uniform1f(context, locations.u_pitched_scale),
15236
- "u_is_offset": new Uniform1i(context, locations.u_is_offset)
16122
+ "u_is_offset": new Uniform1i(context, locations.u_is_offset),
16123
+ "u_height_anchor_ground": new Uniform1i(context, locations.u_height_anchor_ground)
15237
16124
  });
15238
16125
  const symbolTextAndIconUniforms = (context, locations) => ({
15239
16126
  "u_is_size_zoom_constant": new Uniform1i(context, locations.u_is_size_zoom_constant),
@@ -15260,9 +16147,10 @@ const symbolTextAndIconUniforms = (context, locations) => ({
15260
16147
  "u_is_halo": new Uniform1i(context, locations.u_is_halo),
15261
16148
  "u_translation": new Uniform2f(context, locations.u_translation),
15262
16149
  "u_pitched_scale": new Uniform1f(context, locations.u_pitched_scale),
15263
- "u_is_offset": new Uniform1i(context, locations.u_is_offset)
16150
+ "u_is_offset": new Uniform1i(context, locations.u_is_offset),
16151
+ "u_height_anchor_ground": new Uniform1i(context, locations.u_height_anchor_ground)
15264
16152
  });
15265
- const symbolIconUniformValues = (functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, isText, texSize, pitchedScale, isOffset) => {
16153
+ const symbolIconUniformValues = (functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, isText, texSize, pitchedScale, isOffset, heightAnchorGround) => {
15266
16154
  const transform = painter.transform;
15267
16155
  return {
15268
16156
  "u_is_size_zoom_constant": +(functionType === "constant" || functionType === "source"),
@@ -15284,20 +16172,21 @@ const symbolIconUniformValues = (functionType, size, rotateInShader, pitchWithMa
15284
16172
  "u_texture": 0,
15285
16173
  "u_translation": translation,
15286
16174
  "u_pitched_scale": pitchedScale,
15287
- "u_is_offset": isOffset
16175
+ "u_is_offset": isOffset,
16176
+ "u_height_anchor_ground": +heightAnchorGround
15288
16177
  };
15289
16178
  };
15290
- const symbolSDFUniformValues = (functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, isText, texSize, isHalo, pitchedScale, isOffset) => {
16179
+ const symbolSDFUniformValues = (functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, isText, texSize, isHalo, pitchedScale, isOffset, heightAnchorGround) => {
15291
16180
  const transform = painter.transform;
15292
- return extend(symbolIconUniformValues(functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, isText, texSize, pitchedScale, isOffset), {
16181
+ return extend(symbolIconUniformValues(functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, isText, texSize, pitchedScale, isOffset, heightAnchorGround), {
15293
16182
  "u_gamma_scale": pitchWithMap ? Math.cos(transform.pitch * Math.PI / 180) * transform.cameraToCenterDistance : 1,
15294
16183
  "u_device_pixel_ratio": painter.pixelRatio,
15295
16184
  "u_is_halo": isHalo ? 1 : 0,
15296
16185
  "u_is_plain": 1
15297
16186
  });
15298
16187
  };
15299
- const symbolTextAndIconUniformValues = (functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, texSizeSDF, texSizeIcon, pitchedScale, isOffset) => {
15300
- return extend(symbolSDFUniformValues(functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, true, texSizeSDF, true, pitchedScale, isOffset), {
16188
+ const symbolTextAndIconUniformValues = (functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, texSizeSDF, texSizeIcon, pitchedScale, isOffset, heightAnchorGround) => {
16189
+ return extend(symbolSDFUniformValues(functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, true, texSizeSDF, true, pitchedScale, isOffset, heightAnchorGround), {
15301
16190
  "u_texsize_icon": texSizeIcon,
15302
16191
  "u_texture_icon": 1
15303
16192
  });
@@ -15409,7 +16298,6 @@ const programUniforms = {
15409
16298
  backgroundPattern: backgroundPatternUniforms,
15410
16299
  terrain: terrainUniforms,
15411
16300
  terrainDepth: terrainDepthUniforms,
15412
- terrainCoords: terrainCoordsUniforms,
15413
16301
  atmosphere: atmosphereUniforms,
15414
16302
  sky: skyUniforms
15415
16303
  };
@@ -16412,8 +17300,8 @@ function updateVariableAnchors(coords, painter, layer, tileManager, rotationAlig
16412
17300
  const updateTextFitIcon = layer.layout.get("icon-text-fit") !== "none" && bucket.hasIconData();
16413
17301
  if (size) {
16414
17302
  const tileScale = Math.pow(2, transform.zoom - tile.tileID.overscaledZ);
16415
- const getElevation = terrain ? (x, y) => terrain.getElevation(coord, x, y) : null;
16416
- updateVariableAnchorsForBucket(bucket, rotateWithMap, pitchWithMap, variableOffsets, transform, pitchedLabelPlaneMatrix, tileScale, size, updateTextFitIcon, translatePosition(transform, tile, translate, translateAnchor), coord.toUnwrapped(), getElevation);
17303
+ const getElevation = terrain ? (x, y) => terrain.getElevation(coord, x, y) : void 0;
17304
+ updateVariableAnchorsForBucket(bucket, rotateWithMap, pitchWithMap, variableOffsets, transform, pitchedLabelPlaneMatrix, tileScale, size, updateTextFitIcon, translatePosition(transform, tile, translate, translateAnchor), coord.toUnwrapped(), getElevation, layer.layout.get("symbol-height-anchor") === "ground");
16417
17305
  }
16418
17306
  }
16419
17307
  }
@@ -16423,14 +17311,14 @@ function getShiftedAnchor(projectedAnchorPoint, projectionContext, rotateWithMap
16423
17311
  let adjustedShift = shift.mult(pitchedTextShiftCorrection);
16424
17312
  if (!rotateWithMap) adjustedShift = adjustedShift.rotate(-transformAngle);
16425
17313
  const tileAnchorShifted = translatedAnchor.add(adjustedShift);
16426
- return projectWithMatrix(tileAnchorShifted.x, tileAnchorShifted.y, projectionContext.pitchedLabelPlaneMatrix, projectionContext.getElevation).point;
17314
+ return projectWithMatrix(tileAnchorShifted.x, tileAnchorShifted.y, projectionContext.pitchedLabelPlaneMatrix, elevationAt(projectionContext, tileAnchorShifted.x, tileAnchorShifted.y)).point;
16427
17315
  } else if (rotateWithMap) {
16428
17316
  const east = projectTileCoordinatesToLabelPlane(projectionContext.tileAnchorPoint.x + 1, projectionContext.tileAnchorPoint.y, projectionContext).point.sub(projectedAnchorPoint);
16429
17317
  const angle = Math.atan(east.y / east.x) + (east.x < 0 ? Math.PI : 0);
16430
17318
  return projectedAnchorPoint.add(shift.rotate(angle));
16431
17319
  } else return projectedAnchorPoint.add(shift);
16432
17320
  }
16433
- function updateVariableAnchorsForBucket(bucket, rotateWithMap, pitchWithMap, variableOffsets, transform, pitchedLabelPlaneMatrix, tileScale, size, updateTextFitIcon, translation, unwrappedTileID, getElevation) {
17321
+ function updateVariableAnchorsForBucket(bucket, rotateWithMap, pitchWithMap, variableOffsets, transform, pitchedLabelPlaneMatrix, tileScale, size, updateTextFitIcon, translation, unwrappedTileID, getElevation, heightAnchorGround) {
16434
17322
  const placedSymbols = bucket.text.placedSymbolArray;
16435
17323
  const dynamicTextLayoutVertexArray = bucket.text.dynamicLayoutVertexArray;
16436
17324
  const dynamicIconLayoutVertexArray = bucket.icon.dynamicLayoutVertexArray;
@@ -16445,6 +17333,8 @@ function updateVariableAnchorsForBucket(bucket, rotateWithMap, pitchWithMap, var
16445
17333
  const tileAnchor = new Point(symbol.anchorX, symbol.anchorY);
16446
17334
  const projectionContext = {
16447
17335
  getElevation,
17336
+ heightOffset: symbol.heightOffset,
17337
+ heightAnchorGround,
16448
17338
  width: transform.width,
16449
17339
  height: transform.height,
16450
17340
  pitchedLabelPlaneMatrix,
@@ -16554,10 +17444,11 @@ function drawLayerSymbols(painter, tileManager, layer, coords, isText, translate
16554
17444
  const hasVariableAnchors = hasVariablePlacement && bucket.hasTextData();
16555
17445
  const updateTextFitIcon = layer.layout.get("icon-text-fit") !== "none" && hasVariableAnchors && bucket.hasIconData();
16556
17446
  const isOffset = layer._unevaluatedLayout.hasValue("icon-offset");
17447
+ const heightAnchorGround = layer.layout.get("symbol-height-anchor") === "ground";
16557
17448
  if (alongLine) {
16558
17449
  const pitchedLabelPlaneMatrixInverse = create$1();
16559
17450
  fastInvertTransformMat4(pitchedLabelPlaneMatrixInverse, pitchedLabelPlaneMatrix);
16560
- const getElevation = painter.style.map.terrain ? (x, y) => painter.style.map.terrain.getElevation(coord, x, y) : null;
17451
+ const getElevation = painter.style.map.terrain ? (x, y) => painter.style.map.terrain.getElevation(coord, x, y) : void 0;
16561
17452
  updateLineLabels(bucket, painter, isText, pitchedLabelPlaneMatrix, pitchedLabelPlaneMatrixInverse, pitchWithMap, keepUpright, layer.layout.get("text-rotation-alignment") === "map", coord.toUnwrapped(), transform.width, transform.height, translation, getElevation);
16562
17453
  }
16563
17454
  const shaderVariableAnchor = isText && hasVariablePlacement || updateTextFitIcon;
@@ -16566,9 +17457,9 @@ function drawLayerSymbols(painter, tileManager, layer, coords, isText, translate
16566
17457
  const hasHalo = isSDF && layer.paint.get(isText ? "text-halo-width" : "icon-halo-width").constantOr(1) !== 0;
16567
17458
  let uniformValues;
16568
17459
  if (isSDF) {
16569
- if (!bucket.iconsInText) uniformValues = symbolSDFUniformValues(sizeData.kind, size, rotateInShader, pitchWithMap, alongLine, shaderVariableAnchor, painter, uLabelPlaneMatrix, glCoordMatrixForShader, translation, isText, texSize, hasHalo, pitchedTextRescaling, isOffset);
16570
- else uniformValues = symbolTextAndIconUniformValues(sizeData.kind, size, rotateInShader, pitchWithMap, alongLine, shaderVariableAnchor, painter, uLabelPlaneMatrix, glCoordMatrixForShader, translation, texSize, texSizeIcon, pitchedTextRescaling, isOffset);
16571
- } else uniformValues = symbolIconUniformValues(sizeData.kind, size, rotateInShader, pitchWithMap, alongLine, shaderVariableAnchor, painter, uLabelPlaneMatrix, glCoordMatrixForShader, translation, isText, texSize, pitchedTextRescaling, isOffset);
17460
+ if (!bucket.iconsInText) uniformValues = symbolSDFUniformValues(sizeData.kind, size, rotateInShader, pitchWithMap, alongLine, shaderVariableAnchor, painter, uLabelPlaneMatrix, glCoordMatrixForShader, translation, isText, texSize, hasHalo, pitchedTextRescaling, isOffset, heightAnchorGround);
17461
+ else uniformValues = symbolTextAndIconUniformValues(sizeData.kind, size, rotateInShader, pitchWithMap, alongLine, shaderVariableAnchor, painter, uLabelPlaneMatrix, glCoordMatrixForShader, translation, texSize, texSizeIcon, pitchedTextRescaling, isOffset, heightAnchorGround);
17462
+ } else uniformValues = symbolIconUniformValues(sizeData.kind, size, rotateInShader, pitchWithMap, alongLine, shaderVariableAnchor, painter, uLabelPlaneMatrix, glCoordMatrixForShader, translation, isText, texSize, pitchedTextRescaling, isOffset, heightAnchorGround);
16572
17463
  const state = {
16573
17464
  program,
16574
17465
  buffers,
@@ -17380,18 +18271,18 @@ function renderColorRelief(painter, tileManager, layer, coords, stencilModes, de
17380
18271
  }
17381
18272
  if (!dem?.data) continue;
17382
18273
  const textureStride = dem.stride;
17383
- const pixelData = dem.getPixels();
17384
18274
  context.activeTexture.set(gl.TEXTURE0);
17385
- context.pixelStoreUnpackPremultiplyAlpha.set(false);
17386
- tile.demTexture ||= painter.getTileTexture(textureStride);
17387
- if (tile.demTexture) {
17388
- const demTexture = tile.demTexture;
17389
- demTexture.update(pixelData, { premultiply: false });
17390
- demTexture.bind(textureFilter, gl.CLAMP_TO_EDGE);
17391
- } else {
17392
- tile.demTexture = new Texture(context, pixelData, gl.RGBA, { premultiply: false });
17393
- tile.demTexture.bind(textureFilter, gl.CLAMP_TO_EDGE);
18275
+ if (!tile.demTexture || tile.needsColorReliefPrepare) {
18276
+ context.pixelStoreUnpackPremultiplyAlpha.set(false);
18277
+ tile.demTexture ||= painter.getTileTexture(textureStride) ?? new Texture(context, {
18278
+ width: textureStride,
18279
+ height: textureStride,
18280
+ data: null
18281
+ }, gl.RGBA);
18282
+ tile.demTexture.update(dem.getPixels(), { premultiply: false });
18283
+ tile.needsColorReliefPrepare = false;
17394
18284
  }
18285
+ tile.demTexture.bind(textureFilter, gl.CLAMP_TO_EDGE);
17395
18286
  const mesh = projection.getMeshFromTileID(context, coord.canonical, useBorder, true, "raster");
17396
18287
  const terrainData = painter.getTerrainDataForTile(coord, isRenderingToTexture);
17397
18288
  const projectionData = transform.getProjectionData({
@@ -17746,7 +18637,7 @@ function drawDepth(painter, terrain) {
17746
18637
  const depthMode = new DepthMode(gl.LEQUAL, DepthMode.ReadWrite, [0, 1]);
17747
18638
  const tiles = terrain.tileManager.getRenderableTiles();
17748
18639
  const program = painter.useProgram("terrainDepth");
17749
- context.bindFramebuffer.set(terrain.getFramebuffer("depth").framebuffer);
18640
+ context.bindFramebuffer.set(terrain.getFramebuffer().framebuffer);
17750
18641
  context.viewport.set([
17751
18642
  0,
17752
18643
  0,
@@ -17754,7 +18645,7 @@ function drawDepth(painter, terrain) {
17754
18645
  painter.height / devicePixelRatio
17755
18646
  ]);
17756
18647
  context.clear({
17757
- color: Color.transparent,
18648
+ color: Color.white,
17758
18649
  depth: 1
17759
18650
  });
17760
18651
  for (const tile of tiles) {
@@ -17776,54 +18667,6 @@ function drawDepth(painter, terrain) {
17776
18667
  painter.height
17777
18668
  ]);
17778
18669
  }
17779
- /**
17780
- * Redraw the Coords Framebuffers
17781
- * @param painter - the painter
17782
- * @param terrain - the terrain
17783
- */
17784
- function drawCoords(painter, terrain) {
17785
- const context = painter.context;
17786
- const gl = context.gl;
17787
- const tr = painter.transform;
17788
- const colorMode = ColorMode.unblended;
17789
- const depthMode = new DepthMode(gl.LEQUAL, DepthMode.ReadWrite, [0, 1]);
17790
- const coords = terrain.getCoordsTexture();
17791
- const tiles = terrain.tileManager.getRenderableTiles();
17792
- const program = painter.useProgram("terrainCoords");
17793
- context.bindFramebuffer.set(terrain.getFramebuffer("coords").framebuffer);
17794
- context.viewport.set([
17795
- 0,
17796
- 0,
17797
- painter.width / devicePixelRatio,
17798
- painter.height / devicePixelRatio
17799
- ]);
17800
- context.clear({
17801
- color: Color.transparent,
17802
- depth: 1
17803
- });
17804
- terrain.coordsIndex = [];
17805
- for (const tile of tiles) {
17806
- const mesh = terrain.getTerrainMesh(tile.tileID);
17807
- const terrainData = terrain.getTerrainData(tile.tileID);
17808
- context.activeTexture.set(gl.TEXTURE0);
17809
- gl.bindTexture(gl.TEXTURE_2D, coords.texture);
17810
- const uniformValues = terrainCoordsUniformValues(255 - terrain.coordsIndex.length, terrain.getSkirtLength(tr.zoom));
17811
- const projectionData = tr.getProjectionData({
17812
- overscaledTileID: tile.tileID,
17813
- applyTerrainMatrix: false,
17814
- applyGlobeMatrix: true
17815
- });
17816
- program.draw(context, gl.TRIANGLES, depthMode, StencilMode.disabled, colorMode, CullFaceMode.backCCW, uniformValues, terrainData, projectionData, "terrain", mesh.vertexBuffer, mesh.indexBuffer, mesh.segments);
17817
- terrain.coordsIndex.push(tile.tileID.key);
17818
- }
17819
- context.bindFramebuffer.set(null);
17820
- context.viewport.set([
17821
- 0,
17822
- 0,
17823
- painter.width,
17824
- painter.height
17825
- ]);
17826
- }
17827
18670
  function drawTerrain(painter, terrain, tiles, renderOptions) {
17828
18671
  const { isRenderingGlobe } = renderOptions;
17829
18672
  const context = painter.context;
@@ -17952,8 +18795,7 @@ const webglDrawFunctions = {
17952
18795
  custom: drawCustom,
17953
18796
  debug: drawDebug,
17954
18797
  debugPadding: drawDebugPadding,
17955
- terrainDepth: drawDepth,
17956
- terrainCoords: drawCoords
18798
+ terrainDepth: drawDepth
17957
18799
  };
17958
18800
  //#endregion
17959
18801
  //#region src/render/painter.ts
@@ -17972,7 +18814,6 @@ var Painter = class Painter {
17972
18814
  this._rttSharedFbo = null;
17973
18815
  this.terrainFacilitator = {
17974
18816
  depthDirty: true,
17975
- coordsDirty: false,
17976
18817
  matrix: identity(/* @__PURE__ */ new Float64Array(16)),
17977
18818
  renderTime: 0
17978
18819
  };
@@ -18273,7 +19114,7 @@ var Painter = class Painter {
18273
19114
  break;
18274
19115
  }
18275
19116
  }
18276
- this.maybeDrawDepth(false);
19117
+ this.maybeDrawDepth();
18277
19118
  if (this.renderToTexture) {
18278
19119
  this.renderToTexture.prepareForRender(this.style, this.transform.zoom);
18279
19120
  this.opaquePassCutoff = 0;
@@ -18335,30 +19176,20 @@ var Painter = class Painter {
18335
19176
  }
18336
19177
  /**
18337
19178
  * Update the depth framebuffer if the camera has moved or tiles have reloaded.
18338
- * Marks coords as depthDirty so they are re-rendered on next demand.
18339
19179
  */
18340
- maybeDrawDepth(requireExact) {
19180
+ maybeDrawDepth() {
18341
19181
  if (!this.style?.map?.terrain) return;
18342
19182
  const prevMatrix = this.terrainFacilitator.matrix;
18343
19183
  const currMatrix = this.transform.modelViewProjectionMatrix;
18344
19184
  let doUpdate = this.terrainFacilitator.depthDirty;
18345
- doUpdate ||= requireExact ? !exactEquals(prevMatrix, currMatrix) : !equals(prevMatrix, currMatrix);
19185
+ doUpdate ||= !equals(prevMatrix, currMatrix);
18346
19186
  doUpdate ||= this.style.map.terrain.tileManager.anyTilesAfterTime(this.terrainFacilitator.renderTime);
18347
19187
  if (!doUpdate) return;
18348
19188
  copy(prevMatrix, currMatrix);
18349
19189
  this.terrainFacilitator.renderTime = now();
18350
19190
  this.terrainFacilitator.depthDirty = false;
18351
- this.terrainFacilitator.coordsDirty = true;
18352
19191
  this.drawFunctions.terrainDepth(this, this.style.map.terrain);
18353
19192
  }
18354
- /**
18355
- * Render the coords framebuffer if it is coordsDirty
18356
- */
18357
- maybeDrawCoords() {
18358
- if (!this.style?.map?.terrain || !this.terrainFacilitator.coordsDirty) return;
18359
- this.terrainFacilitator.coordsDirty = false;
18360
- this.drawFunctions.terrainCoords(this, this.style.map.terrain);
18361
- }
18362
19193
  renderLayer(painter, tileManager, layer, coords, renderOptions) {
18363
19194
  if (layer.isHidden(this.transform.zoom)) return;
18364
19195
  if (layer.type !== "background" && layer.type !== "custom" && !(coords || []).length) return;
@@ -21094,7 +21925,7 @@ var HandlerManager = class {
21094
21925
  _terrainGestureElevation(terrain, around, aroundOnSurface, tr, combinedEventsInProgress) {
21095
21926
  if (!aroundOnSurface) return;
21096
21927
  if (!this._terrainMovement && (combinedEventsInProgress.drag || combinedEventsInProgress.zoom)) {
21097
- const anchor = terrain.pointCoordinate(around);
21928
+ const anchor = tr.screenTerrainPointToMercatorCoordinate(around, terrain);
21098
21929
  this._terrainGestureAnchorElevation = anchor ? anchor.z : null;
21099
21930
  }
21100
21931
  if (this._terrainGestureAnchorElevation === null) return;
@@ -21908,855 +22739,155 @@ var AttributionControl = class {
21908
22739
  this._container.remove();
21909
22740
  this._map.off("styledata", this._updateData);
21910
22741
  this._map.off("sourcedata", this._updateData);
21911
- this._map.off("terrain", this._updateData);
21912
- this._map.off("resize", this._updateCompact);
21913
- this._map.off("drag", this._updateCompactMinimize);
21914
- this._map = void 0;
21915
- this._compact = void 0;
21916
- this._attribHTML = void 0;
21917
- }
21918
- _setElementTitle(element, title) {
21919
- const str = this._map._getUIString(`AttributionControl.${title}`);
21920
- element.title = str;
21921
- element.setAttribute("aria-label", str);
21922
- }
21923
- _updateAttributions() {
21924
- if (!this._map.style) return;
21925
- let attributions = [];
21926
- if (this.options.customAttribution) {
21927
- if (Array.isArray(this.options.customAttribution)) attributions = attributions.concat(this.options.customAttribution.map((attribution) => {
21928
- if (typeof attribution !== "string") return "";
21929
- return attribution;
21930
- }));
21931
- else if (typeof this.options.customAttribution === "string") attributions.push(this.options.customAttribution);
21932
- }
21933
- if (this._map.style.stylesheet) {
21934
- const stylesheet = this._map.style.stylesheet;
21935
- this.styleOwner = stylesheet.owner;
21936
- this.styleId = stylesheet.id;
21937
- }
21938
- const tileManagers = this._map.style.tileManagers;
21939
- for (const id in tileManagers) {
21940
- const tileManager = tileManagers[id];
21941
- if (tileManager.used || tileManager.usedForTerrain) {
21942
- const source = tileManager.getSource();
21943
- if (source.attribution && !attributions.includes(source.attribution)) attributions.push(source.attribution);
21944
- }
21945
- }
21946
- attributions = attributions.filter((e) => String(e).trim());
21947
- attributions.sort((a, b) => a.length - b.length);
21948
- attributions = attributions.filter((attrib, i) => {
21949
- for (let j = i + 1; j < attributions.length; j++) if (attributions[j].includes(attrib)) return false;
21950
- return true;
21951
- });
21952
- const attribHTML = attributions.join(" | ");
21953
- if (attribHTML === this._attribHTML) return;
21954
- this._attribHTML = attribHTML;
21955
- if (attributions.length) {
21956
- this._innerContainer.innerHTML = DOM.sanitize(attribHTML);
21957
- this._container.classList.remove("maplibregl-attrib-empty");
21958
- } else this._container.classList.add("maplibregl-attrib-empty");
21959
- this._updateCompact();
21960
- this._editLink = null;
21961
- }
21962
- };
21963
- //#endregion
21964
- //#region src/ui/control/logo_control.ts
21965
- /**
21966
- * A `LogoControl` is a control that adds the watermark.
21967
- *
21968
- * @group Markers and Controls
21969
- *
21970
- * @example
21971
- * ```ts
21972
- * map.addControl(new LogoControl({compact: false}));
21973
- * ```
21974
- **/
21975
- var LogoControl = class {
21976
- /**
21977
- * @param options - the control's options
21978
- */
21979
- constructor(options = {}) {
21980
- this._updateCompact = () => {
21981
- const containerChildren = this._container.children;
21982
- if (containerChildren.length) {
21983
- const anchor = containerChildren[0];
21984
- if (this._map.getCanvasContainer().offsetWidth <= 640 || this._compact) {
21985
- if (this._compact !== false) anchor.classList.add("maplibregl-compact");
21986
- } else anchor.classList.remove("maplibregl-compact");
21987
- }
21988
- };
21989
- this.options = options;
21990
- }
21991
- getDefaultPosition() {
21992
- return "bottom-left";
21993
- }
21994
- /** {@inheritDoc IControl.onAdd} */
21995
- onAdd(map) {
21996
- this._map = map;
21997
- this._compact = this.options?.compact;
21998
- this._container = DOM.create("div", "maplibregl-ctrl");
21999
- const anchor = DOM.create("a", "maplibregl-ctrl-logo");
22000
- anchor.target = "_blank";
22001
- anchor.rel = "noopener nofollow";
22002
- anchor.href = "https://maplibre.org/";
22003
- anchor.setAttribute("aria-label", this._map._getUIString("LogoControl.Title"));
22004
- anchor.setAttribute("rel", "noopener nofollow");
22005
- this._container.appendChild(anchor);
22006
- this._container.style.display = "block";
22007
- this._map.on("resize", this._updateCompact);
22008
- this._updateCompact();
22009
- return this._container;
22010
- }
22011
- /** {@inheritDoc IControl.onRemove} */
22012
- onRemove() {
22013
- this._container.remove();
22014
- this._map.off("resize", this._updateCompact);
22015
- this._map = void 0;
22016
- this._compact = void 0;
22017
- }
22018
- };
22019
- //#endregion
22020
- //#region src/util/task_queue.ts
22021
- var TaskQueue = class {
22022
- constructor() {
22023
- this._queue = [];
22024
- this._id = 0;
22025
- this._cleared = false;
22026
- this._currentlyRunning = false;
22027
- }
22028
- add(callback) {
22029
- const id = ++this._id;
22030
- this._queue.push({
22031
- callback,
22032
- id,
22033
- cancelled: false
22034
- });
22035
- return id;
22036
- }
22037
- remove(id) {
22038
- const running = this._currentlyRunning;
22039
- const queue = running ? this._queue.concat(running) : this._queue;
22040
- for (const task of queue) if (task.id === id) {
22041
- task.cancelled = true;
22042
- return;
22043
- }
22044
- }
22045
- run(timeStamp = 0) {
22046
- if (this._currentlyRunning) throw new Error("Attempting to run(), but is already running.");
22047
- const queue = this._currentlyRunning = this._queue;
22048
- this._queue = [];
22049
- for (const task of queue) {
22050
- if (task.cancelled) continue;
22051
- task.callback(timeStamp);
22052
- if (this._cleared) break;
22053
- }
22054
- this._cleared = false;
22055
- this._currentlyRunning = false;
22056
- }
22057
- clear() {
22058
- if (this._currentlyRunning) this._cleared = true;
22059
- this._queue = [];
22060
- }
22061
- };
22062
- //#endregion
22063
- //#region src/data/pos3d_attributes.ts
22064
- const pos3dAttributes = createLayout([{
22065
- name: "a_pos3d",
22066
- type: "Int16",
22067
- components: 3
22068
- }]);
22069
- //#endregion
22070
- //#region src/tile/terrain_tile_manager.ts
22071
- /**
22072
- * @internal
22073
- * This class is a helper for the Terrain-class, it:
22074
- *
22075
- * - loads raster-dem tiles
22076
- * - manages all renderToTexture tiles.
22077
- * - caches previous rendered tiles.
22078
- * - finds all necessary renderToTexture tiles for a OverscaledTileID area
22079
- * - finds the corresponding raster-dem tile for OverscaledTileID
22080
- */
22081
- var TerrainTileManager = class extends Evented {
22082
- constructor(tileManager) {
22083
- super();
22084
- this._lastTilesetChange = now();
22085
- this.tileManager = tileManager;
22086
- this._tiles = {};
22087
- this._renderableTilesKeys = [];
22088
- this._sourceTileCache = {};
22089
- this.minzoom = 0;
22090
- this.maxzoom = 22;
22091
- this.deltaZoom = 1;
22092
- this.tileSize = tileManager._source.tileSize * 2 ** this.deltaZoom;
22093
- tileManager.usedForTerrain = true;
22094
- tileManager.tileSize = this.tileSize;
22095
- }
22096
- destruct() {
22097
- this.tileManager.usedForTerrain = false;
22098
- this.tileManager.tileSize = null;
22099
- this.releaseAllRTT();
22100
- }
22101
- getSource() {
22102
- return this.tileManager._source;
22103
- }
22104
- /**
22105
- * Load Terrain Tiles, create internal render-to-texture tiles, free GPU memory.
22106
- * @param transform - the operation to do
22107
- * @param terrain - the terrain
22108
- */
22109
- update(transform, terrain) {
22110
- this.tileManager.update(transform, terrain);
22111
- this._renderableTilesKeys = [];
22112
- const keys = {};
22113
- for (const tileID of coveringTiles(transform, {
22114
- tileSize: this.tileSize,
22115
- minzoom: this.minzoom,
22116
- maxzoom: this.maxzoom,
22117
- reparseOverscaled: false,
22118
- terrain,
22119
- calculateTileZoom: this.tileManager._source.calculateTileZoom
22120
- })) {
22121
- keys[tileID.key] = true;
22122
- this._renderableTilesKeys.push(tileID.key);
22123
- if (!this._tiles[tileID.key]) {
22124
- tileID.terrainRttPosMatrix32f = /* @__PURE__ */ new Float32Array(16);
22125
- ortho(tileID.terrainRttPosMatrix32f, 0, EXTENT, EXTENT, 0, 0, 1);
22126
- this._tiles[tileID.key] = new Tile(tileID, this.tileSize);
22127
- this._lastTilesetChange = now();
22128
- }
22129
- }
22130
- for (const key in this._tiles) if (!keys[key]) {
22131
- this._tiles[key].releaseRTT(this.tileManager.map.painter);
22132
- delete this._tiles[key];
22133
- }
22134
- }
22135
- /**
22136
- * Release the RTT objects for `tileID` (and its ancestors/descendants),
22137
- */
22138
- releaseRTT(tileID) {
22139
- for (const key in this._tiles) {
22140
- const tile = this._tiles[key];
22141
- if (tile.tileID.equals(tileID) || tile.tileID.isChildOf(tileID) || tileID.isChildOf(tile.tileID)) tile.releaseRTT(this.tileManager.map.painter);
22142
- }
22143
- }
22144
- /**
22145
- * Release the A RTT objects for all tiles.
22146
- */
22147
- releaseAllRTT() {
22148
- for (const key in this._tiles) this._tiles[key].releaseRTT(this.tileManager.map.painter);
22149
- }
22150
- /**
22151
- * get a list of tiles, which are loaded and should be rendered in the current scene
22152
- * @returns the renderable tiles
22153
- */
22154
- getRenderableTiles() {
22155
- return this._renderableTilesKeys.map((key) => this.getTileByID(key));
22156
- }
22157
- /**
22158
- * get terrain tile by the TileID key
22159
- * @param id - the tile id
22160
- * @returns the tile
22161
- */
22162
- getTileByID(id) {
22163
- return this._tiles[id];
22164
- }
22165
- /**
22166
- * Searches for the corresponding current renderable terrain-tiles
22167
- * @param tileID - the tile to look for
22168
- * @returns the tiles that were found
22169
- */
22170
- getTerrainCoords(tileID, terrainTileRanges) {
22171
- if (terrainTileRanges) return this._getTerrainCoordsForTileRanges(tileID, terrainTileRanges);
22172
- else return this._getTerrainCoordsForRegularTile(tileID);
22173
- }
22174
- /**
22175
- * Searches for the corresponding current renderable terrain-tiles.
22176
- * Includes terrain tiles that are either:
22177
- * - the same as the tileID
22178
- * - a parent of the tileID
22179
- * - a child of the tileID
22180
- * @param tileID - the tile to look for
22181
- * @returns the tiles that were found
22182
- */
22183
- _getTerrainCoordsForRegularTile(tileID) {
22184
- const coords = {};
22185
- for (const key of this._renderableTilesKeys) {
22186
- const terrainTileID = this._tiles[key].tileID;
22187
- const coord = tileID.clone();
22188
- const mat = createMat4f64();
22189
- if (terrainTileID.canonical.equals(tileID.canonical)) ortho(mat, 0, EXTENT, EXTENT, 0, 0, 1);
22190
- else if (terrainTileID.canonical.isChildOf(tileID.canonical)) {
22191
- const dz = terrainTileID.canonical.z - tileID.canonical.z;
22192
- const dx = terrainTileID.canonical.x - (terrainTileID.canonical.x >> dz << dz);
22193
- const dy = terrainTileID.canonical.y - (terrainTileID.canonical.y >> dz << dz);
22194
- const size = EXTENT >> dz;
22195
- ortho(mat, 0, size, size, 0, 0, 1);
22196
- translate(mat, mat, [
22197
- -dx * size,
22198
- -dy * size,
22199
- 0
22200
- ]);
22201
- } else if (tileID.canonical.isChildOf(terrainTileID.canonical)) {
22202
- const dz = tileID.canonical.z - terrainTileID.canonical.z;
22203
- const dx = tileID.canonical.x - (tileID.canonical.x >> dz << dz);
22204
- const dy = tileID.canonical.y - (tileID.canonical.y >> dz << dz);
22205
- const size = EXTENT >> dz;
22206
- ortho(mat, 0, EXTENT, EXTENT, 0, 0, 1);
22207
- translate(mat, mat, [
22208
- dx * size,
22209
- dy * size,
22210
- 0
22211
- ]);
22212
- scale(mat, mat, [
22213
- 1 / 2 ** dz,
22214
- 1 / 2 ** dz,
22215
- 0
22216
- ]);
22217
- } else continue;
22218
- coord.terrainRttPosMatrix32f = new Float32Array(mat);
22219
- coords[key] = coord;
22220
- }
22221
- return coords;
22222
- }
22223
- /**
22224
- * Searches for the corresponding current renderable terrain-tiles.
22225
- * Includes terrain tiles that are within terrain tile ranges.
22226
- * @param tileID - the tile to look for
22227
- * @returns the tiles that were found
22228
- */
22229
- _getTerrainCoordsForTileRanges(tileID, terrainTileRanges) {
22230
- const coords = {};
22231
- for (const key of this._renderableTilesKeys) {
22232
- const terrainTileID = this._tiles[key].tileID;
22233
- if (!this._isWithinTileRanges(terrainTileID, terrainTileRanges)) continue;
22234
- const coord = tileID.clone();
22235
- const mat = createMat4f64();
22236
- if (terrainTileID.canonical.z === tileID.canonical.z) {
22237
- const dx = tileID.canonical.x - terrainTileID.canonical.x + tileID.wrap * (1 << tileID.canonical.z);
22238
- const dy = tileID.canonical.y - terrainTileID.canonical.y;
22239
- ortho(mat, 0, EXTENT, EXTENT, 0, 0, 1);
22240
- translate(mat, mat, [
22241
- dx * EXTENT,
22242
- dy * EXTENT,
22243
- 0
22244
- ]);
22245
- } else if (terrainTileID.canonical.z > tileID.canonical.z) {
22246
- const dz = terrainTileID.canonical.z - tileID.canonical.z;
22247
- const dx = terrainTileID.canonical.x - (terrainTileID.canonical.x >> dz << dz) + tileID.wrap * (1 << terrainTileID.canonical.z);
22248
- const dy = terrainTileID.canonical.y - (terrainTileID.canonical.y >> dz << dz);
22249
- const dx2 = tileID.canonical.x - (terrainTileID.canonical.x >> dz);
22250
- const dy2 = tileID.canonical.y - (terrainTileID.canonical.y >> dz);
22251
- const size = EXTENT >> dz;
22252
- ortho(mat, 0, size, size, 0, 0, 1);
22253
- translate(mat, mat, [
22254
- -dx * size + dx2 * EXTENT,
22255
- -dy * size + dy2 * EXTENT,
22256
- 0
22257
- ]);
22258
- } else {
22259
- const dz = tileID.canonical.z - terrainTileID.canonical.z;
22260
- const dx = tileID.canonical.x - (tileID.canonical.x >> dz << dz) + tileID.wrap * (1 << tileID.canonical.z);
22261
- const dy = tileID.canonical.y - (tileID.canonical.y >> dz << dz);
22262
- const dx2 = (tileID.canonical.x >> dz) - terrainTileID.canonical.x;
22263
- const dy2 = (tileID.canonical.y >> dz) - terrainTileID.canonical.y;
22264
- const size = EXTENT << dz;
22265
- ortho(mat, 0, size, size, 0, 0, 1);
22266
- translate(mat, mat, [
22267
- dx * EXTENT + dx2 * size,
22268
- dy * EXTENT + dy2 * size,
22269
- 0
22270
- ]);
22271
- }
22272
- coord.terrainRttPosMatrix32f = new Float32Array(mat);
22273
- coords[key] = coord;
22274
- }
22275
- return coords;
22276
- }
22277
- /**
22278
- * find the covering raster-dem tile
22279
- * @param tileID - the tile to look for
22280
- * @param searchForDEM - Optional parameter to search for (parent) source tiles with loaded dem.
22281
- * @returns the tile
22282
- */
22283
- getSourceTile(tileID, searchForDEM) {
22284
- const source = this.tileManager._source;
22285
- let z = tileID.overscaledZ - this.deltaZoom;
22286
- if (z > source.maxzoom) z = source.maxzoom;
22287
- if (z < source.minzoom) return void 0;
22288
- this._sourceTileCache[tileID.key] ||= tileID.scaledTo(z).key;
22289
- let tile = this.findTileInCaches(this._sourceTileCache[tileID.key]);
22290
- if (!tile?.dem && searchForDEM) while (z >= source.minzoom && !tile?.dem) tile = this.findTileInCaches(tileID.scaledTo(z--).key);
22291
- return tile;
22292
- }
22293
- findTileInCaches(key) {
22294
- let tile = this.tileManager.getTileByID(key);
22295
- if (tile) return tile;
22296
- tile = this.tileManager._outOfViewCache.getByKey(key);
22297
- return tile;
22298
- }
22299
- /**
22300
- * gets whether any tiles were loaded after a specific time. This is used to update depth & coords framebuffers.
22301
- * @param time - the time
22302
- * @returns true if any tiles came into view at or after the specified time
22303
- */
22304
- anyTilesAfterTime(time = now()) {
22305
- return this._lastTilesetChange >= time;
22306
- }
22307
- /**
22308
- * Checks whether a tile is within the canonical tile ranges.
22309
- * @param tileID - Tile to check
22310
- * @param canonicalTileRanges - Canonical tile ranges
22311
- * @returns
22312
- */
22313
- _isWithinTileRanges(tileID, canonicalTileRanges) {
22314
- const range = canonicalTileRanges[tileID.canonical.z];
22315
- return !!range && (tileID.wrap > range.minWrap || tileID.wrap < range.maxWrap || tileID.canonical.x >= range.minTileXWrapped && tileID.canonical.x <= range.maxTileXWrapped && tileID.canonical.y >= range.minTileY && tileID.canonical.y <= range.maxTileY);
22316
- }
22317
- };
22318
- //#endregion
22319
- //#region src/render/terrain.ts
22320
- /**
22321
- * @internal
22322
- * This is the main class which handles most of the 3D Terrain logic. It has the following topics:
22323
- *
22324
- * 1. loads raster-dem tiles via the internal tileManager this.tileManager
22325
- * 2. creates a depth-framebuffer, which is used to calculate the visibility of coordinates
22326
- * 3. creates a coords-framebuffer, which is used the get to tile-coordinate for a screen-pixel
22327
- * 4. stores all render-to-texture tiles in the this.tileManager._tiles
22328
- * 5. calculates the elevation for a specific tile-coordinate
22329
- * 6. creates a terrain-mesh
22330
- *
22331
- * A note about the GPU resource-usage:
22332
- *
22333
- * Framebuffers:
22334
- *
22335
- * - one for the depth & coords framebuffer with the size of the map-div.
22336
- * - one for rendering a tile to texture with the size of tileSize (= 512x512).
22337
- *
22338
- * Textures:
22339
- *
22340
- * - one texture for an empty raster-dem tile with size 1x1
22341
- * - one texture for an empty depth-buffer, when terrain is disabled with size 1x1
22342
- * - one texture for an each loaded raster-dem with size of the source.tileSize
22343
- * - one texture for the coords-framebuffer with the size of the map-div.
22344
- * - one texture for the depth-framebuffer with the size of the map-div.
22345
- * - one texture for the encoded tile-coords with the size 2*tileSize (=1024x1024)
22346
- * - finally for each render-to-texture tile (= this._tiles) a set of textures
22347
- * for each render stack (The stack-concept is documented in painter.ts).
22348
- *
22349
- * Normally there exists 1-3 Textures per tile, depending on the stylesheet.
22350
- * Each Textures has the size 2*tileSize (= 1024x1024). Also there exists a
22351
- * cache of the last 150 newest rendered tiles.
22352
- *
22353
- */
22354
- var Terrain = class {
22355
- constructor(painter, tileManager, options, terrainSkirtLength = "auto") {
22356
- this._meshCache = {};
22357
- this.painter = painter;
22358
- this.tileManager = new TerrainTileManager(tileManager);
22359
- this.options = options;
22360
- this.exaggeration = typeof options.exaggeration === "number" ? options.exaggeration : 1;
22361
- this._terrainSkirtLength = terrainSkirtLength;
22362
- this.qualityFactor = 2;
22363
- this.meshSize = 128;
22364
- this._demMatrixCache = /* @__PURE__ */ new Map();
22365
- this._elevationSamplerCache = /* @__PURE__ */ new Map();
22366
- this.coordsIndex = [];
22367
- this._coordsTextureSize = 1024;
22368
- }
22369
- destroy() {
22370
- if (this._fbo) {
22371
- this._fbo.destroy();
22372
- this._fbo = null;
22373
- }
22374
- if (this._fboCoordsTexture) {
22375
- this._fboCoordsTexture.destroy();
22376
- this._fboCoordsTexture = null;
22377
- }
22378
- if (this._fboDepthTexture) {
22379
- this._fboDepthTexture.destroy();
22380
- this._fboDepthTexture = null;
22381
- }
22382
- if (this._emptyDemTexture) {
22383
- this._emptyDemTexture.destroy();
22384
- this._emptyDemTexture = null;
22385
- }
22386
- if (this._emptyDepthTexture) {
22387
- this._emptyDepthTexture.destroy();
22388
- this._emptyDepthTexture = null;
22389
- }
22390
- if (this._coordsTexture) {
22391
- this._coordsTexture.destroy();
22392
- this._coordsTexture = null;
22393
- }
22394
- for (const key in this._meshCache) this._meshCache[key].destroy();
22395
- this._meshCache = {};
22396
- this.tileManager.destruct();
22397
- }
22398
- /**
22399
- * Get the elevation-value from original dem-data for a given tile-coordinate.
22400
- * Coordinates that fall outside `[0, extent)` are normalized to the
22401
- * appropriate neighbor tile before lookup.
22402
- * @param tileID - the tile to get the elevation for
22403
- * @param x - x coordinate relative to the tile, may be outside `[0, extent)`
22404
- * @param y - y coordinate relative to the tile, may be outside `[0, extent)`
22405
- * @param extent - optional, default 8192
22406
- * @returns the elevation
22407
- */
22408
- getDEMElevation(tileID, x, y, extent = EXTENT) {
22409
- const normalized = tileID.normalizeCoordinates(x, y, extent);
22410
- if (!normalized) return 0;
22411
- const sampler = this._getElevationSampler(normalized.tileID);
22412
- return sampler ? sampler(normalized.x, normalized.y, extent) : 0;
22413
- }
22414
- /**
22415
- * Get the elevation for given {@link LngLat} in respect of exaggeration.
22416
- * @param lnglat - the location
22417
- * @param zoom - the zoom, use {@link getElevationForLngLat} if you don't want a specific zoom level, but more accurate results.
22418
- * @returns the elevation
22419
- */
22420
- getElevationForLngLatZoom(lnglat, zoom) {
22421
- if (!isInBoundsForZoomLngLat(zoom, lnglat.wrap())) return 0;
22422
- const { tileID, mercatorX, mercatorY } = this._getOverscaledTileIDFromLngLatZoom(lnglat, zoom);
22423
- return this.getElevation(tileID, mercatorX % EXTENT, mercatorY % EXTENT, EXTENT);
22424
- }
22425
- /**
22426
- * Get the elevation for given {@link LngLat} in respect of exaggeration.
22427
- * This will traverse up the zoom levels to find the first tile with data to return.
22428
- * @param lnglat - the location
22429
- * @returns the elevation
22430
- */
22431
- getElevationForLngLat(lnglat, transform) {
22432
- const terrainCoveringTiles = coveringTiles(transform, {
22433
- maxzoom: this.tileManager.maxzoom,
22434
- minzoom: this.tileManager.minzoom,
22435
- tileSize: 512,
22436
- terrain: this
22437
- });
22438
- let zoom = 0;
22439
- for (const tile of terrainCoveringTiles) if (tile.canonical.z > zoom) zoom = Math.min(tile.canonical.z, this.tileManager.maxzoom);
22440
- return this.getElevationForLngLatZoom(lnglat, zoom);
22441
- }
22442
- /**
22443
- * Get the elevation for given coordinate in respect of exaggeration.
22444
- * @param tileID - the tile id
22445
- * @param x - x coordinate relative to the tile, may be outside `[0, extent)`
22446
- * @param y - y coordinate relative to the tile, may be outside `[0, extent)`
22447
- * @param extent - optional, default 8192
22448
- * @returns the elevation
22449
- */
22450
- getElevation(tileID, x, y, extent = EXTENT) {
22451
- return this.getDEMElevation(tileID, x, y, extent) * this.exaggeration;
22452
- }
22453
- /**
22454
- * Clear CPU elevation samplers that may retain a previously selected DEM tile.
22455
- * @internal
22456
- */
22457
- resetElevationCache() {
22458
- this._elevationSamplerCache.clear();
22459
- }
22460
- _getElevationSampler(tileID) {
22461
- const key = tileID.key;
22462
- const cachedSampler = this._elevationSamplerCache.get(key);
22463
- if (cachedSampler) return cachedSampler;
22464
- const sourceTile = this.tileManager.getSourceTile(tileID, true);
22465
- const dem = sourceTile?.dem;
22466
- if (!sourceTile || !dem) return null;
22467
- const matrix = this._getDEMTileMatrix(tileID, sourceTile);
22468
- const demPixelScaleX = matrix[0] * dem.dim;
22469
- const demPixelScaleY = matrix[5] * dem.dim;
22470
- const demPixelOffsetX = matrix[12] * dem.dim;
22471
- const demPixelOffsetY = matrix[13] * dem.dim;
22472
- const sampler = (x, y, extent) => {
22473
- const extentScale = extent === 8192 ? 1 : EXTENT / extent;
22474
- return dem.sampleBilinear(x * extentScale * demPixelScaleX + demPixelOffsetX, y * extentScale * demPixelScaleY + demPixelOffsetY);
22475
- };
22476
- this._elevationSamplerCache.set(key, sampler);
22477
- return sampler;
22478
- }
22479
- _getDEMTileMatrix(tileID, sourceTile) {
22480
- const matrixKey = `${sourceTile.tileID.key}/${tileID.key}`;
22481
- const cachedMatrix = this._demMatrixCache.get(matrixKey);
22482
- if (cachedMatrix) return cachedMatrix;
22483
- const maxzoom = this.tileManager.getSource().maxzoom;
22484
- let dz = tileID.canonical.z - sourceTile.tileID.canonical.z;
22485
- if (tileID.overscaledZ > tileID.canonical.z) {
22486
- if (tileID.canonical.z >= maxzoom) dz = tileID.canonical.z - maxzoom;
22487
- else warnOnce("cannot calculate elevation if elevation maxzoom > source.maxzoom");
22488
- }
22489
- const dx = tileID.canonical.x - (tileID.canonical.x >> dz << dz);
22490
- const dy = tileID.canonical.y - (tileID.canonical.y >> dz << dz);
22491
- const demMatrix = fromScaling(/* @__PURE__ */ new Float64Array(16), [
22492
- 1 / (EXTENT << dz),
22493
- 1 / (EXTENT << dz),
22494
- 0
22495
- ]);
22496
- translate(demMatrix, demMatrix, [
22497
- dx * EXTENT,
22498
- dy * EXTENT,
22499
- 0
22500
- ]);
22501
- this._demMatrixCache.set(matrixKey, demMatrix);
22502
- return demMatrix;
22503
- }
22504
- /**
22505
- * returns a Terrain Object for a tile. Unless the tile corresponds to data (e.g. tile is loading), return a flat dem object
22506
- * @param tileID - the tile to get the terrain for
22507
- * @returns the terrain data to use in the program
22508
- */
22509
- getTerrainData(tileID) {
22510
- if (!this._emptyDemTexture) {
22511
- const context = this.painter.context;
22512
- const image = new RGBAImage({
22513
- width: 1,
22514
- height: 1
22515
- }, /* @__PURE__ */ new Uint8Array(4));
22516
- this._emptyDepthTexture = new Texture(context, image, context.gl.RGBA, { premultiply: false });
22517
- this._emptyDemUnpack = [
22518
- 0,
22519
- 0,
22520
- 0,
22521
- 0
22522
- ];
22523
- this._emptyDemTexture = new Texture(context, new RGBAImage({
22524
- width: 1,
22525
- height: 1
22526
- }), context.gl.RGBA, { premultiply: false });
22527
- this._emptyDemTexture.bind(context.gl.NEAREST, context.gl.CLAMP_TO_EDGE);
22528
- this._emptyDemMatrix = identity([]);
22529
- }
22530
- const sourceTile = this.tileManager.getSourceTile(tileID, true);
22531
- if (sourceTile?.dem && (!sourceTile.demTexture || sourceTile.needsTerrainPrepare)) {
22532
- const context = this.painter.context;
22533
- sourceTile.demTexture ||= this.painter.getTileTexture(sourceTile.dem.stride);
22534
- if (sourceTile.demTexture) sourceTile.demTexture.update(sourceTile.dem.getPixels(), { premultiply: false });
22535
- else sourceTile.demTexture = new Texture(context, sourceTile.dem.getPixels(), context.gl.RGBA, { premultiply: false });
22536
- sourceTile.demTexture.bind(context.gl.NEAREST, context.gl.CLAMP_TO_EDGE);
22537
- sourceTile.needsTerrainPrepare = false;
22538
- }
22539
- const terrainMatrix = sourceTile ? this._getDEMTileMatrix(tileID, sourceTile) : this._emptyDemMatrix;
22540
- return {
22541
- "u_depth": 2,
22542
- "u_terrain": 3,
22543
- "u_terrain_dim": sourceTile?.dem?.dim || 1,
22544
- "u_terrain_matrix": terrainMatrix,
22545
- "u_terrain_unpack": sourceTile?.dem?.getUnpackVector() || this._emptyDemUnpack,
22546
- "u_terrain_exaggeration": this.exaggeration,
22547
- texture: (sourceTile?.demTexture || this._emptyDemTexture).texture,
22548
- depthTexture: (this._fboDepthTexture || this._emptyDepthTexture).texture,
22549
- tile: sourceTile
22550
- };
22551
- }
22552
- /**
22553
- * get a framebuffer as big as the map-div, which will be used to render depth & coords into a texture
22554
- * @param texture - the texture
22555
- * @returns the frame buffer
22556
- */
22557
- getFramebuffer(texture) {
22558
- const painter = this.painter;
22559
- const width = painter.width / devicePixelRatio;
22560
- const height = painter.height / devicePixelRatio;
22561
- if (this._fbo && (this._fbo.width !== width || this._fbo.height !== height)) {
22562
- this._fbo.destroy();
22563
- this._fboCoordsTexture.destroy();
22564
- this._fboDepthTexture.destroy();
22565
- delete this._fbo;
22566
- delete this._fboDepthTexture;
22567
- delete this._fboCoordsTexture;
22568
- }
22569
- if (!this._fboCoordsTexture) {
22570
- this._fboCoordsTexture = new Texture(painter.context, {
22571
- width,
22572
- height,
22573
- data: null
22574
- }, painter.context.gl.RGBA, { premultiply: false });
22575
- this._fboCoordsTexture.bind(painter.context.gl.NEAREST, painter.context.gl.CLAMP_TO_EDGE);
22742
+ this._map.off("terrain", this._updateData);
22743
+ this._map.off("resize", this._updateCompact);
22744
+ this._map.off("drag", this._updateCompactMinimize);
22745
+ this._map = void 0;
22746
+ this._compact = void 0;
22747
+ this._attribHTML = void 0;
22748
+ }
22749
+ _setElementTitle(element, title) {
22750
+ const str = this._map._getUIString(`AttributionControl.${title}`);
22751
+ element.title = str;
22752
+ element.setAttribute("aria-label", str);
22753
+ }
22754
+ _updateAttributions() {
22755
+ if (!this._map.style) return;
22756
+ let attributions = [];
22757
+ if (this.options.customAttribution) {
22758
+ if (Array.isArray(this.options.customAttribution)) attributions = attributions.concat(this.options.customAttribution.map((attribution) => {
22759
+ if (typeof attribution !== "string") return "";
22760
+ return attribution;
22761
+ }));
22762
+ else if (typeof this.options.customAttribution === "string") attributions.push(this.options.customAttribution);
22576
22763
  }
22577
- if (!this._fboDepthTexture) {
22578
- this._fboDepthTexture = new Texture(painter.context, {
22579
- width,
22580
- height,
22581
- data: null
22582
- }, painter.context.gl.RGBA, { premultiply: false });
22583
- this._fboDepthTexture.bind(painter.context.gl.NEAREST, painter.context.gl.CLAMP_TO_EDGE);
22764
+ if (this._map.style.stylesheet) {
22765
+ const stylesheet = this._map.style.stylesheet;
22766
+ this.styleOwner = stylesheet.owner;
22767
+ this.styleId = stylesheet.id;
22584
22768
  }
22585
- if (!this._fbo) {
22586
- this._fbo = painter.context.createFramebuffer(width, height, true, false);
22587
- this._fbo.depthAttachment.set(painter.context.createRenderbuffer(painter.context.gl.DEPTH_COMPONENT16, width, height));
22769
+ const tileManagers = this._map.style.tileManagers;
22770
+ for (const id in tileManagers) {
22771
+ const tileManager = tileManagers[id];
22772
+ if (tileManager.used || tileManager.usedForTerrain) {
22773
+ const source = tileManager.getSource();
22774
+ if (source.attribution && !attributions.includes(source.attribution)) attributions.push(source.attribution);
22775
+ }
22588
22776
  }
22589
- this._fbo.colorAttachment.set(texture === "coords" ? this._fboCoordsTexture.texture : this._fboDepthTexture.texture);
22590
- return this._fbo;
22591
- }
22592
- /**
22593
- * create coords texture, needed to grab coordinates from canvas
22594
- * encode coords coordinate into 4 bytes:
22595
- * - 8 lower bits for x
22596
- * - 8 lower bits for y
22597
- * - 4 higher bits for x
22598
- * - 4 higher bits for y
22599
- * - 8 bits for coordsIndex (1 .. 255) (= number of terraintile), is later set in draw_terrain uniform value
22600
- * @returns the texture
22601
- */
22602
- getCoordsTexture() {
22603
- const context = this.painter.context;
22604
- if (this._coordsTexture) return this._coordsTexture;
22605
- const data = new Uint8Array(this._coordsTextureSize * this._coordsTextureSize * 4);
22606
- for (let y = 0, i = 0; y < this._coordsTextureSize; y++) for (let x = 0; x < this._coordsTextureSize; x++, i += 4) {
22607
- data[i + 0] = x & 255;
22608
- data[i + 1] = y & 255;
22609
- data[i + 2] = x >> 8 << 4 | y >> 8;
22610
- data[i + 3] = 0;
22611
- }
22612
- const image = new RGBAImage({
22613
- width: this._coordsTextureSize,
22614
- height: this._coordsTextureSize
22615
- }, new Uint8Array(data.buffer));
22616
- const texture = new Texture(context, image, context.gl.RGBA, { premultiply: false });
22617
- texture.bind(context.gl.NEAREST, context.gl.CLAMP_TO_EDGE);
22618
- this._coordsTexture = texture;
22619
- return texture;
22620
- }
22621
- /**
22622
- * Reads a pixel from the coords-framebuffer and translate this to mercator, or null, if the pixel doesn't lie on the terrain's surface (but the sky instead).
22623
- * @param p - Screen-Coordinate
22624
- * @returns Mercator coordinate for a screen pixel, or null, if the pixel is not covered by terrain (is in the sky).
22625
- */
22626
- pointCoordinate(p) {
22627
- this.painter.maybeDrawDepth(true);
22628
- this.painter.maybeDrawCoords();
22629
- const rgba = /* @__PURE__ */ new Uint8Array(4);
22630
- const context = this.painter.context, gl = context.gl;
22631
- const px = Math.round(p.x * this.painter.pixelRatio / devicePixelRatio);
22632
- const py = Math.round(p.y * this.painter.pixelRatio / devicePixelRatio);
22633
- const fbHeight = Math.round(this.painter.height / devicePixelRatio);
22634
- context.bindFramebuffer.set(this.getFramebuffer("coords").framebuffer);
22635
- gl.readPixels(px, fbHeight - py - 1, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, rgba);
22636
- context.bindFramebuffer.set(null);
22637
- const x = rgba[0] + (rgba[2] >> 4 << 8);
22638
- const y = rgba[1] + ((rgba[2] & 15) << 8);
22639
- const tileID = this.coordsIndex[255 - rgba[3]];
22640
- const tile = tileID && this.tileManager.getTileByID(tileID);
22641
- if (!tile) return null;
22642
- const coordsSize = this._coordsTextureSize;
22643
- const worldSize = (1 << tile.tileID.canonical.z) * coordsSize;
22644
- return new MercatorCoordinate((tile.tileID.canonical.x * coordsSize + x) / worldSize + tile.tileID.wrap, (tile.tileID.canonical.y * coordsSize + y) / worldSize, this.getElevation(tile.tileID, x, y, coordsSize));
22777
+ attributions = attributions.filter((e) => String(e).trim());
22778
+ attributions.sort((a, b) => a.length - b.length);
22779
+ attributions = attributions.filter((attrib, i) => {
22780
+ for (let j = i + 1; j < attributions.length; j++) if (attributions[j].includes(attrib)) return false;
22781
+ return true;
22782
+ });
22783
+ const attribHTML = attributions.join(" | ");
22784
+ if (attribHTML === this._attribHTML) return;
22785
+ this._attribHTML = attribHTML;
22786
+ if (attributions.length) {
22787
+ this._innerContainer.innerHTML = DOM.sanitize(attribHTML);
22788
+ this._container.classList.remove("maplibregl-attrib-empty");
22789
+ } else this._container.classList.add("maplibregl-attrib-empty");
22790
+ this._updateCompact();
22791
+ this._editLink = null;
22645
22792
  }
22793
+ };
22794
+ //#endregion
22795
+ //#region src/ui/control/logo_control.ts
22796
+ /**
22797
+ * A `LogoControl` is a control that adds the watermark.
22798
+ *
22799
+ * @group Markers and Controls
22800
+ *
22801
+ * @example
22802
+ * ```ts
22803
+ * map.addControl(new LogoControl({compact: false}));
22804
+ * ```
22805
+ **/
22806
+ var LogoControl = class {
22646
22807
  /**
22647
- * Reads the depth value from the depth-framebuffer at a given screen pixel
22648
- * @param p - Screen coordinate
22649
- * @returns depth value in clip space (between 0 and 1)
22808
+ * @param options - the control's options
22650
22809
  */
22651
- depthAtPoint(p) {
22652
- const rgba = /* @__PURE__ */ new Uint8Array(4);
22653
- const context = this.painter.context, gl = context.gl;
22654
- context.bindFramebuffer.set(this.getFramebuffer("depth").framebuffer);
22655
- gl.readPixels(p.x, this.painter.height / devicePixelRatio - p.y - 1, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, rgba);
22656
- context.bindFramebuffer.set(null);
22657
- return (rgba[0] / 16777216 + rgba[1] / 65536 + rgba[2] / 256 + rgba[3]) / 256;
22810
+ constructor(options = {}) {
22811
+ this._updateCompact = () => {
22812
+ const containerChildren = this._container.children;
22813
+ if (containerChildren.length) {
22814
+ const anchor = containerChildren[0];
22815
+ if (this._map.getCanvasContainer().offsetWidth <= 640 || this._compact) {
22816
+ if (this._compact !== false) anchor.classList.add("maplibregl-compact");
22817
+ } else anchor.classList.remove("maplibregl-compact");
22818
+ }
22819
+ };
22820
+ this.options = options;
22658
22821
  }
22659
- /**
22660
- * create a regular mesh which will be used by all terrain-tiles
22661
- * @returns the created regular mesh
22662
- */
22663
- getTerrainMesh(tileId) {
22664
- const globeEnabled = this.painter.style.projection?.transitionState > 0;
22665
- const northPole = globeEnabled && tileId.canonical.y === 0;
22666
- const southPole = globeEnabled && tileId.canonical.y === (1 << tileId.canonical.z) - 1;
22667
- const key = `m_${northPole ? "n" : ""}_${southPole ? "s" : ""}`;
22668
- if (this._meshCache[key]) return this._meshCache[key];
22669
- const context = this.painter.context;
22670
- const vertexArray = new Pos3dArray();
22671
- const indexArray = new TriangleIndexArray();
22672
- const meshSize = this.meshSize;
22673
- const delta = EXTENT / meshSize;
22674
- const meshSize2 = meshSize * meshSize;
22675
- for (let y = 0; y <= meshSize; y++) for (let x = 0; x <= meshSize; x++) vertexArray.emplaceBack(x * delta, y * delta, 0);
22676
- for (let y = 0; y < meshSize2; y += meshSize + 1) for (let x = 0; x < meshSize; x++) {
22677
- indexArray.emplaceBack(x + y, meshSize + x + y + 1, meshSize + x + y + 2);
22678
- indexArray.emplaceBack(x + y, meshSize + x + y + 2, x + y + 1);
22679
- }
22680
- if (this._terrainSkirtLength !== "none") this._buildSkirts(vertexArray, indexArray, meshSize, delta, northPole, southPole);
22681
- const mesh = new Mesh(context.createVertexBuffer(vertexArray, pos3dAttributes.members), context.createIndexBuffer(indexArray), SegmentVector.simpleSegment(0, 0, vertexArray.length, indexArray.length));
22682
- this._meshCache[key] = mesh;
22683
- return mesh;
22822
+ getDefaultPosition() {
22823
+ return "bottom-left";
22684
22824
  }
22685
- /**
22686
- * Calculates the height of the tile skirts for the "auto" strategy.
22687
- * @see {@link MapOptions.terrainSkirtLength}
22688
- * @param zoom - current zoomlevel
22689
- * @returns the elevation delta in meters
22690
- */
22691
- getSkirtLength(zoom) {
22692
- return 2 * Math.PI * earthRadius / Math.pow(2, Math.max(zoom, 0)) / 5;
22825
+ /** {@inheritDoc IControl.onAdd} */
22826
+ onAdd(map) {
22827
+ this._map = map;
22828
+ this._compact = this.options?.compact;
22829
+ this._container = DOM.create("div", "maplibregl-ctrl");
22830
+ const anchor = DOM.create("a", "maplibregl-ctrl-logo");
22831
+ anchor.target = "_blank";
22832
+ anchor.rel = "noopener nofollow";
22833
+ anchor.href = "https://maplibre.org/";
22834
+ anchor.setAttribute("aria-label", this._map._getUIString("LogoControl.Title"));
22835
+ anchor.setAttribute("rel", "noopener nofollow");
22836
+ this._container.appendChild(anchor);
22837
+ this._container.style.display = "block";
22838
+ this._map.on("resize", this._updateCompact);
22839
+ this._updateCompact();
22840
+ return this._container;
22693
22841
  }
22694
- getMinTileElevationForLngLatZoom(lnglat, zoom) {
22695
- if (!isInBoundsForZoomLngLat(zoom, lnglat.wrap())) return 0;
22696
- const { tileID } = this._getOverscaledTileIDFromLngLatZoom(lnglat, zoom);
22697
- return this.getMinMaxElevation(tileID).minElevation ?? 0;
22842
+ /** {@inheritDoc IControl.onRemove} */
22843
+ onRemove() {
22844
+ this._container.remove();
22845
+ this._map.off("resize", this._updateCompact);
22846
+ this._map = void 0;
22847
+ this._compact = void 0;
22698
22848
  }
22699
- /**
22700
- * Get the minimum and maximum elevation contained in a tile. This includes any
22701
- * exaggeration included in the terrain.
22702
- *
22703
- * @param tileID - ID of the tile to be used as a source for the min/max elevation
22704
- * @returns the minimum and maximum elevation found in the tile, including the terrain's
22705
- * exaggeration
22706
- */
22707
- getMinMaxElevation(tileID) {
22708
- const tile = this.tileManager.getSourceTile(tileID, true);
22709
- const minMax = {
22710
- minElevation: null,
22711
- maxElevation: null
22712
- };
22713
- if (tile?.dem) {
22714
- minMax.minElevation = tile.dem.min * this.exaggeration;
22715
- minMax.maxElevation = tile.dem.max * this.exaggeration;
22716
- }
22717
- return minMax;
22849
+ };
22850
+ //#endregion
22851
+ //#region src/util/task_queue.ts
22852
+ var TaskQueue = class {
22853
+ constructor() {
22854
+ this._queue = [];
22855
+ this._id = 0;
22856
+ this._cleared = false;
22857
+ this._currentlyRunning = false;
22718
22858
  }
22719
- _getOverscaledTileIDFromLngLatZoom(lnglat, zoom) {
22720
- const mercatorCoordinate = MercatorCoordinate.fromLngLat(lnglat.wrap());
22721
- const worldSize = (1 << zoom) * EXTENT;
22722
- const mercatorX = mercatorCoordinate.x * worldSize;
22723
- const mercatorY = mercatorCoordinate.y * worldSize;
22724
- const tileX = Math.floor(mercatorX / EXTENT), tileY = Math.floor(mercatorY / EXTENT);
22725
- return {
22726
- tileID: new OverscaledTileID(zoom, 0, zoom, tileX, tileY),
22727
- mercatorX,
22728
- mercatorY
22729
- };
22859
+ add(callback) {
22860
+ const id = ++this._id;
22861
+ this._queue.push({
22862
+ callback,
22863
+ id,
22864
+ cancelled: false
22865
+ });
22866
+ return id;
22730
22867
  }
22731
- /** Add an extra frame around the mesh to avoid hairline gaps (stitching) on tile boundaries with different zoomlevels.
22732
- * @see {@link MapOptions.terrainSkirtLength}
22733
- */
22734
- _buildSkirts(vertexArray, indexArray, meshSize, delta, northPole, southPole) {
22735
- const offsetTop = vertexArray.length;
22736
- const offsetTopEdge = 0;
22737
- const offsetBottom = offsetTop + (meshSize + 1);
22738
- const offsetBottomEdge = (meshSize + 1) * meshSize;
22739
- const northY = northPole ? NORTH_POLE_Y : 0;
22740
- const northZ = northPole ? 0 : 1;
22741
- const southY = southPole ? SOUTH_POLE_Y : EXTENT;
22742
- const southZ = southPole ? 0 : 1;
22743
- for (let x = 0; x <= meshSize; x++) vertexArray.emplaceBack(x * delta, northY, northZ);
22744
- for (let x = 0; x <= meshSize; x++) vertexArray.emplaceBack(x * delta, southY, southZ);
22745
- for (let x = 0; x < meshSize; x++) {
22746
- indexArray.emplaceBack(offsetBottomEdge + x, offsetBottom + x, offsetBottom + x + 1);
22747
- indexArray.emplaceBack(offsetBottomEdge + x, offsetBottom + x + 1, offsetBottomEdge + x + 1);
22748
- indexArray.emplaceBack(offsetTopEdge + x, offsetTop + x + 1, offsetTop + x);
22749
- indexArray.emplaceBack(offsetTopEdge + x, offsetTopEdge + x + 1, offsetTop + x + 1);
22868
+ remove(id) {
22869
+ const running = this._currentlyRunning;
22870
+ const queue = running ? this._queue.concat(running) : this._queue;
22871
+ for (const task of queue) if (task.id === id) {
22872
+ task.cancelled = true;
22873
+ return;
22750
22874
  }
22751
- const offsetLeft = vertexArray.length;
22752
- const offsetRight = offsetLeft + (meshSize + 1) * 2;
22753
- for (const x of [0, 1]) for (let y = 0; y <= meshSize; y++) for (const z of [0, 1]) vertexArray.emplaceBack(x * EXTENT, y * delta, z);
22754
- for (let y = 0; y < meshSize * 2; y += 2) {
22755
- indexArray.emplaceBack(offsetLeft + y, offsetLeft + y + 1, offsetLeft + y + 3);
22756
- indexArray.emplaceBack(offsetLeft + y, offsetLeft + y + 3, offsetLeft + y + 2);
22757
- indexArray.emplaceBack(offsetRight + y, offsetRight + y + 3, offsetRight + y + 1);
22758
- indexArray.emplaceBack(offsetRight + y, offsetRight + y + 2, offsetRight + y + 3);
22875
+ }
22876
+ run(timeStamp = 0) {
22877
+ if (this._currentlyRunning) throw new Error("Attempting to run(), but is already running.");
22878
+ const queue = this._currentlyRunning = this._queue;
22879
+ this._queue = [];
22880
+ for (const task of queue) {
22881
+ if (task.cancelled) continue;
22882
+ task.callback(timeStamp);
22883
+ if (this._cleared) break;
22759
22884
  }
22885
+ this._cleared = false;
22886
+ this._currentlyRunning = false;
22887
+ }
22888
+ clear() {
22889
+ if (this._currentlyRunning) this._cleared = true;
22890
+ this._queue = [];
22760
22891
  }
22761
22892
  };
22762
22893
  //#endregion