maplibre-gl 6.11.0 → 6.11.2

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 (79) hide show
  1. package/README.md +1 -1
  2. package/dist/maplibre-gl-dev.mjs +413 -214
  3. package/dist/maplibre-gl-dev.mjs.map +1 -1
  4. package/dist/maplibre-gl-shared-dev.mjs +6 -8
  5. package/dist/maplibre-gl-shared-dev.mjs.map +1 -1
  6. package/dist/maplibre-gl-shared.mjs +2 -2
  7. package/dist/maplibre-gl-shared.mjs.map +1 -1
  8. package/dist/maplibre-gl-worker-dev.mjs +23 -20
  9. package/dist/maplibre-gl-worker-dev.mjs.map +1 -1
  10. package/dist/maplibre-gl-worker.mjs +6 -2
  11. package/dist/maplibre-gl-worker.mjs.map +1 -1
  12. package/dist/maplibre-gl.d.ts +132 -37
  13. package/dist/maplibre-gl.mjs +15 -15
  14. package/dist/maplibre-gl.mjs.map +1 -1
  15. package/package.json +2 -2
  16. package/src/data/bucket/symbol_bucket.test.ts +7 -6
  17. package/src/data/bucket/symbol_bucket.ts +2 -2
  18. package/src/data/bucket.ts +6 -4
  19. package/src/geo/projection/camera_helper.ts +6 -0
  20. package/src/geo/projection/globe_transform.test.ts +10 -1
  21. package/src/geo/projection/globe_utils.ts +5 -2
  22. package/src/geo/projection/mercator_camera_helper.ts +31 -7
  23. package/src/geo/projection/mercator_transform.test.ts +12 -0
  24. package/src/geo/projection/mercator_transform.ts +1 -1
  25. package/src/geo/projection/projection_data.ts +4 -1
  26. package/src/geo/projection/vertical_perspective_camera_helper.test.ts +24 -1
  27. package/src/geo/projection/vertical_perspective_camera_helper.ts +3 -0
  28. package/src/geo/transform_helper.ts +2 -2
  29. package/src/index.ts +2 -1
  30. package/src/render/glyph_atlas.test.ts +45 -0
  31. package/src/render/glyph_atlas.ts +28 -25
  32. package/src/render/glyph_manager.test.ts +92 -68
  33. package/src/render/glyph_manager.ts +41 -25
  34. package/src/render/painter.ts +7 -3
  35. package/src/render/render_context.ts +5 -1
  36. package/src/shaders/glsl/hillshade.fragment.glsl +1 -1
  37. package/src/shaders/glsl/hillshade.fragment.glsl.g.ts +1 -1
  38. package/src/shaders/glsl/hillshade_prepare.fragment.glsl +3 -2
  39. package/src/shaders/glsl/hillshade_prepare.fragment.glsl.g.ts +1 -1
  40. package/src/source/geojson_source.test.ts +32 -13
  41. package/src/source/geojson_source.ts +8 -3
  42. package/src/source/geojson_source_diff.test.ts +10 -0
  43. package/src/source/geojson_source_diff.ts +7 -44
  44. package/src/source/vector_tile_source.test.ts +25 -0
  45. package/src/source/vector_tile_source.ts +3 -0
  46. package/src/source/worker_source.ts +2 -2
  47. package/src/source/worker_tile.test.ts +3 -3
  48. package/src/source/worker_tile.ts +4 -3
  49. package/src/style/style.ts +2 -2
  50. package/src/style/style_glyph.ts +6 -0
  51. package/src/symbol/shaping.test.ts +8 -8
  52. package/src/symbol/shaping.ts +12 -12
  53. package/src/symbol/symbol_layout.ts +4 -12
  54. package/src/symbol/tagged_string.test.ts +8 -6
  55. package/src/symbol/tagged_string.ts +5 -5
  56. package/src/ui/camera.test.ts +22 -2
  57. package/src/ui/camera.ts +78 -15
  58. package/src/ui/handler/click_zoom.ts +1 -1
  59. package/src/ui/handler/dblclick_zoom.test.ts +35 -0
  60. package/src/ui/handler/drag_pan.test.ts +18 -0
  61. package/src/ui/handler/scroll_zoom.test.ts +26 -0
  62. package/src/ui/handler/tap_zoom.ts +2 -2
  63. package/src/ui/handler_manager.test.ts +33 -14
  64. package/src/ui/handler_manager.ts +53 -27
  65. package/src/ui/map.ts +45 -65
  66. package/src/ui/map_tests/map_bounds.test.ts +17 -1
  67. package/src/ui/map_tests/map_events.test.ts +20 -1
  68. package/src/ui/map_tests/map_pixel_ratio.test.ts +31 -0
  69. package/src/ui/map_tests/map_terrain.test.ts +204 -0
  70. package/src/util/actor_messages.ts +4 -9
  71. package/src/util/dom.test.ts +136 -2
  72. package/src/util/dom.ts +97 -35
  73. package/src/util/test/util.ts +3 -1
  74. package/src/webgl/context.ts +7 -3
  75. package/src/webgl/program.ts +2 -5
  76. package/src/webgl/projection_uniform_buffer.test.ts +76 -0
  77. package/src/webgl/projection_uniform_buffer.ts +34 -3
  78. package/src/webgl/render_to_texture.test.ts +1 -1
  79. package/src/webgl/uniform_buffer.ts +8 -8
@@ -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.11.0/LICENSE.txt
3
+ * @license 3-Clause BSD. Full text of license: https://github.com/maplibre/maplibre-gl-js/blob/v6.11.2/LICENSE.txt
4
4
  */
5
5
  import { $ as projectToWorldCoordinates, $n as differenceOfAnglesDegrees, $r as add, $t as Transitionable, A as evaluateSizeForZoom, Ai as rotateX, An as addProtocol, Ar as scaleZoom, At as toEvaluationFeature, B as isCluster, Bi as rotate, Bn as arrayBufferToImage, Br as EXTENT, Bt as SegmentVector, C as addDynamicAttributes, Ci as exactEquals, Cn as GLOBAL_DISPATCHER_ID, Cr as pointPlaneSignedDistance, Ct as HEATMAP_FULL_RENDER_FBO_KEY, Di as multiply, Dn as getVideo, Dr as remapSaturate, Dt as RGBAImage, E as TextAnchorEnum, Ei as invert$1, En as getReferrer, Er as readImageUsingVideoFrame, Et as AlphaImage, F as potpack, Fi as create$4, Fn as isAbortError, Fr as uniqueId, Ft as Uniform4f, Gn as createIdentityMat4f32, Gr as zero, Gt as PosArray, Hi as offscreenCanvasSupported, Hn as bezier, Hr as length$1, Ht as CollisionCircleLayoutArray, I as isStyleImageWebGLData, Ii as fromRotation, In as throwIfAborted, Ir as warnOnce, It as UniformColor, J as cameraDirectionFromPitchBearing, Jn as createVec3f64, Jr as multiply$1, Jt as TriangleIndexArray, K as isFillExtrusionStyleLayer, Kn as createIdentityMat4f64, Kr as fromEuler, Kt as QuadTriangleArray, L as renderStyleImage, Li as create$2, Lr as wrap, Lt as UniformColorArray, M as getAnchorAlignment, Mi as rotateZ, Mn as removeProtocol, Mr as subscribe, Mt as Uniform1i, Ni as scale, Nn as config, Nr as threePlaneIntersection, Nt as Uniform2f, O as clipLine, Oi as ortho, On as makeRequest, Or as rollPitchBearingEqual, Ot as isCircleStyleLayer, P as ImagePosition, Pi as translate, Pn as AbortError, Pr as translatePosition, Pt as Uniform3f, Q as maxMercatorHorizonAngle, Qn as degreesToRadians, Qr as transformMat4, Qt as Properties, R as parseGlyphPbf, Ri as determinant, Rn as MAX_VALID_LATITUDE, Rr as zoomScale, Rt as UniformFloatArray, S as SymbolBucket, Si as equals, Sn as AJAXError, Sr as pick, St as isHillshadeStyleLayer, T as getAnchorJustification, Ti as identity, Tn as getJSON, Tr as rayPlaneIntersection, Tt as renderColorRamp, U as collisionCircleLayout, Ui as Point, Un as clamp, Ur as scale$3, Ut as LineStripIndexArray, V as codePointUsesLocalIdeographFontFamily, Vi as isOffscreenCanvasDistorted, Vn as arrayBufferToImageBitmap, Vr as create$3, Vt as CollisionBoxArray, W as isLineStyleLayer, Wn as clone$2, Wr as sqrLen, Wt as Pos3dArray, X as cameraMercatorCoordinateFromCenterAndRotation, Xn as deepEqual, Xr as mul, Xt as isRasterStyleLayer, Y as cameraMercatorCoordinate, Yn as createVec4f64, Yr as slerp, Yt as createLayout, Z as getMercatorHorizon, Zn as defaultEasing, Zr as scale$2, Zt as DataConstantProperty, _ as createStyleLayer, _i as transformQuat, _n as derefLayers, _r as lerp, _t as SubdivisionGranularityExpression, a as GeoJSONFeature, ai as length, an as validateAndEmit, ar as findLineIntersection, at as lngFromMercatorX, b as isBackgroundStyleLayer, bi as copy, bn as Event, br as nextPowerOfTwo, ci as rotateX$1, cn as emptyStyle, cr as getEdgeTiles, ct as mercatorZfromAltitude, d as OverscaledTileID, di as scale$1, dr as isImageBitmap, ei as clone, en as EvaluationParameters, er as distanceOfAnglesRadians, et as tileCoordinatesToMercatorCoordinates, f as UnwrappedTileID, fi as scaleAndAdd, fn as interpolateFactory, fr as isPointableEvent, ft as EXTENT_BOUNDS, g as Actor, gi as transformMat4$1, gn as diff, gr as isWorker, gt as SOUTH_POLE_Y, h as isInBoundsForZoomLngLat, hi as transformMat3, hn as ValidationError, hr as isTouchableOrPointableType, ht as NORTH_POLE_Y, ii as len, in as emitValidationErrors, ir as filterObject, it as latFromMercatorY, ji as rotateY$1, jn as getProtocol, jr as sphericalToCartesian, jt as Uniform1f, k as evaluateSizeForFeature, ki as perspective, kn as sameOrigin, kr as rollPitchBearingToQuat, kt as polygonIntersectsPolygon, li as rotateY, lt as LngLat, m as compareTileId, mi as subtract, mn as Color, mr as isTouchableEvent, mt as isFillStyleLayer, n as TileCache, ni as distance, nr as evaluateZoomSnap, nt as MercatorCoordinate, oi as negate, on as validateStyle, or as getAABB, ot as mercatorXfromLng, p as calculateTileKey, pi as sub, pn as ProjectionDefinition, pr as isSafari, pt as Bounds, q as calculateTileMatrix, qn as createMat4f64, qr as fromValues, qt as RasterBoundsArray, ri as dot, rn as SPEC_SOURCE_TYPES, rr as extend, rt as altitudeFromMercatorZ, s as GEOJSON_TILE_LAYER_NAME, si as normalize, sn as validateStyleAndEmit, sr as getAngleDelta, st as mercatorYfromLat, ti as cross, tn as ZoomHistory, tr as ensureError, tt as unprojectFromWorldCoordinates, u as CanonicalTileID, ui as rotateZ$1, un as featureFilter, ur as getRollPitchBearing, ut as earthRadius, v as isCustomStyleLayer, vi as zero$1, vn as latest, vr as mapObject, vt as SubdivisionGranularitySetting, w as getOverlapMode, wi as fromScaling, wn as getArrayBuffer, wr as radiansToDegrees, wt as isHeatmapStyleLayer, x as isSymbolStyleLayer, xi as create$1, xn as Evented, xr as parseCacheControl, xt as Texture, y as validateCustomStyleLayer, yi as clone$1, yn as ErrorEvent, yr as mod, yt as isColorReliefStyleLayer, zi as invert, zn as angleToRotateBetweenVectors2D, zr as pixelsToTileUnits, zt as UniformMatrix4f } from "./maplibre-gl-shared-dev.mjs";
6
6
  //#region package.json
7
- var version$2 = "6.11.0";
7
+ var version$2 = "6.11.2";
8
8
  //#endregion
9
9
  //#region src/util/browser.ts
10
10
  let linkEl;
@@ -198,6 +198,92 @@ function isTimeFrozen() {
198
198
  }
199
199
  //#endregion
200
200
  //#region src/util/dom.ts
201
+ const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
202
+ const ALLOWED_TAGS = /* @__PURE__ */ new Set([
203
+ "a",
204
+ "abbr",
205
+ "b",
206
+ "bdi",
207
+ "bdo",
208
+ "br",
209
+ "cite",
210
+ "code",
211
+ "del",
212
+ "div",
213
+ "em",
214
+ "i",
215
+ "img",
216
+ "ins",
217
+ "kbd",
218
+ "li",
219
+ "mark",
220
+ "ol",
221
+ "p",
222
+ "q",
223
+ "s",
224
+ "samp",
225
+ "small",
226
+ "span",
227
+ "strong",
228
+ "sub",
229
+ "sup",
230
+ "time",
231
+ "u",
232
+ "ul",
233
+ "var",
234
+ "wbr"
235
+ ]);
236
+ const ALLOWED_ATTRIBUTES = /* @__PURE__ */ new Set([
237
+ "alt",
238
+ "class",
239
+ "datetime",
240
+ "dir",
241
+ "height",
242
+ "href",
243
+ "hreflang",
244
+ "lang",
245
+ "referrerpolicy",
246
+ "rel",
247
+ "role",
248
+ "src",
249
+ "target",
250
+ "title",
251
+ "translate",
252
+ "type",
253
+ "width"
254
+ ]);
255
+ const URL_ATTRIBUTES = /* @__PURE__ */ new Set(["href", "src"]);
256
+ const ALLOWED_PROTOCOLS = /* @__PURE__ */ new Set([
257
+ "http:",
258
+ "https:",
259
+ "mailto:"
260
+ ]);
261
+ /**
262
+ * Relative URLs have to resolve against something before their protocol can be read. Only the protocol of the
263
+ * result is used, so the base never leaks into the sanitized markup.
264
+ */
265
+ const RELATIVE_URL_BASE = "https://maplibre.invalid/";
266
+ let elementInternals;
267
+ /**
268
+ * The `Element` members the sanitizer uses, read off the prototype rather than off the element it is looking at.
269
+ * A form control named `children`, `remove` or `localName` shadows the same-named property of its own form element,
270
+ * which would otherwise let untrusted markup pick what the sanitizer walking that tree sees.
271
+ *
272
+ * They are read on first use rather than when this module is loaded, since the module is also imported where there
273
+ * is no DOM at all, such as during server side rendering.
274
+ */
275
+ function getElementInternals() {
276
+ elementInternals ??= {
277
+ getAttribute: Element.prototype.getAttribute,
278
+ getAttributeNames: Element.prototype.getAttributeNames,
279
+ querySelectorAll: Element.prototype.querySelectorAll,
280
+ remove: Element.prototype.remove,
281
+ removeAttribute: Element.prototype.removeAttribute,
282
+ localName: Object.getOwnPropertyDescriptor(Element.prototype, "localName").get,
283
+ namespaceURI: Object.getOwnPropertyDescriptor(Element.prototype, "namespaceURI").get
284
+ };
285
+ return elementInternals;
286
+ }
201
287
  var DOM = class DOM {
202
288
  static {
203
289
  this.docStyle = typeof window !== "undefined" && window.document?.documentElement.style;
@@ -257,55 +343,54 @@ var DOM = class DOM {
257
343
  return points;
258
344
  }
259
345
  /**
260
- * Sanitize an HTML string - this might not be enough to prevent all XSS attacks
261
- * Base on https://javascriptsource.com/sanitize-an-html-string-to-reduce-the-risk-of-xss-attacks/
262
- * (c) 2021 Chris Ferdinandi, MIT License, https://gomakethings.com
346
+ * Sanitize an untrusted HTML string, such as the attribution a remote TileJSON asks the map to display.
263
347
  *
264
- * Returns the sanitized nodes rather than a string, since re-parsing serialized HTML can produce a different tree.
348
+ * Only the elements in `ALLOWED_TAGS` and the attributes in `ALLOWED_ATTRIBUTES` survive; anything else is
349
+ * dropped along with its subtree. An allow list is used rather than a list of known-dangerous markup because
350
+ * the latter silently permits whatever it has not heard of yet, including markup added to HTML after it was
351
+ * written.
352
+ *
353
+ * The sanitized nodes are returned rather than a string: serializing and re-parsing is not a round trip, so
354
+ * a string result lets carefully nested markup mutate into a different - and no longer sanitized - tree.
265
355
  */
266
356
  static sanitize(str) {
267
- const html = new DOMParser().parseFromString(str, "text/html").body || document.createElement("body");
268
- const dangerousElements = html.querySelectorAll("script, iframe");
269
- for (const element of dangerousElements) element.remove();
270
- DOM.clean(html);
357
+ const body = new DOMParser().parseFromString(str, "text/html").body;
358
+ const { querySelectorAll, remove } = getElementInternals();
359
+ for (const element of Array.from(querySelectorAll.call(body, "*"))) if (DOM.isAllowedElement(element)) DOM.removeDisallowedAttributes(element);
360
+ else remove.call(element);
271
361
  const fragment = document.createDocumentFragment();
272
- fragment.append(...html.childNodes);
362
+ fragment.append(...body.childNodes);
273
363
  return fragment;
274
364
  }
275
365
  /**
276
- * Check if the attribute is potentially dangerous
366
+ * An element is allowed only when it is plain HTML markup on the allow list. Elements in the SVG and MathML
367
+ * namespaces are rejected even when their local name is allowed, since foreign content follows different
368
+ * parsing rules and is what makes most mutation attacks possible in the first place.
277
369
  */
278
- static isPossiblyDangerous(name, value) {
279
- const val = value.replace(/\s+/g, "").toLowerCase();
280
- if ([
281
- "src",
282
- "href",
283
- "xlink:href"
284
- ].includes(name)) {
285
- if (val.includes("javascript:") || val.includes("data:")) return true;
286
- }
287
- if (name === "srcdoc") return true;
288
- if (name.startsWith("on")) return true;
370
+ static isAllowedElement(element) {
371
+ const { namespaceURI, localName } = getElementInternals();
372
+ return namespaceURI.call(element) === HTML_NAMESPACE && ALLOWED_TAGS.has(localName.call(element));
289
373
  }
290
- /**
291
- * Remove dangerous stuff from the HTML document's nodes
292
- * @param html - The HTML document
293
- */
294
- static clean(html) {
295
- const nodes = html.children;
296
- for (const node of nodes) {
297
- DOM.removeAttributes(node);
298
- DOM.clean(node);
374
+ static removeDisallowedAttributes(element) {
375
+ const { getAttributeNames, getAttribute, removeAttribute } = getElementInternals();
376
+ for (const name of getAttributeNames.call(element)) {
377
+ if (DOM.isAllowedAttribute(name, getAttribute.call(element, name))) continue;
378
+ removeAttribute.call(element, name);
299
379
  }
300
380
  }
301
381
  /**
302
- * Remove potentially dangerous attributes from an element
303
- * @param elem - The element
382
+ * `href` and `src` are further restricted to the protocols in `ALLOWED_PROTOCOLS`, so that `javascript:` and
383
+ * `data:` URLs cannot turn an otherwise harmless link or image into a script. The protocol is taken from a
384
+ * parsed URL rather than matched against the raw string, since the URL parser is what the browser will apply
385
+ * and it ignores tabs, newlines and leading control characters that a string comparison would trip over.
304
386
  */
305
- static removeAttributes(elem) {
306
- for (const { name, value } of Array.from(elem.attributes)) {
307
- if (!DOM.isPossiblyDangerous(name, value)) continue;
308
- elem.removeAttribute(name);
387
+ static isAllowedAttribute(name, value) {
388
+ if (!ALLOWED_ATTRIBUTES.has(name) && !name.startsWith("aria-")) return false;
389
+ if (!URL_ATTRIBUTES.has(name)) return true;
390
+ try {
391
+ return ALLOWED_PROTOCOLS.has(new URL(value, RELATIVE_URL_BASE).protocol);
392
+ } catch {
393
+ return false;
309
394
  }
310
395
  }
311
396
  };
@@ -1534,12 +1619,13 @@ var GlyphManager = class {
1534
1619
  }
1535
1620
  async getGlyphs(glyphs) {
1536
1621
  const glyphsPromises = [];
1537
- for (const stack in glyphs) for (const id of glyphs[stack]) glyphsPromises.push(this._getAndCacheGlyphsPromise(stack, id));
1622
+ for (const stack in glyphs) for (const variant in glyphs[stack]) for (const id of glyphs[stack][variant]) glyphsPromises.push(this._getAndCacheGlyphsPromise(stack, id, variant));
1538
1623
  const updatedGlyphs = await Promise.all(glyphsPromises);
1539
1624
  const result = {};
1540
- for (const { stack, id, glyph } of updatedGlyphs) {
1625
+ for (const { stack, id, variant, glyph } of updatedGlyphs) {
1541
1626
  result[stack] ||= {};
1542
- result[stack][id] = glyph && {
1627
+ result[stack][variant] ||= {};
1628
+ result[stack][variant][id] = glyph && {
1543
1629
  id: glyph.id,
1544
1630
  bitmap: glyph.bitmap.clone(),
1545
1631
  metrics: glyph.metrics
@@ -1555,39 +1641,56 @@ var GlyphManager = class {
1555
1641
  * has no way to serve the one shape they are written as. A file the style pinned with
1556
1642
  * `font-faces` draws it where one covers it, and the local fonts otherwise. For a single
1557
1643
  * codepoint a declared file still wins over the glyphs URL and the local fallbacks.
1644
+ * Providers support the `default` variant; unsupported variants return `null`.
1558
1645
  */
1559
- async _getAndCacheGlyphsPromise(stack, id) {
1646
+ async _getAndCacheGlyphsPromise(stack, id, variant) {
1560
1647
  this.entries[stack] ??= {
1561
- glyphs: {},
1648
+ glyphs: { default: {} },
1562
1649
  requests: {},
1563
1650
  ranges: {}
1564
1651
  };
1565
1652
  const entry = this.entries[stack];
1566
- let glyph = entry.glyphs[id];
1653
+ const glyphs = entry.glyphs[variant] ||= {};
1654
+ let glyph = glyphs[id];
1567
1655
  if (glyph !== void 0) return {
1568
1656
  stack,
1569
1657
  id,
1658
+ variant,
1570
1659
  glyph
1571
1660
  };
1661
+ if (variant !== "default") {
1662
+ glyphs[id] = null;
1663
+ return {
1664
+ stack,
1665
+ id,
1666
+ variant,
1667
+ glyph: null
1668
+ };
1669
+ }
1572
1670
  const codePoint = id.codePointAt(0);
1573
1671
  const fontFaceFamily = this.fontFaceManager.hasFontFaces() ? await this.fontFaceManager.getFontFamily(stack, codePoint) : null;
1574
1672
  if (fontFaceFamily) {
1575
- glyph = entry.glyphs[id] = await this._drawGlyph(entry, stack, id, fontFaceFamily);
1673
+ glyph = glyphs[id] = await this._drawGlyph(entry, stack, id, fontFaceFamily);
1576
1674
  return {
1577
1675
  stack,
1578
1676
  id,
1677
+ variant,
1579
1678
  glyph
1580
1679
  };
1581
1680
  }
1582
1681
  if (!this.url || isCluster(id) || this._charUsesLocalIdeographFontFamily(codePoint)) {
1583
- glyph = entry.glyphs[id] = await this._drawGlyph(entry, stack, id);
1682
+ glyph = glyphs[id] = await this._drawGlyph(entry, stack, id);
1584
1683
  return {
1585
1684
  stack,
1586
1685
  id,
1686
+ variant,
1587
1687
  glyph
1588
1688
  };
1589
1689
  }
1590
- return await this._downloadAndCacheRangePromise(stack, id);
1690
+ return {
1691
+ ...await this._downloadAndCacheRangePromise(stack, id),
1692
+ variant
1693
+ };
1591
1694
  }
1592
1695
  /**
1593
1696
  * Gets a glyph from the server-side cache, downloading the PBF range it falls in if need be.
@@ -1607,7 +1710,7 @@ var GlyphManager = class {
1607
1710
  entry.requests[range] ||= this._loadGlyphRange(stack, range);
1608
1711
  try {
1609
1712
  const response = await entry.requests[range];
1610
- for (const responseId in response) entry.glyphs[String.fromCodePoint(+responseId)] = response[+responseId];
1713
+ for (const responseId in response) entry.glyphs.default[String.fromCodePoint(+responseId)] = response[+responseId];
1611
1714
  entry.ranges[range] = true;
1612
1715
  return {
1613
1716
  stack,
@@ -1615,7 +1718,7 @@ var GlyphManager = class {
1615
1718
  glyph: response[codePoint] || null
1616
1719
  };
1617
1720
  } catch (e) {
1618
- const glyph = entry.glyphs[id] = await this._drawGlyph(entry, stack, id);
1721
+ const glyph = entry.glyphs.default[id] = await this._drawGlyph(entry, stack, id);
1619
1722
  this._warnOnMissingGlyphRange(glyph, range, codePoint, ensureError(e));
1620
1723
  return {
1621
1724
  stack,
@@ -1790,7 +1893,7 @@ var GlyphManager = class {
1790
1893
  entry.tinySDF = null;
1791
1894
  entry.ideographTinySDF = null;
1792
1895
  entry.fontFaceTinySDFs = {};
1793
- entry.glyphs = {};
1896
+ entry.glyphs = { default: {} };
1794
1897
  entry.requests = {};
1795
1898
  entry.ranges = {};
1796
1899
  }
@@ -2963,6 +3066,7 @@ var VectorTileSource = class extends Evented {
2963
3066
  };
2964
3067
  params.request.collectResourceTiming = this._collectResourceTiming;
2965
3068
  await this.dispatcher.waitForInitComplete();
3069
+ if (tile.aborted) return;
2966
3070
  let messageType = "RT";
2967
3071
  if (!tile.actor || tile.state === "expired") {
2968
3072
  tile.actor = this.dispatcher.getReadyActor();
@@ -3474,18 +3578,13 @@ function applySourceDiff(updateable, diff, promoteId) {
3474
3578
  /**
3475
3579
  * Merge two GeoJSONSourceDiffs, considering the order of operations as specified above (remove, add, update).
3476
3580
  *
3477
- * For `add` features that use promoteId, the feature id will be set to the promoteId value temporarily so that
3478
- * the merge can be completed, then reverted to the original promoteId state after the merge.
3581
+ * `add` features of a promoteId source carry their id in a property, so `promoteId` is needed to key them.
3479
3582
  */
3480
3583
  function mergeSourceDiffs(prevDiff, nextDiff, promoteId) {
3481
3584
  if (!prevDiff) return nextDiff || {};
3482
3585
  if (!nextDiff) return prevDiff || {};
3483
- if (promoteId) {
3484
- promoteFeatureIds(prevDiff.add, promoteId);
3485
- promoteFeatureIds(nextDiff.add, promoteId);
3486
- }
3487
- const prev = diffToHashed(prevDiff);
3488
- const next = diffToHashed(nextDiff);
3586
+ const prev = diffToHashed(prevDiff, promoteId);
3587
+ const next = diffToHashed(nextDiff, promoteId);
3489
3588
  resolveMergeConflicts(prev, next);
3490
3589
  const merged = {};
3491
3590
  if (prev.removeAll || next.removeAll) merged.removeAll = true;
@@ -3493,9 +3592,7 @@ function mergeSourceDiffs(prevDiff, nextDiff, promoteId) {
3493
3592
  merged.add = new Map([...prev.add, ...next.add]);
3494
3593
  merged.update = new Map([...prev.update, ...next.update]);
3495
3594
  if (merged.remove.size && merged.add.size) for (const id of merged.add.keys()) merged.remove.delete(id);
3496
- const mergedDiff = hashedToDiff(merged);
3497
- if (promoteId) demoteFeatureIds(mergedDiff.add, promoteId);
3498
- return mergedDiff;
3595
+ return hashedToDiff(merged);
3499
3596
  }
3500
3597
  /**
3501
3598
  * Resolve merge conflicts between two GeoJSONSourceDiffs considering the ordering above (remove/add/update).
@@ -3542,32 +3639,16 @@ function mergeFeatureDiffs(prev, next) {
3542
3639
  return merged;
3543
3640
  }
3544
3641
  /**
3545
- * Mutates diff.add and applies a feature id using the promoteId property
3546
- */
3547
- function promoteFeatureIds(add, promoteId) {
3548
- if (!add) return;
3549
- for (const feature of add) {
3550
- const id = getFeatureId(feature, promoteId);
3551
- if (id != null) feature.id = id;
3552
- }
3553
- }
3554
- /**
3555
- * Mutates diff.add and removes the feature id if using the promoteId property
3556
- */
3557
- function demoteFeatureIds(add, promoteId) {
3558
- if (!add) return;
3559
- for (const feature of add) if (getFeatureId(feature, promoteId) != null) delete feature.id;
3560
- }
3561
- /**
3562
3642
  * @internal
3563
3643
  * Convert a GeoJSONSourceDiff to an idempotent hashed representation using Sets and Maps
3644
+ * @param promoteId - If set, `add` features are keyed by that property instead of by `id`.
3564
3645
  */
3565
- function diffToHashed(diff) {
3646
+ function diffToHashed(diff, promoteId) {
3566
3647
  if (!diff) return {};
3567
3648
  const hashed = {};
3568
3649
  hashed.removeAll = diff.removeAll;
3569
3650
  hashed.remove = new Set(diff.remove || []);
3570
- hashed.add = new Map(diff.add?.map((feature) => [feature.id, feature]));
3651
+ hashed.add = new Map(diff.add?.map((feature) => [getFeatureId(feature, promoteId), feature]));
3571
3652
  hashed.update = new Map(diff.update?.map((update) => [update.id, update]));
3572
3653
  return hashed;
3573
3654
  }
@@ -3718,7 +3799,7 @@ var GeoJSONSource = class extends Evented {
3718
3799
  maxZoom: this.maxzoom,
3719
3800
  lineMetrics: options.lineMetrics || false,
3720
3801
  generateId: options.generateId || false,
3721
- promoteId: typeof options.promoteId === "string" ? options.promoteId : void 0,
3802
+ promoteId: this._promoteIdKey,
3722
3803
  cluster: options.cluster || false,
3723
3804
  clusterOptions: {
3724
3805
  maxZoom: this._getClusterMaxZoom(options.clusterMaxZoom),
@@ -3733,6 +3814,10 @@ var GeoJSONSource = class extends Evented {
3733
3814
  filter: options.filter
3734
3815
  }, options.workerOptions);
3735
3816
  }
3817
+ /** The `promoteId` property name, when it is a plain string. */
3818
+ get _promoteIdKey() {
3819
+ return typeof this.promoteId === "string" ? this.promoteId : void 0;
3820
+ }
3736
3821
  _hasPendingWorkerUpdate() {
3737
3822
  return this._pendingWorkerUpdate.data !== void 0 || this._pendingWorkerUpdate.diff !== void 0 || this._pendingWorkerUpdate.updateCluster;
3738
3823
  }
@@ -3779,7 +3864,7 @@ var GeoJSONSource = class extends Evented {
3779
3864
  * @param diff - The changes that need to be applied.
3780
3865
  */
3781
3866
  updateData(diff) {
3782
- this._pendingWorkerUpdate.diff = mergeSourceDiffs(this._pendingWorkerUpdate.diff, diff);
3867
+ this._pendingWorkerUpdate.diff = mergeSourceDiffs(this._pendingWorkerUpdate.diff, diff, this._promoteIdKey);
3783
3868
  return this._updateWorkerData();
3784
3869
  }
3785
3870
  /**
@@ -4011,7 +4096,7 @@ var GeoJSONSource = class extends Evented {
4011
4096
  */
4012
4097
  _applyDiffToSource(diff) {
4013
4098
  if (!diff) return;
4014
- const promoteId = typeof this.promoteId === "string" ? this.promoteId : void 0;
4099
+ const promoteId = this._promoteIdKey;
4015
4100
  if (!this._data.url && !this._data.updateable) {
4016
4101
  const updateable = toUpdateable(this._data.geojson, promoteId);
4017
4102
  if (!updateable) throw new Error(`GeoJSONSource "${this.id}": GeoJSON data is not compatible with updateData`);
@@ -9556,8 +9641,8 @@ const shaders = {
9556
9641
  fillPattern: prepare("#ifdef GL_ES\nprecision highp float;\n#endif\nuniform vec2 u_texsize;uniform float u_fade;uniform bool u_sdf_pattern;uniform sampler2D u_image;in vec2 v_pos_a;in vec2 v_pos_b;\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define highp vec4 color\n#pragma maplibre: define lowp vec4 pattern_from\n#pragma maplibre: define lowp vec4 pattern_to\nvoid main() {\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize highp vec4 color\n#pragma maplibre: initialize mediump vec4 pattern_from\n#pragma maplibre: initialize mediump vec4 pattern_to\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;vec2 imagecoord=mod(v_pos_a,1.0);vec2 pos=mix(pattern_tl_a/u_texsize,pattern_br_a/u_texsize,imagecoord);vec4 color1=texture(u_image,pos);vec2 imagecoord_b=mod(v_pos_b,1.0);vec2 pos2=mix(pattern_tl_b/u_texsize,pattern_br_b/u_texsize,imagecoord_b);vec4 color2=texture(u_image,pos2);if (u_sdf_pattern) {highp float sdf_edge=(256.0-64.0)/256.0;highp float sdf_gamma_a=max(fwidth(color1.a)*0.5,1.0/255.0/16.0);highp float sdf_gamma_b=max(fwidth(color2.a)*0.5,1.0/255.0/16.0);float sdf_alpha_a=smoothstep(sdf_edge-sdf_gamma_a,sdf_edge+sdf_gamma_a,color1.a);float sdf_alpha_b=smoothstep(sdf_edge-sdf_gamma_b,sdf_edge+sdf_gamma_b,color2.a);fragColor=mix(color*sdf_alpha_a,color*sdf_alpha_b,u_fade)*opacity;} else {fragColor=mix(color1,color2,u_fade)*opacity;}\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "uniform vec2 u_pixel_coord_upper;uniform vec2 u_pixel_coord_lower;uniform vec3 u_scale;uniform vec2 u_fill_translate;layout(location=0) in vec2 a_pos;out vec2 v_pos_a;out vec2 v_pos_b;\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define highp vec4 color\n#pragma maplibre: define lowp vec4 pattern_from\n#pragma maplibre: define lowp vec4 pattern_to\n#pragma maplibre: define lowp float pixel_ratio_from\n#pragma maplibre: define lowp float pixel_ratio_to\nvoid main() {\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize highp vec4 color\n#pragma maplibre: initialize mediump vec4 pattern_from\n#pragma maplibre: initialize mediump vec4 pattern_to\n#pragma maplibre: initialize lowp float pixel_ratio_from\n#pragma maplibre: initialize lowp float pixel_ratio_to\nif (opacity < 0.01) {gl_Position=vec4(-2.0,-2.0,-2.0,1.0);return;}vec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;float tileZoomRatio=u_scale.x;float fromScale=u_scale.y;float toScale=u_scale.z;vec2 display_size_a=(pattern_br_a-pattern_tl_a)/pixel_ratio_from;vec2 display_size_b=(pattern_br_b-pattern_tl_b)/pixel_ratio_to;gl_Position=projectTile(a_pos+u_fill_translate,a_pos);v_pos_a=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,fromScale*display_size_a,tileZoomRatio,a_pos);v_pos_b=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,toScale*display_size_b,tileZoomRatio,a_pos);}"),
9557
9642
  fillExtrusion: prepare("in vec4 v_color;void main() {fragColor=v_color;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "uniform vec3 u_lightcolor;uniform lowp vec3 u_lightpos;uniform lowp vec3 u_lightpos_globe;uniform lowp float u_lightintensity;uniform float u_vertical_gradient;uniform lowp float u_opacity;uniform vec2 u_fill_translate;layout(location=0) in vec2 a_pos;layout(location=1) in ivec4 a_normal_ed;\n#ifdef TERRAIN3D\nlayout(location=2) in vec2 a_centroid;\n#endif\nout vec4 v_color;\n#pragma maplibre: define highp float base\n#pragma maplibre: define highp float height\n#pragma maplibre: define highp vec4 color\nvoid main() {\n#pragma maplibre: initialize highp float base\n#pragma maplibre: initialize highp float height\n#pragma maplibre: initialize highp vec4 color\nvec3 normal=vec3(a_normal_ed.xyz);float gradient_base=max(0.0,base);float gradient_height=max(0.0,height);\n#ifdef TERRAIN3D\nfloat height_terrain3d_offset=get_elevation(a_centroid);float base_terrain3d_offset=height_terrain3d_offset-(base > 0.0 ? 0.0 : 10.0);\n#else\nfloat height_terrain3d_offset=0.0;float base_terrain3d_offset=0.0;\n#endif\nbase=max(0.0,base)+base_terrain3d_offset;height=max(0.0,height)+height_terrain3d_offset;float t=float(a_normal_ed.x & 1);float elevation=t > 0.0 ? height : base;vec2 posInTile=a_pos+u_fill_translate;\n#ifdef GLOBE\nvec3 spherePos=projectToSphere(posInTile,a_pos);gl_Position=interpolateProjectionFor3D(posInTile,spherePos,elevation);\n#else\ngl_Position=u_projection_matrix*vec4(posInTile,elevation,1.0);\n#endif\nfloat colorvalue=color.r*0.2126+color.g*0.7152+color.b*0.0722;v_color=vec4(0.0,0.0,0.0,1.0);vec4 ambientlight=vec4(0.03,0.03,0.03,1.0);color+=ambientlight;vec3 normalForLighting=normal/16384.0;float directional=clamp(dot(normalForLighting,u_lightpos),0.0,1.0);\n#ifdef GLOBE\nmat3 rotMatrix=globeGetRotationMatrix(spherePos);normalForLighting=rotMatrix*normalForLighting;directional=mix(directional,clamp(dot(normalForLighting,u_lightpos_globe),0.0,1.0),u_projection_transition);\n#endif\ndirectional=mix((1.0-u_lightintensity),max((1.0-colorvalue+u_lightintensity),1.0),directional);if (normal.y !=0.0) {directional*=((1.0-u_vertical_gradient)+(u_vertical_gradient*clamp((t+gradient_base)*pow(gradient_height/150.0,0.5),mix(0.7,0.98,1.0-u_lightintensity),1.0)));}v_color.r+=clamp(color.r*directional*u_lightcolor.r,mix(0.0,0.3,1.0-u_lightcolor.r),1.0);v_color.g+=clamp(color.g*directional*u_lightcolor.g,mix(0.0,0.3,1.0-u_lightcolor.g),1.0);v_color.b+=clamp(color.b*directional*u_lightcolor.b,mix(0.0,0.3,1.0-u_lightcolor.b),1.0);v_color*=u_opacity;}"),
9558
9643
  fillExtrusionPattern: prepare("uniform vec2 u_texsize;uniform float u_fade;uniform sampler2D u_image;in vec2 v_pos_a;in vec2 v_pos_b;in vec4 v_lighting;\n#pragma maplibre: define lowp float base\n#pragma maplibre: define lowp float height\n#pragma maplibre: define lowp vec4 pattern_from\n#pragma maplibre: define lowp vec4 pattern_to\n#pragma maplibre: define lowp float pixel_ratio_from\n#pragma maplibre: define lowp float pixel_ratio_to\nvoid main() {\n#pragma maplibre: initialize lowp float base\n#pragma maplibre: initialize lowp float height\n#pragma maplibre: initialize mediump vec4 pattern_from\n#pragma maplibre: initialize mediump vec4 pattern_to\n#pragma maplibre: initialize lowp float pixel_ratio_from\n#pragma maplibre: initialize lowp float pixel_ratio_to\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;vec2 imagecoord=mod(v_pos_a,1.0);vec2 pos=mix(pattern_tl_a/u_texsize,pattern_br_a/u_texsize,imagecoord);vec4 color1=texture(u_image,pos);vec2 imagecoord_b=mod(v_pos_b,1.0);vec2 pos2=mix(pattern_tl_b/u_texsize,pattern_br_b/u_texsize,imagecoord_b);vec4 color2=texture(u_image,pos2);vec4 mixedColor=mix(color1,color2,u_fade);fragColor=mixedColor*v_lighting;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "uniform vec2 u_pixel_coord_upper;uniform vec2 u_pixel_coord_lower;uniform float u_height_factor;uniform vec3 u_scale;uniform float u_vertical_gradient;uniform lowp float u_opacity;uniform vec2 u_fill_translate;uniform vec3 u_lightcolor;uniform lowp vec3 u_lightpos;uniform lowp vec3 u_lightpos_globe;uniform lowp float u_lightintensity;layout(location=0) in vec2 a_pos;layout(location=1) in ivec4 a_normal_ed;\n#ifdef TERRAIN3D\nlayout(location=2) in vec2 a_centroid;\n#endif\n#ifdef GLOBE\nout vec3 v_sphere_pos;\n#endif\nout vec2 v_pos_a;out vec2 v_pos_b;out vec4 v_lighting;\n#pragma maplibre: define lowp float base\n#pragma maplibre: define lowp float height\n#pragma maplibre: define lowp vec4 pattern_from\n#pragma maplibre: define lowp vec4 pattern_to\n#pragma maplibre: define lowp float pixel_ratio_from\n#pragma maplibre: define lowp float pixel_ratio_to\nvoid main() {\n#pragma maplibre: initialize lowp float base\n#pragma maplibre: initialize lowp float height\n#pragma maplibre: initialize mediump vec4 pattern_from\n#pragma maplibre: initialize mediump vec4 pattern_to\n#pragma maplibre: initialize lowp float pixel_ratio_from\n#pragma maplibre: initialize lowp float pixel_ratio_to\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;float tileRatio=u_scale.x;float fromScale=u_scale.y;float toScale=u_scale.z;vec3 normal=vec3(a_normal_ed.xyz);float edgedistance=float(a_normal_ed.w);vec2 display_size_a=(pattern_br_a-pattern_tl_a)/pixel_ratio_from;vec2 display_size_b=(pattern_br_b-pattern_tl_b)/pixel_ratio_to;float gradient_base=max(0.0,base);float gradient_height=max(0.0,height);\n#ifdef TERRAIN3D\nfloat height_terrain3d_offset=get_elevation(a_centroid);float base_terrain3d_offset=height_terrain3d_offset-(base > 0.0 ? 0.0 : 10.0);\n#else\nfloat height_terrain3d_offset=0.0;float base_terrain3d_offset=0.0;\n#endif\nbase=max(0.0,base)+base_terrain3d_offset;height=max(0.0,height)+height_terrain3d_offset;float t=float(a_normal_ed.x & 1);float elevation=t > 0.0 ? height : base;vec2 posInTile=a_pos+u_fill_translate;\n#ifdef GLOBE\nvec3 spherePos=projectToSphere(posInTile,a_pos);vec3 elevatedPos=spherePos*(1.0+elevation/GLOBE_RADIUS);v_sphere_pos=elevatedPos;gl_Position=interpolateProjectionFor3D(posInTile,spherePos,elevation);\n#else\ngl_Position=u_projection_matrix*vec4(posInTile,elevation,1.0);\n#endif\nvec2 pos=a_normal_ed.x==1 && a_normal_ed.y==0 && a_normal_ed.z==16384\n? a_pos\n: vec2(edgedistance,elevation*u_height_factor);v_pos_a=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,fromScale*display_size_a,tileRatio,pos);v_pos_b=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,toScale*display_size_b,tileRatio,pos);v_lighting=vec4(0.0,0.0,0.0,1.0);float directional=clamp(dot(normal/16383.0,u_lightpos),0.0,1.0);directional=mix((1.0-u_lightintensity),max((0.5+u_lightintensity),1.0),directional);if (normal.y !=0.0) {directional*=((1.0-u_vertical_gradient)+(u_vertical_gradient*clamp((t+gradient_base)*pow(gradient_height/150.0,0.5),mix(0.7,0.98,1.0-u_lightintensity),1.0)));}v_lighting.rgb+=clamp(directional*u_lightcolor,mix(vec3(0.0),vec3(0.3),1.0-u_lightcolor),vec3(1.0));v_lighting*=u_opacity;}"),
9559
- hillshadePrepare: prepare("#ifdef GL_ES\nprecision highp float;\n#endif\nuniform sampler2D u_image;in vec2 v_pos;uniform vec2 u_dimension;uniform float u_zoom;uniform vec4 u_unpack;float getElevation(ivec2 texel) {vec4 data=texelFetch(u_image,texel,0)*255.0;data.a=-1.0;return dot(data,u_unpack);}void main() {ivec2 pos=ivec2(gl_FragCoord.xy)+ivec2(1);float tileSize=u_dimension.x-4.0;float a=getElevation(pos+ivec2(-1,-1));float b=getElevation(pos+ivec2(0,-1));float c=getElevation(pos+ivec2(1,-1));float d=getElevation(pos+ivec2(-1,0));float e=getElevation(pos);float f=getElevation(pos+ivec2(1,0));float g=getElevation(pos+ivec2(-1,1));float h=getElevation(pos+ivec2(0,1));float i=getElevation(pos+ivec2(1,1));float exaggerationFactor=u_zoom < 2.0 ? 0.4 : u_zoom < 4.5 ? 0.35 : 0.3;float exaggeration=u_zoom < 15.0 ? (u_zoom-15.0)*exaggerationFactor : 0.0;vec2 deriv=vec2((c+f+f+i)-(a+d+d+g),(g+h+h+i)-(a+b+b+c))*tileSize/pow(2.0,exaggeration+(28.2562-u_zoom));fragColor=clamp(vec4(deriv.x/8.0+0.5,deriv.y/8.0+0.5,1.0,1.0),0.0,1.0);\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "uniform mat4 u_matrix;uniform vec2 u_dimension;layout(location=0) in vec2 a_pos;layout(location=1) in vec2 a_texture_pos;out vec2 v_pos;void main() {gl_Position=u_matrix*vec4(a_pos,0,1);highp vec2 epsilon=1.0/u_dimension;float scale=(u_dimension.x-2.0)/u_dimension.x;v_pos=(a_texture_pos/8192.0)*scale+epsilon;}"),
9560
- hillshade: prepare("uniform sampler2D u_image;in highp vec2 v_pos;uniform vec2 u_latrange;uniform float u_exaggeration;uniform vec4 u_accent;uniform int u_method;uniform float u_altitudes[NUM_ILLUMINATION_SOURCES];uniform float u_azimuths[NUM_ILLUMINATION_SOURCES];uniform vec4 u_shadows[NUM_ILLUMINATION_SOURCES];uniform vec4 u_highlights[NUM_ILLUMINATION_SOURCES];\n#define PI 3.141592653589793\n#define STANDARD 0\n#define COMBINED 1\n#define IGOR 2\n#define MULTIDIRECTIONAL 3\n#define BASIC 4\nfloat get_aspect(vec2 deriv){return deriv.x !=0.0 ? atan(deriv.y,-deriv.x) : PI/2.0*(deriv.y > 0.0 ? 1.0 :-1.0);}void igor_hillshade(vec2 deriv){deriv=deriv*u_exaggeration*2.0;float aspect=get_aspect(deriv);float azimuth=u_azimuths[0]+PI;float slope_stength=atan(length(deriv))*2.0/PI;float aspect_strength=1.0-abs(mod((aspect+azimuth)/PI+0.5,2.0)-1.0);float shadow_strength=slope_stength*aspect_strength;float highlight_strength=slope_stength*(1.0-aspect_strength);fragColor=u_shadows[0]*shadow_strength+u_highlights[0]*highlight_strength;}void standard_hillshade(vec2 deriv){float azimuth=u_azimuths[0]+PI;float slope=atan(0.625*length(deriv));float aspect=get_aspect(deriv);float intensity=u_exaggeration;float base=1.875-intensity*1.75;float maxValue=0.5*PI;float scaledSlope=intensity !=0.5 ? ((pow(base,slope)-1.0)/(pow(base,maxValue)-1.0))*maxValue : slope;float accent=cos(scaledSlope);vec4 accent_color=(1.0-accent)*u_accent*clamp(intensity*2.0,0.0,1.0);float shade=abs(mod((aspect+azimuth)/PI+0.5,2.0)-1.0);vec4 shade_color=mix(u_shadows[0],u_highlights[0],shade)*sin(scaledSlope)*clamp(intensity*2.0,0.0,1.0);fragColor=accent_color*(1.0-shade_color.a)+shade_color;}void basic_hillshade(vec2 deriv){deriv=deriv*u_exaggeration*2.0;float azimuth=u_azimuths[0]+PI;float cos_az=cos(azimuth);float sin_az=sin(azimuth);float cos_alt=cos(u_altitudes[0]);float sin_alt=sin(u_altitudes[0]);float cang=(sin_alt-(deriv.y*cos_az*cos_alt-deriv.x*sin_az*cos_alt))/sqrt(1.0+dot(deriv,deriv));float shade=clamp(cang,0.0,1.0);if(shade > 0.5){fragColor=u_highlights[0]*(2.0*shade-1.0);}else\n{fragColor=u_shadows[0]*(1.0-2.0*shade);}}void multidirectional_hillshade(vec2 deriv){deriv=deriv*u_exaggeration*2.0;fragColor=vec4(0,0,0,0);for(int i=0; i < NUM_ILLUMINATION_SOURCES; i++){float cos_alt=cos(u_altitudes[i]);float sin_alt=sin(u_altitudes[i]);float cos_az=-cos(u_azimuths[i]);float sin_az=-sin(u_azimuths[i]);float cang=(sin_alt-(deriv.y*cos_az*cos_alt-deriv.x*sin_az*cos_alt))/sqrt(1.0+dot(deriv,deriv));float shade=clamp(cang,0.0,1.0);if(shade > 0.5){fragColor+=u_highlights[i]*(2.0*shade-1.0)/float(NUM_ILLUMINATION_SOURCES);}else\n{fragColor+=u_shadows[i]*(1.0-2.0*shade)/float(NUM_ILLUMINATION_SOURCES);}}}void combined_hillshade(vec2 deriv){deriv=deriv*u_exaggeration*2.0;float azimuth=u_azimuths[0]+PI;float cos_az=cos(azimuth);float sin_az=sin(azimuth);float cos_alt=cos(u_altitudes[0]);float sin_alt=sin(u_altitudes[0]);float cang=acos((sin_alt-(deriv.y*cos_az*cos_alt-deriv.x*sin_az*cos_alt))/sqrt(1.0+dot(deriv,deriv)));cang=clamp(cang,0.0,PI/2.0);float shade=cang*atan(length(deriv))*4.0/PI/PI;float highlight=(PI/2.0-cang)*atan(length(deriv))*4.0/PI/PI;fragColor=u_shadows[0]*shade+u_highlights[0]*highlight;}void main() {highp vec2 size=vec2(textureSize(u_image,0));highp vec2 texturePos=(v_pos*(size-2.0)+1.0)/size;vec4 pixel=texture(u_image,texturePos);float scaleFactor=cos(radians((u_latrange[0]-u_latrange[1])*(1.0-v_pos.y)+u_latrange[1]));vec2 deriv=((pixel.rg*8.0)-4.0)/scaleFactor;if (u_method==BASIC) {basic_hillshade(deriv);} else if (u_method==COMBINED) {combined_hillshade(deriv);} else if (u_method==IGOR) {igor_hillshade(deriv);} else if (u_method==MULTIDIRECTIONAL) {multidirectional_hillshade(deriv);} else if (u_method==STANDARD) {standard_hillshade(deriv);} else {standard_hillshade(deriv);}\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "uniform mat4 u_matrix;layout(location=0) in vec2 a_pos;out vec2 v_pos;void main() {gl_Position=projectTile(a_pos,a_pos);v_pos=a_pos/8192.0;if (a_pos.y <-32767.5) {v_pos.y=0.0;}if (a_pos.y > 32766.5) {v_pos.y=1.0;}}"),
9644
+ hillshadePrepare: prepare("#ifdef GL_ES\nprecision highp float;\n#endif\nuniform sampler2D u_image;in vec2 v_pos;uniform vec2 u_dimension;uniform float u_zoom;uniform vec4 u_unpack;float getElevation(ivec2 texel) {vec4 data=texelFetch(u_image,texel,0)*255.0;data.a=-1.0;return dot(data,u_unpack);}void main() {ivec2 pos=ivec2(gl_FragCoord.xy)+ivec2(1);float tileSize=u_dimension.x-4.0;float a=getElevation(pos+ivec2(-1,-1));float b=getElevation(pos+ivec2(0,-1));float c=getElevation(pos+ivec2(1,-1));float d=getElevation(pos+ivec2(-1,0));float e=getElevation(pos);float f=getElevation(pos+ivec2(1,0));float g=getElevation(pos+ivec2(-1,1));float h=getElevation(pos+ivec2(0,1));float i=getElevation(pos+ivec2(1,1));float exaggerationFactor=u_zoom < 2.0 ? 0.4 : u_zoom < 4.5 ? 0.35 : 0.3;float exaggeration=u_zoom < 15.0 ? (u_zoom-15.0)*exaggerationFactor : 0.0;vec2 deriv=vec2((c+f+f+i)-(a+d+d+g),(g+h+h+i)-(a+b+b+c))*tileSize/pow(2.0,exaggeration+(28.2562-u_zoom));fragColor=clamp(vec4(deriv.x/8.0+128.0/255.0,deriv.y/8.0+128.0/255.0,1.0,1.0),0.0,1.0);\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "uniform mat4 u_matrix;uniform vec2 u_dimension;layout(location=0) in vec2 a_pos;layout(location=1) in vec2 a_texture_pos;out vec2 v_pos;void main() {gl_Position=u_matrix*vec4(a_pos,0,1);highp vec2 epsilon=1.0/u_dimension;float scale=(u_dimension.x-2.0)/u_dimension.x;v_pos=(a_texture_pos/8192.0)*scale+epsilon;}"),
9645
+ hillshade: prepare("uniform sampler2D u_image;in highp vec2 v_pos;uniform vec2 u_latrange;uniform float u_exaggeration;uniform vec4 u_accent;uniform int u_method;uniform float u_altitudes[NUM_ILLUMINATION_SOURCES];uniform float u_azimuths[NUM_ILLUMINATION_SOURCES];uniform vec4 u_shadows[NUM_ILLUMINATION_SOURCES];uniform vec4 u_highlights[NUM_ILLUMINATION_SOURCES];\n#define PI 3.141592653589793\n#define STANDARD 0\n#define COMBINED 1\n#define IGOR 2\n#define MULTIDIRECTIONAL 3\n#define BASIC 4\nfloat get_aspect(vec2 deriv){return deriv.x !=0.0 ? atan(deriv.y,-deriv.x) : PI/2.0*(deriv.y > 0.0 ? 1.0 :-1.0);}void igor_hillshade(vec2 deriv){deriv=deriv*u_exaggeration*2.0;float aspect=get_aspect(deriv);float azimuth=u_azimuths[0]+PI;float slope_stength=atan(length(deriv))*2.0/PI;float aspect_strength=1.0-abs(mod((aspect+azimuth)/PI+0.5,2.0)-1.0);float shadow_strength=slope_stength*aspect_strength;float highlight_strength=slope_stength*(1.0-aspect_strength);fragColor=u_shadows[0]*shadow_strength+u_highlights[0]*highlight_strength;}void standard_hillshade(vec2 deriv){float azimuth=u_azimuths[0]+PI;float slope=atan(0.625*length(deriv));float aspect=get_aspect(deriv);float intensity=u_exaggeration;float base=1.875-intensity*1.75;float maxValue=0.5*PI;float scaledSlope=intensity !=0.5 ? ((pow(base,slope)-1.0)/(pow(base,maxValue)-1.0))*maxValue : slope;float accent=cos(scaledSlope);vec4 accent_color=(1.0-accent)*u_accent*clamp(intensity*2.0,0.0,1.0);float shade=abs(mod((aspect+azimuth)/PI+0.5,2.0)-1.0);vec4 shade_color=mix(u_shadows[0],u_highlights[0],shade)*sin(scaledSlope)*clamp(intensity*2.0,0.0,1.0);fragColor=accent_color*(1.0-shade_color.a)+shade_color;}void basic_hillshade(vec2 deriv){deriv=deriv*u_exaggeration*2.0;float azimuth=u_azimuths[0]+PI;float cos_az=cos(azimuth);float sin_az=sin(azimuth);float cos_alt=cos(u_altitudes[0]);float sin_alt=sin(u_altitudes[0]);float cang=(sin_alt-(deriv.y*cos_az*cos_alt-deriv.x*sin_az*cos_alt))/sqrt(1.0+dot(deriv,deriv));float shade=clamp(cang,0.0,1.0);if(shade > 0.5){fragColor=u_highlights[0]*(2.0*shade-1.0);}else\n{fragColor=u_shadows[0]*(1.0-2.0*shade);}}void multidirectional_hillshade(vec2 deriv){deriv=deriv*u_exaggeration*2.0;fragColor=vec4(0,0,0,0);for(int i=0; i < NUM_ILLUMINATION_SOURCES; i++){float cos_alt=cos(u_altitudes[i]);float sin_alt=sin(u_altitudes[i]);float cos_az=-cos(u_azimuths[i]);float sin_az=-sin(u_azimuths[i]);float cang=(sin_alt-(deriv.y*cos_az*cos_alt-deriv.x*sin_az*cos_alt))/sqrt(1.0+dot(deriv,deriv));float shade=clamp(cang,0.0,1.0);if(shade > 0.5){fragColor+=u_highlights[i]*(2.0*shade-1.0)/float(NUM_ILLUMINATION_SOURCES);}else\n{fragColor+=u_shadows[i]*(1.0-2.0*shade)/float(NUM_ILLUMINATION_SOURCES);}}}void combined_hillshade(vec2 deriv){deriv=deriv*u_exaggeration*2.0;float azimuth=u_azimuths[0]+PI;float cos_az=cos(azimuth);float sin_az=sin(azimuth);float cos_alt=cos(u_altitudes[0]);float sin_alt=sin(u_altitudes[0]);float cang=acos((sin_alt-(deriv.y*cos_az*cos_alt-deriv.x*sin_az*cos_alt))/sqrt(1.0+dot(deriv,deriv)));cang=clamp(cang,0.0,PI/2.0);float shade=cang*atan(length(deriv))*4.0/PI/PI;float highlight=(PI/2.0-cang)*atan(length(deriv))*4.0/PI/PI;fragColor=u_shadows[0]*shade+u_highlights[0]*highlight;}void main() {highp vec2 size=vec2(textureSize(u_image,0));highp vec2 texturePos=(v_pos*(size-2.0)+1.0)/size;vec4 pixel=texture(u_image,texturePos);float scaleFactor=cos(radians((u_latrange[0]-u_latrange[1])*(1.0-v_pos.y)+u_latrange[1]));vec2 deriv=(pixel.rg-128.0/255.0)*8.0/scaleFactor;if (u_method==BASIC) {basic_hillshade(deriv);} else if (u_method==COMBINED) {combined_hillshade(deriv);} else if (u_method==IGOR) {igor_hillshade(deriv);} else if (u_method==MULTIDIRECTIONAL) {multidirectional_hillshade(deriv);} else if (u_method==STANDARD) {standard_hillshade(deriv);} else {standard_hillshade(deriv);}\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "uniform mat4 u_matrix;layout(location=0) in vec2 a_pos;out vec2 v_pos;void main() {gl_Position=projectTile(a_pos,a_pos);v_pos=a_pos/8192.0;if (a_pos.y <-32767.5) {v_pos.y=0.0;}if (a_pos.y > 32766.5) {v_pos.y=1.0;}}"),
9561
9646
  line: prepare("flat in vec2 v_width2;in vec2 v_normal;in float v_gamma_scale;\n#ifdef GLOBE\nin float v_depth;\n#endif\n#pragma maplibre: define highp vec4 color\n#pragma maplibre: define lowp float blur\n#pragma maplibre: define lowp float opacity\nvoid main() {\n#pragma maplibre: initialize highp vec4 color\n#pragma maplibre: initialize lowp float blur\n#pragma maplibre: initialize lowp float opacity\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);fragColor=color*(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\nlayout(location=0) in ivec2 a_pos_normal;layout(location=1) in uvec4 a_data;uniform vec2 u_translation;uniform mediump float u_ratio;out vec2 v_normal;flat out vec2 v_width2;out float v_gamma_scale;out highp float v_linesofar;\n#ifdef GLOBE\nout float v_depth;\n#endif\n#pragma maplibre: define highp vec4 color\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\nvoid main() {\n#pragma maplibre: initialize highp vec4 color\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\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);v_linesofar=float((a_data.z >> 2u)+a_data.w*64u)*2.0;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\nv_width2=vec2(outset,inset);}"),
9562
9647
  lineGradient: prepare("uniform sampler2D u_image;flat in vec2 v_width2;in vec2 v_normal;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\nvoid main() {\n#pragma maplibre: initialize lowp float blur\n#pragma maplibre: initialize lowp float opacity\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);fragColor=color*(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\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 float u_image_height;out vec2 v_normal;flat out vec2 v_width2;out float v_gamma_scale;out highp vec2 v_uv;\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\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\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);highp float texel_height=1.0/u_image_height;highp 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\nv_width2=vec2(outset,inset);}"),
9563
9648
  linePattern: prepare("#ifdef GL_ES\nprecision highp float;\n#endif\nuniform vec2 u_texsize;uniform float u_fade;uniform mediump vec3 u_scale;uniform sampler2D u_image;in vec2 v_normal;flat in vec2 v_width2;in float v_linesofar;in float v_gamma_scale;flat in float v_width;\n#ifdef GLOBE\nin float v_depth;\n#endif\n#pragma maplibre: define lowp vec4 pattern_from\n#pragma maplibre: define lowp vec4 pattern_to\n#pragma maplibre: define lowp float pixel_ratio_from\n#pragma maplibre: define lowp float pixel_ratio_to\n#pragma maplibre: define lowp float blur\n#pragma maplibre: define lowp float opacity\nvoid main() {\n#pragma maplibre: initialize mediump vec4 pattern_from\n#pragma maplibre: initialize mediump vec4 pattern_to\n#pragma maplibre: initialize lowp float pixel_ratio_from\n#pragma maplibre: initialize lowp float pixel_ratio_to\n#pragma maplibre: initialize lowp float blur\n#pragma maplibre: initialize lowp float opacity\nclipAntimeridian();vec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;float tileZoomRatio=u_scale.x;float fromScale=u_scale.y;float toScale=u_scale.z;vec2 display_size_a=(pattern_br_a-pattern_tl_a)/pixel_ratio_from;vec2 display_size_b=(pattern_br_b-pattern_tl_b)/pixel_ratio_to;vec2 pattern_size_a=vec2(display_size_a.x*fromScale/tileZoomRatio,display_size_a.y);vec2 pattern_size_b=vec2(display_size_b.x*toScale/tileZoomRatio,display_size_b.y);float aspect_a=display_size_a.y/v_width;float aspect_b=display_size_b.y/v_width;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);float x_a=mod(v_linesofar/pattern_size_a.x*aspect_a,1.0);float x_b=mod(v_linesofar/pattern_size_b.x*aspect_b,1.0);float y=0.5*v_normal.y+0.5;vec2 texel_size=1.0/u_texsize;vec2 pos_a=mix(pattern_tl_a*texel_size-texel_size,pattern_br_a*texel_size+texel_size,vec2(x_a,y));vec2 pos_b=mix(pattern_tl_b*texel_size-texel_size,pattern_br_b*texel_size+texel_size,vec2(x_b,y));vec4 color=mix(texture(u_image,pos_a),texture(u_image,pos_b),u_fade);fragColor=color*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;uniform vec2 u_translation;uniform mediump float u_ratio;out vec2 v_normal;flat out vec2 v_width2;out float v_linesofar;out float v_gamma_scale;flat out float v_width;\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 lowp float offset\n#pragma maplibre: define mediump float gapwidth\n#pragma maplibre: define mediump float width\n#pragma maplibre: define lowp float floorwidth\n#pragma maplibre: define lowp vec4 pattern_from\n#pragma maplibre: define lowp vec4 pattern_to\n#pragma maplibre: define lowp float pixel_ratio_from\n#pragma maplibre: define lowp float pixel_ratio_to\nvoid main() {\n#pragma maplibre: initialize lowp float blur\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize lowp float offset\n#pragma maplibre: initialize mediump float gapwidth\n#pragma maplibre: initialize mediump float width\n#pragma maplibre: initialize lowp float floorwidth\n#pragma maplibre: initialize mediump vec4 pattern_from\n#pragma maplibre: initialize mediump vec4 pattern_to\n#pragma maplibre: initialize lowp float pixel_ratio_from\n#pragma maplibre: initialize lowp float pixel_ratio_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;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\nv_linesofar=a_linesofar;v_width2=vec2(outset,inset);v_width=floorwidth;}"),
@@ -10300,7 +10385,7 @@ var TransformHelper = class {
10300
10385
  const altitudeAGL = alt - elevation;
10301
10386
  let distanceToCenter;
10302
10387
  let clampedElevation = elevation;
10303
- if (dzNormalized * altitudeAGL >= 0 || Math.abs(dzNormalized) < .1) {
10388
+ if (dzNormalized * altitudeAGL >= 0 || Math.abs(dzNormalized) < Math.cos(degreesToRadians(89.25))) {
10304
10389
  distanceToCenter = 1e4;
10305
10390
  clampedElevation = alt + distanceToCenter * dzNormalized;
10306
10391
  } else distanceToCenter = -altitudeAGL / dzNormalized;
@@ -11618,7 +11703,7 @@ var MercatorTransform = class MercatorTransform {
11618
11703
  }
11619
11704
  isPointOnMapSurface(p, terrain) {
11620
11705
  if (terrain) return this.screenTerrainPointToMercatorCoordinate(p, terrain) != null;
11621
- return p.y > this.height / 2 - getMercatorHorizon(this);
11706
+ return p.y > this.centerPoint.y - getMercatorHorizon(this);
11622
11707
  }
11623
11708
  calculatePosMatrix(tileID, aligned = false, useFloat32 = false) {
11624
11709
  const posMatrixKey = tileID.key ?? calculateTileKey(tileID.wrap, tileID.canonical.z, tileID.canonical.z, tileID.canonical.x, tileID.canonical.y);
@@ -12104,42 +12189,64 @@ var MercatorCameraHelper = class {
12104
12189
  tr.setLocationAtPoint(tr.renderWorldCopies ? newCenter.wrap() : newCenter, pointAtOffset);
12105
12190
  }
12106
12191
  };
12192
+ const endTransform = this._transformAtAnimationEnd(tr, endZoom, endEulerAngles, options.padding);
12193
+ if (options.around) endTransform.setLocationAtPoint(options.around, options.aroundPoint);
12194
+ else endTransform.setLocationAtPoint(center, endTransform.centerPoint.add(options.offsetAsPoint));
12107
12195
  return {
12108
12196
  easeFunc,
12109
12197
  isZooming,
12110
- elevationCenter: center
12198
+ elevationCenter: endTransform.center
12111
12199
  };
12112
12200
  }
12113
12201
  handleFlyTo(tr, options) {
12114
12202
  const optionsZoom = typeof options.zoom !== "undefined";
12115
12203
  const startZoom = tr.zoom;
12116
12204
  const constrained = tr.applyConstrain(LngLat.convert(options.center || options.locationAtOffset), optionsZoom ? +options.zoom : startZoom);
12117
- const targetCenter = constrained.center;
12205
+ const constrainedCenter = constrained.center;
12118
12206
  const targetZoom = constrained.zoom;
12119
- normalizeCenter(tr, targetCenter);
12207
+ normalizeCenter(tr, constrainedCenter);
12120
12208
  const startWorldSize = tr.worldSize;
12121
12209
  const from = projectToWorldCoordinates(startWorldSize, options.locationAtOffset);
12122
- const delta = projectToWorldCoordinates(startWorldSize, targetCenter).sub(from);
12210
+ const delta = projectToWorldCoordinates(startWorldSize, constrainedCenter).sub(from);
12123
12211
  const pixelPathLength = delta.mag();
12124
12212
  const scaleOfZoom = zoomScale(targetZoom - startZoom);
12125
12213
  const requestedMinZoom = typeof options.minZoom !== "undefined" ? +options.minZoom : tr.minZoom;
12126
12214
  const effectiveMinZoom = Math.max(requestedMinZoom, tr.minZoom);
12127
12215
  const minZoomPreConstrain = Math.min(effectiveMinZoom, startZoom, targetZoom);
12128
- const minZoom = tr.applyConstrain(targetCenter, minZoomPreConstrain).zoom;
12216
+ const minZoom = tr.applyConstrain(constrainedCenter, minZoomPreConstrain).zoom;
12129
12217
  const scaleOfMinZoom = zoomScale(minZoom - startZoom);
12130
12218
  const easeFunc = (k, scale, centerFactor, pointAtOffset) => {
12131
12219
  tr.setZoom(k === 1 ? targetZoom : startZoom + scaleZoom(scale));
12132
- const newCenter = k === 1 ? targetCenter : unprojectFromWorldCoordinates(startWorldSize, from.add(delta.mult(centerFactor)));
12220
+ const newCenter = k === 1 ? constrainedCenter : unprojectFromWorldCoordinates(startWorldSize, from.add(delta.mult(centerFactor)));
12133
12221
  tr.setLocationAtPoint(tr.renderWorldCopies ? newCenter.wrap() : newCenter, pointAtOffset);
12134
12222
  };
12223
+ const endTransform = this._transformAtAnimationEnd(tr, targetZoom, {
12224
+ roll: options.roll,
12225
+ pitch: options.pitch,
12226
+ bearing: options.bearing
12227
+ }, options.padding);
12228
+ endTransform.setLocationAtPoint(constrainedCenter, endTransform.centerPoint.add(options.offsetAsPoint));
12135
12229
  return {
12136
12230
  easeFunc,
12137
12231
  scaleOfZoom,
12138
- targetCenter,
12232
+ targetCenter: endTransform.center,
12139
12233
  scaleOfMinZoom,
12140
12234
  pixelPathLength
12141
12235
  };
12142
12236
  }
12237
+ /**
12238
+ * A copy of the transform at the animation's end zoom, angles and padding, for reading where the map center
12239
+ * ends up once a location is placed at its screen point.
12240
+ */
12241
+ _transformAtAnimationEnd(tr, zoom, angles, padding) {
12242
+ const endTransform = tr.clone();
12243
+ endTransform.setZoom(zoom);
12244
+ endTransform.setRoll(angles.roll);
12245
+ endTransform.setPitch(angles.pitch);
12246
+ endTransform.setBearing(angles.bearing);
12247
+ endTransform.setPadding(padding);
12248
+ return endTransform;
12249
+ }
12143
12250
  };
12144
12251
  //#endregion
12145
12252
  //#region src/style/projection_properties.g.ts
@@ -12647,9 +12754,11 @@ function raySphereIntersection(origin, direction, radius = 1) {
12647
12754
  const q = -originDotDirection + (originDotDirection < 0 ? 1 : -1) * Math.sqrt(discriminant);
12648
12755
  const t0 = c / q;
12649
12756
  const t1 = q;
12757
+ const tMax = Math.max(t0, t1);
12758
+ if (tMax < 0) return null;
12650
12759
  return {
12651
12760
  tMin: Math.min(t0, t1),
12652
- tMax: Math.max(t0, t1)
12761
+ tMax
12653
12762
  };
12654
12763
  }
12655
12764
  /** Height of the atmosphere relative to the globe radius, 100 km for Earth. */
@@ -14472,6 +14581,7 @@ var VerticalPerspectiveCameraHelper = class VerticalPerspectiveCameraHelper {
14472
14581
  }
14473
14582
  cameraForBoxAndBearing(options, padding, bounds, bearing, tr) {
14474
14583
  const result = cameraForBoxAndBearing(options, padding, bounds, bearing, tr);
14584
+ if (!result) return;
14475
14585
  const xLeft = padding.left / tr.width * 2 - 1;
14476
14586
  const xRight = (tr.width - padding.right) / tr.width * 2 - 1;
14477
14587
  const yTop = padding.top / tr.height * -2 + 1;
@@ -16263,13 +16373,11 @@ var UniformBuffer = class {
16263
16373
  this.buffer = gl.createBuffer();
16264
16374
  gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer);
16265
16375
  gl.bufferData(gl.UNIFORM_BUFFER, layout.sizeWords * 4, gl.DYNAMIC_DRAW);
16266
- gl.bindBufferBase(gl.UNIFORM_BUFFER, binding, this.buffer);
16267
16376
  this.uploaded = new Float32Array(layout.sizeWords);
16268
16377
  this.pending = new Float32Array(layout.sizeWords);
16269
16378
  this.uploadedWords = new Uint32Array(this.uploaded.buffer);
16270
16379
  this.pendingWords = new Uint32Array(this.pending.buffer);
16271
16380
  this.hasData = false;
16272
- this.bindingDirty = false;
16273
16381
  }
16274
16382
  upload() {
16275
16383
  const gl = this.context.gl;
@@ -16282,19 +16390,23 @@ var UniformBuffer = class {
16282
16390
  break;
16283
16391
  }
16284
16392
  }
16285
- this.bind();
16286
- if (!changed) return;
16393
+ if (!changed) {
16394
+ this.bind();
16395
+ return;
16396
+ }
16287
16397
  gl.bindBufferBase(gl.UNIFORM_BUFFER, this.binding, this.buffer);
16398
+ this.context.boundUniformBuffers[this.binding] = this.buffer;
16288
16399
  gl.bufferData(gl.UNIFORM_BUFFER, this.pending, gl.DYNAMIC_DRAW);
16289
16400
  this.uploaded.set(this.pending);
16290
16401
  this.hasData = true;
16291
16402
  }
16292
16403
  /** Restores the indexed binding after external rendering without uploading unchanged data. */
16293
16404
  bind() {
16294
- if (!this.bindingDirty) return;
16405
+ const boundUniformBuffers = this.context.boundUniformBuffers;
16406
+ if (boundUniformBuffers[this.binding] === this.buffer) return;
16295
16407
  const gl = this.context.gl;
16296
16408
  gl.bindBufferBase(gl.UNIFORM_BUFFER, this.binding, this.buffer);
16297
- this.bindingDirty = false;
16409
+ boundUniformBuffers[this.binding] = this.buffer;
16298
16410
  }
16299
16411
  destroy() {
16300
16412
  const gl = this.context.gl;
@@ -16335,7 +16447,7 @@ const layout$2 = std140Layout([
16335
16447
  const offsets$2 = layout$2.offsets;
16336
16448
  /**
16337
16449
  * @internal
16338
- * The buffer behind the `ProjectionUBO` block in the shader preludes, written by `Program.draw` before each draw.
16450
+ * The buffer behind the `ProjectionUBO` block in the shader preludes.
16339
16451
  */
16340
16452
  function createProjectionUniformBuffer(context) {
16341
16453
  return new UniformBuffer(context, UBO_BINDINGS.ProjectionUBO, layout$2);
@@ -16350,6 +16462,34 @@ function updateProjectionUniformBuffer(buffer, projectionData) {
16350
16462
  buffer.pendingWords[offsets$2.u_projection_clip_antimeridian] = projectionData.clipAntimeridian ? 1 : 0;
16351
16463
  buffer.upload();
16352
16464
  }
16465
+ function bindProjectionUniformBuffer(context, projectionData) {
16466
+ if (!projectionData) {
16467
+ context.projectionUniformBuffer.bind();
16468
+ return;
16469
+ }
16470
+ const key = projectionData.uniformBufferKey;
16471
+ if (key === void 0) {
16472
+ updateProjectionUniformBuffer(context.projectionUniformBuffer, projectionData);
16473
+ return;
16474
+ }
16475
+ const keyedBuffer = context.keyedProjectionUniformBuffers.get(key);
16476
+ if (keyedBuffer) {
16477
+ keyedBuffer.bind();
16478
+ return;
16479
+ }
16480
+ const buffer = context.freeProjectionUniformBuffers.pop() ?? createProjectionUniformBuffer(context);
16481
+ context.keyedProjectionUniformBuffers.set(key, buffer);
16482
+ updateProjectionUniformBuffer(buffer, projectionData);
16483
+ }
16484
+ function releaseProjectionUniformBuffers(context) {
16485
+ context.freeProjectionUniformBuffers.push(...context.keyedProjectionUniformBuffers.values());
16486
+ context.keyedProjectionUniformBuffers.clear();
16487
+ }
16488
+ function destroyProjectionUniformBuffers(context) {
16489
+ context.projectionUniformBuffer.destroy();
16490
+ for (const buffer of context.keyedProjectionUniformBuffers.values()) buffer.destroy();
16491
+ for (const buffer of context.freeProjectionUniformBuffers) buffer.destroy();
16492
+ }
16353
16493
  //#endregion
16354
16494
  //#region src/webgl/terrain_uniform_buffer.ts
16355
16495
  const layout$1 = std140Layout([
@@ -16494,7 +16634,6 @@ var Program = class {
16494
16634
  const gl = context.gl;
16495
16635
  if (this.failedToCreate) return;
16496
16636
  context.program.set(this.program);
16497
- context.projectionUniformBuffer.bind();
16498
16637
  context.terrainUniformBuffer.bind();
16499
16638
  context.frameUniformBuffer.bind();
16500
16639
  context.setDepthMode(depthMode);
@@ -16509,7 +16648,7 @@ var Program = class {
16509
16648
  for (const name in this.terrainUniforms) this.terrainUniforms[name].set(terrain[name]);
16510
16649
  updateTerrainUniformBuffer(context.terrainUniformBuffer, terrain);
16511
16650
  }
16512
- if (projectionData) updateProjectionUniformBuffer(context.projectionUniformBuffer, projectionData);
16651
+ bindProjectionUniformBuffer(context, projectionData);
16513
16652
  if (uniformValues) for (const name in this.fixedUniforms) this.fixedUniforms[name].set(uniformValues[name]);
16514
16653
  if (configuration) configuration.setUniforms(context, this.binderUniforms, currentProperties, { zoom });
16515
16654
  let primitiveSize = 0;
@@ -17897,7 +18036,10 @@ var Context = class {
17897
18036
  this.maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
17898
18037
  gl.getExtension("EXT_color_buffer_half_float");
17899
18038
  gl.getExtension("EXT_color_buffer_float");
18039
+ this.boundUniformBuffers = [];
17900
18040
  this.projectionUniformBuffer = createProjectionUniformBuffer(this);
18041
+ this.keyedProjectionUniformBuffers = /* @__PURE__ */ new Map();
18042
+ this.freeProjectionUniformBuffers = [];
17901
18043
  this.terrainUniformBuffer = createTerrainUniformBuffer(this);
17902
18044
  this.frameUniformBuffer = createFrameUniformBuffer(this);
17903
18045
  }
@@ -17961,9 +18103,7 @@ var Context = class {
17961
18103
  this.pixelStoreUnpack.dirty = true;
17962
18104
  this.pixelStoreUnpackPremultiplyAlpha.dirty = true;
17963
18105
  this.pixelStoreUnpackFlipY.dirty = true;
17964
- this.projectionUniformBuffer.bindingDirty = true;
17965
- this.terrainUniformBuffer.bindingDirty = true;
17966
- this.frameUniformBuffer.bindingDirty = true;
18106
+ this.boundUniformBuffers = [];
17967
18107
  }
17968
18108
  /**
17969
18109
  * Reset some GL state to default values before handing users the raw context, as we do for
@@ -18142,12 +18282,15 @@ function createRenderContext(transform, projection, terrain) {
18142
18282
  };
18143
18283
  }
18144
18284
  function getProjectionDataForTile(renderContext, tileID, options = {}) {
18145
- return renderContext.transform.getProjectionData({
18285
+ const projectionData = renderContext.transform.getProjectionData({
18146
18286
  overscaledTileID: tileID,
18147
18287
  aligned: options.aligned,
18148
18288
  applyGlobeMatrix: !renderContext.isRenderingToTexture,
18149
18289
  applyTerrainMatrix: options.applyTerrainMatrix ?? true
18150
18290
  });
18291
+ if (renderContext.isRenderingToTexture) return projectionData;
18292
+ projectionData.uniformBufferKey = options.aligned ? `${tileID.key}/aligned` : tileID.key;
18293
+ return projectionData;
18151
18294
  }
18152
18295
  /**
18153
18296
  * Returns terrain data for a tile.
@@ -19700,8 +19843,8 @@ var Painter = class Painter {
19700
19843
  this.crossTileSymbolIndex = new CrossTileSymbolIndex();
19701
19844
  }
19702
19845
  resize(width, height, pixelRatio) {
19703
- this.width = Math.floor(width * pixelRatio);
19704
- this.height = Math.floor(height * pixelRatio);
19846
+ this.width = Math.round(width * pixelRatio);
19847
+ this.height = Math.round(height * pixelRatio);
19705
19848
  this.pixelRatio = pixelRatio;
19706
19849
  this.context.viewport.set([
19707
19850
  0,
@@ -19957,6 +20100,7 @@ var Painter = class Painter {
19957
20100
  this.symbolFadeChange = style.placement.symbolFadeChange(now());
19958
20101
  updateFrameUniformBuffer(this.context.frameUniformBuffer, this);
19959
20102
  this.imageManager.beginFrame();
20103
+ releaseProjectionUniformBuffers(this.context);
19960
20104
  const layerIds = this.style._order;
19961
20105
  const tileManagers = this.style.tileManagers;
19962
20106
  const coordsAscending = {};
@@ -20262,7 +20406,7 @@ var Painter = class Painter {
20262
20406
  if (this.tileExtentMesh) this.tileExtentMesh.vertexBuffer?.destroy();
20263
20407
  if (this.tileExtentMesh) this.tileExtentMesh.indexBuffer?.destroy();
20264
20408
  if (this.debugOverlayTexture) this.debugOverlayTexture.destroy();
20265
- this.context.projectionUniformBuffer.destroy();
20409
+ destroyProjectionUniformBuffers(this.context);
20266
20410
  this.context.terrainUniformBuffer.destroy();
20267
20411
  this.context.frameUniformBuffer.destroy();
20268
20412
  if (this.cache) {
@@ -20998,7 +21142,7 @@ var TapZoomHandler = class {
20998
21142
  return { cameraAnimation: (map) => map.easeTo({
20999
21143
  duration: 300,
21000
21144
  zoom: evaluateZoomSnap(tr.zoom + 1, map.getZoomSnap()),
21001
- around: tr.unproject(zoomInPoint)
21145
+ around: tr.transform.isPointOnMapSurface(zoomInPoint) ? tr.unproject(zoomInPoint) : void 0
21002
21146
  }, { originalEvent: e }) };
21003
21147
  } else if (zoomOutPoint) {
21004
21148
  this._active = true;
@@ -21007,7 +21151,7 @@ var TapZoomHandler = class {
21007
21151
  return { cameraAnimation: (map) => map.easeTo({
21008
21152
  duration: 300,
21009
21153
  zoom: evaluateZoomSnap(tr.zoom - 1, map.getZoomSnap()),
21010
- around: tr.unproject(zoomOutPoint)
21154
+ around: tr.transform.isPointOnMapSurface(zoomOutPoint) ? tr.unproject(zoomOutPoint) : void 0
21011
21155
  }, { originalEvent: e }) };
21012
21156
  }
21013
21157
  }
@@ -22058,7 +22202,7 @@ var ClickZoomHandler = class {
22058
22202
  map.easeTo({
22059
22203
  duration: 300,
22060
22204
  zoom: evaluateZoomSnap(this._tr.zoom + (e.shiftKey ? -1 : 1), map.getZoomSnap()),
22061
- around: this._tr.unproject(point)
22205
+ around: this._tr.transform.isPointOnMapSurface(point) ? this._tr.unproject(point) : void 0
22062
22206
  }, { originalEvent: e });
22063
22207
  } };
22064
22208
  }
@@ -22544,7 +22688,10 @@ var HandlerManager = class {
22544
22688
  return this._el?.ownerDocument?.defaultView || window;
22545
22689
  }
22546
22690
  constructor(map, camera, options) {
22547
- this._terrainGestureAnchorElevation = null;
22691
+ this._terrainGesture = {
22692
+ inFlight: false,
22693
+ anchorElevation: null
22694
+ };
22548
22695
  this.handleWindowEvent = (e) => {
22549
22696
  this.handleEvent(e, `${e.type}Window`);
22550
22697
  };
@@ -22729,7 +22876,7 @@ var HandlerManager = class {
22729
22876
  const touchPitch = map.touchPitch = new TwoFingersTouchPitchHandler(map);
22730
22877
  this._add("touchPitch", touchPitch);
22731
22878
  if (options.interactive && options.touchPitch) map.touchPitch.enable(options.touchPitch);
22732
- const getCenter = () => map.project(map.getCenter());
22879
+ const getCenter = () => this._camera.transform.centerPoint;
22733
22880
  const mouseRotate = generateMouseRotationHandler(options, getCenter);
22734
22881
  const mousePitch = generateMousePitchHandler(options);
22735
22882
  const mouseRoll = generateMouseRollHandler(options, getCenter);
@@ -22833,14 +22980,18 @@ var HandlerManager = class {
22833
22980
  this._updateMapTransform(combined, combinedEventsInProgress, combinedDeactivatedHandlers);
22834
22981
  this._changes = [];
22835
22982
  }
22983
+ /**
22984
+ * Applies a frame's combined handler deltas to the requested camera state and fires the movement
22985
+ * events. A frame without a change only fires the events and takes no requested camera state, so a
22986
+ * trailing frame that merely ends a handler leaves none behind for the next gesture to start from.
22987
+ */
22836
22988
  _updateMapTransform(combinedResult, combinedEventsInProgress, deactivatedHandlers) {
22837
- const map = this._map;
22838
- const tr = this._camera.getTransformForUpdate();
22839
- const terrain = map.terrain;
22840
- if (!hasChange(combinedResult) && !(terrain && this._terrainMovement)) {
22989
+ const terrain = this._map.terrain;
22990
+ if (!hasChange(combinedResult)) {
22841
22991
  this._fireEvents(combinedEventsInProgress, deactivatedHandlers, true);
22842
22992
  return;
22843
22993
  }
22994
+ const tr = this._camera.getTransformForUpdate();
22844
22995
  this._camera.stop(true);
22845
22996
  const { panDelta, zoomDelta, bearingDelta, pitchDelta, rollDelta } = combinedResult;
22846
22997
  let { around, aroundOnSurface } = this._resolveAround(combinedResult, terrain, tr);
@@ -22895,12 +23046,12 @@ var HandlerManager = class {
22895
23046
  */
22896
23047
  _terrainGestureElevation(terrain, around, aroundOnSurface, tr, combinedEventsInProgress) {
22897
23048
  if (!aroundOnSurface) return;
22898
- if (!this._terrainMovement && (combinedEventsInProgress.drag || combinedEventsInProgress.zoom)) {
23049
+ if (this._terrainGesture.anchorElevation === null && (combinedEventsInProgress.drag || combinedEventsInProgress.zoom)) {
22899
23050
  const anchor = tr.screenTerrainPointToMercatorCoordinate(around, terrain);
22900
- this._terrainGestureAnchorElevation = anchor ? anchor.z : null;
23051
+ this._terrainGesture.anchorElevation = anchor ? anchor.z : void 0;
22901
23052
  }
22902
- if (this._terrainGestureAnchorElevation === null) return;
22903
- const elevation = this._terrainGestureAnchorElevation;
23053
+ const elevation = this._terrainGesture.anchorElevation;
23054
+ if (elevation === null || elevation === void 0) return;
22904
23055
  if (around.distSqr(tr.centerPoint) < .01) return;
22905
23056
  if (elevation - tr.elevation >= TERRAIN_ANCHOR_MAX_CAMERA_ALTITUDE_FRACTION * (tr.getCameraAltitude() - tr.elevation)) return;
22906
23057
  return elevation;
@@ -22920,25 +23071,21 @@ var HandlerManager = class {
22920
23071
  _handleMapControls({ terrain, tr, deltasForHelper, preZoomAroundLoc, combinedEventsInProgress, panDelta }) {
22921
23072
  const cameraHelper = this._camera.cameraHelper;
22922
23073
  cameraHelper.handleMapControlsRollPitchBearingZoom(deltasForHelper, tr);
22923
- if (!terrain) {
22924
- cameraHelper.handleMapControlsPan(deltasForHelper, tr, preZoomAroundLoc);
23074
+ if (!terrain && !cameraHelper.useGlobeControls && !tr.isPointOnMapSurface(deltasForHelper.around)) {
23075
+ if (panDelta) tr.setCenter(tr.screenPointToLocation(tr.centerPoint.sub(panDelta)));
22925
23076
  return;
22926
23077
  }
22927
- if (cameraHelper.useGlobeControls) {
22928
- if (!this._terrainMovement && (combinedEventsInProgress.drag || combinedEventsInProgress.zoom)) {
22929
- this._terrainMovement = true;
22930
- this._camera.elevationFreeze = true;
22931
- }
23078
+ if (!terrain) {
22932
23079
  cameraHelper.handleMapControlsPan(deltasForHelper, tr, preZoomAroundLoc);
22933
23080
  return;
22934
23081
  }
22935
- if (!this._terrainMovement && (combinedEventsInProgress.drag || combinedEventsInProgress.zoom)) {
22936
- this._terrainMovement = true;
23082
+ if (!this._terrainGesture.inFlight) {
23083
+ this._terrainGesture.inFlight = true;
22937
23084
  this._camera.elevationFreeze = true;
22938
23085
  cameraHelper.handleMapControlsPan(deltasForHelper, tr, preZoomAroundLoc);
22939
23086
  return;
22940
23087
  }
22941
- if (deltasForHelper.aroundElevation === void 0 && combinedEventsInProgress.drag && this._terrainMovement && panDelta) {
23088
+ if (!cameraHelper.useGlobeControls && deltasForHelper.aroundElevation === void 0 && combinedEventsInProgress.drag && panDelta) {
22942
23089
  tr.setCenter(tr.screenPointToLocation(tr.centerPoint.sub(panDelta)));
22943
23090
  return;
22944
23091
  }
@@ -22973,10 +23120,12 @@ var HandlerManager = class {
22973
23120
  for (const name in endEvents) this._fireEvent(name, endEvents[name]);
22974
23121
  const stillMoving = isMoving(this._eventsInProgress);
22975
23122
  const finishedMoving = (wasMoving || nowMoving) && !stillMoving;
22976
- if (finishedMoving && this._terrainMovement) {
23123
+ if (finishedMoving && this._terrainGesture.inFlight) {
22977
23124
  this._camera.elevationFreeze = false;
22978
- this._terrainMovement = false;
22979
- this._terrainGestureAnchorElevation = null;
23125
+ this._terrainGesture = {
23126
+ inFlight: false,
23127
+ anchorElevation: null
23128
+ };
22980
23129
  const tr = this._camera.getTransformForUpdate();
22981
23130
  if (this._map.getCenterClampedToGround()) tr.recalculateZoomAndCenter(this._map.terrain);
22982
23131
  this._camera.applyUpdatedTransform(tr);
@@ -22990,14 +23139,19 @@ var HandlerManager = class {
22990
23139
  inertialEase.freezeElevation = true;
22991
23140
  this._map.easeTo(inertialEase, { originalEvent: originalEndEvent });
22992
23141
  } else {
22993
- this._map.fire(new MapMovementEvent("moveend", { originalEvent: originalEndEvent }));
23142
+ this._fireEvent("moveend", originalEndEvent);
22994
23143
  if (shouldSnapToNorth(this._map.getBearing())) this._map.resetNorth();
22995
23144
  }
22996
23145
  this._updatingCamera = false;
22997
23146
  }
22998
23147
  }
23148
+ /**
23149
+ * Fires a movement event through the camera, whose evented parent is the map: the map's listeners
23150
+ * see the event as before, and the camera's own `moveend` listener drops the requested camera state,
23151
+ * so the next gesture starts from the rendered transform instead of the state this one ended in.
23152
+ */
22999
23153
  _fireEvent(type, e) {
23000
- this._map.fire(new MapMovementEvent(type, e ? { originalEvent: e } : {}));
23154
+ this._camera.fire(new MapMovementEvent(type, e ? { originalEvent: e } : {}));
23001
23155
  }
23002
23156
  _requestFrame() {
23003
23157
  this._map.triggerRepaint();
@@ -23044,6 +23198,15 @@ var Camera = class extends Evented {
23044
23198
  delete this._requestedCameraState;
23045
23199
  });
23046
23200
  }
23201
+ /**
23202
+ * @internal
23203
+ * Hands the camera the map's terrain, or null when the map has none, and brings the center
23204
+ * elevation up to date with it, see {@link Camera.applyTerrainChange}.
23205
+ */
23206
+ setTerrain(terrain) {
23207
+ this.terrain = terrain;
23208
+ this.applyTerrainChange();
23209
+ }
23047
23210
  migrateProjection(newTransform, newCameraHelper) {
23048
23211
  newTransform.apply(this.transform, true);
23049
23212
  this.transform = newTransform;
@@ -23103,7 +23266,7 @@ var Camera = class extends Evented {
23103
23266
  }
23104
23267
  setVerticalFieldOfView(fov, eventData) {
23105
23268
  if (fov != this.transform.fov) {
23106
- this.transform.setFov(fov);
23269
+ this.applyTransformChange((tr) => tr.setFov(fov));
23107
23270
  this.fire(new MapMovementEvent("movestart", eventData)).fire(new MapMovementEvent("move", eventData)).fire(new MapMovementEvent("moveend", eventData));
23108
23271
  }
23109
23272
  return this;
@@ -23340,14 +23503,15 @@ var Camera = class extends Evented {
23340
23503
  this._zooming ||= easeHandler.isZooming;
23341
23504
  this._easeId = options.easeId;
23342
23505
  this._prepareEase(eventData, options.noMoveStart, currently);
23343
- if (this.terrain) this._prepareElevation(easeHandler.elevationCenter);
23506
+ if (this.terrain) this._prepareElevation(easeHandler.elevationCenter, tr);
23344
23507
  this._ease((k) => {
23345
23508
  easeHandler.easeFunc(k);
23346
- if (this.terrain && !options.freezeElevation) this._updateElevation(k);
23509
+ if (this.terrain && !options.freezeElevation) this._updateElevation(k, tr);
23347
23510
  this.applyUpdatedTransform(tr);
23348
23511
  this._fireMoveEvents(eventData);
23349
23512
  }, (interruptingEaseId) => {
23350
23513
  if (this.terrain && options.freezeElevation) this._finalizeElevation();
23514
+ else this.elevationFreeze = false;
23351
23515
  this._afterEase(eventData, interruptingEaseId);
23352
23516
  }, options);
23353
23517
  return this;
@@ -23360,23 +23524,37 @@ var Camera = class extends Evented {
23360
23524
  if (this._pitching && !currently.pitching) this.fire(new MapMovementEvent("pitchstart", eventData));
23361
23525
  if (this._rolling && !currently.rolling) this.fire(new MapMovementEvent("rollstart", eventData));
23362
23526
  }
23363
- _prepareElevation(center) {
23527
+ /**
23528
+ * @internal
23529
+ * Starts easing the center elevation: records where it stands on the transform the animation edits and
23530
+ * samples the terrain under the map center the animation ends on.
23531
+ * @param center - the map center when the animation ends
23532
+ * @param tr - the transform the animation edits
23533
+ */
23534
+ _prepareElevation(center, tr) {
23364
23535
  this._elevationCenter = center;
23365
- this._elevationStart = this.transform.elevation;
23366
- this._elevationTarget = this.terrain.getElevationForLngLat(center, this.transform);
23536
+ this._elevationStart = tr.elevation;
23537
+ this._elevationTarget = this.terrain.getElevationForLngLat(center, tr);
23367
23538
  this.elevationFreeze = true;
23368
23539
  }
23369
- _updateElevation(k) {
23370
- if (this._elevationStart === void 0 || this._elevationCenter === void 0) this._prepareElevation(this.transform.center);
23371
- this.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this._elevationCenter, this.transform.tileZoom));
23372
- const elevation = this.terrain.getElevationForLngLat(this._elevationCenter, this.transform);
23540
+ /**
23541
+ * @internal
23542
+ * Eases the center elevation towards the terrain under `_elevationCenter`, on the transform the
23543
+ * animation edits, so that `applyUpdatedTransform` carries it to the rendered transform.
23544
+ * @param k - the animation's progress, 0 to 1
23545
+ * @param tr - the transform the animation edits
23546
+ */
23547
+ _updateElevation(k, tr) {
23548
+ if (this._elevationStart === void 0 || this._elevationCenter === void 0) this._prepareElevation(tr.center, tr);
23549
+ tr.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this._elevationCenter, tr.tileZoom));
23550
+ const elevation = this.terrain.getElevationForLngLat(this._elevationCenter, tr);
23373
23551
  if (k < 1 && elevation !== this._elevationTarget) {
23374
23552
  const pitch1 = this._elevationTarget - this._elevationStart;
23375
23553
  const pitch2 = (elevation - (pitch1 * k + this._elevationStart)) / (1 - k);
23376
23554
  this._elevationStart += k * (pitch1 - pitch2);
23377
23555
  this._elevationTarget = elevation;
23378
23556
  }
23379
- this.transform.setElevation(interpolateFactory.number(this._elevationStart, this._elevationTarget, k));
23557
+ tr.setElevation(interpolateFactory.number(this._elevationStart, this._elevationTarget, k));
23380
23558
  }
23381
23559
  _finalizeElevation() {
23382
23560
  this.elevationFreeze = false;
@@ -23384,6 +23562,23 @@ var Camera = class extends Evented {
23384
23562
  }
23385
23563
  /**
23386
23564
  * @internal
23565
+ * Applies a change of the terrain under the center to the transform: the terrain was set or
23566
+ * removed, or a DEM tile landed. The center keeps its place and the camera moves with the
23567
+ * center's elevation, as it does on every rendered frame while nothing holds the elevation.
23568
+ * While a gesture or an ease holds it this does nothing: the camera stays where the user put
23569
+ * it and the hold's end re-solves zoom and center onto the new terrain without moving it.
23570
+ * Nothing is in flight when this writes, so it writes the rendered transform, like the
23571
+ * per-frame clamp; a requested camera state created here would outlive the call and the
23572
+ * next gesture would start from it.
23573
+ */
23574
+ applyTerrainChange() {
23575
+ if (this.elevationFreeze) return;
23576
+ const tr = this.transform;
23577
+ tr.setMinElevationForCurrentTile(this.terrain ? this.terrain.getMinTileElevationForLngLatZoom(tr.center, tr.tileZoom) : 0);
23578
+ if (this.getCenterClampedToGround()) tr.setElevation(this.terrain ? this.terrain.getElevationForLngLat(tr.center, tr) : 0);
23579
+ }
23580
+ /**
23581
+ * @internal
23387
23582
  * Called when the camera is about to be manipulated.
23388
23583
  * If `transformCameraUpdate` is specified or terrain is enabled, a copy of
23389
23584
  * the current transform is created to track the accumulated changes.
@@ -23447,6 +23642,19 @@ var Camera = class extends Evented {
23447
23642
  }
23448
23643
  this.transform.apply(finalTransform, false);
23449
23644
  }
23645
+ /**
23646
+ * @internal
23647
+ * Applies a change that is not itself a movement, such as new bounds, limits or field of view, the way
23648
+ * any camera update is applied. A requested camera state created only for this change is dropped again,
23649
+ * so a later movement cannot restore the old values.
23650
+ */
23651
+ applyTransformChange(change) {
23652
+ const hadRequestedCameraState = this._requestedCameraState !== void 0;
23653
+ const tr = this.getTransformForUpdate();
23654
+ change(tr);
23655
+ this.applyUpdatedTransform(tr);
23656
+ if (!hadRequestedCameraState) delete this._requestedCameraState;
23657
+ }
23450
23658
  _fireMoveEvents(eventData) {
23451
23659
  this.fire(new MapMovementEvent("move", eventData));
23452
23660
  if (this._zooming) this.fire(new MapMovementEvent("zoom", eventData));
@@ -23565,7 +23773,7 @@ var Camera = class extends Evented {
23565
23773
  this._rolling = roll !== startRoll;
23566
23774
  this._padding = !tr.isPaddingEqual(padding);
23567
23775
  this._prepareEase(eventData, false);
23568
- if (this.terrain) this._prepareElevation(flyToHandler.targetCenter);
23776
+ if (this.terrain) this._prepareElevation(flyToHandler.targetCenter, tr);
23569
23777
  this._ease((k) => {
23570
23778
  const s = k * S;
23571
23779
  const scale = 1 / w(s);
@@ -23578,11 +23786,12 @@ var Camera = class extends Evented {
23578
23786
  pointAtOffset = tr.centerPoint.add(offsetAsPoint);
23579
23787
  }
23580
23788
  flyToHandler.easeFunc(k, scale, centerFactor, pointAtOffset);
23581
- if (this.terrain && !options.freezeElevation) this._updateElevation(k);
23789
+ if (this.terrain && !options.freezeElevation) this._updateElevation(k, tr);
23582
23790
  this.applyUpdatedTransform(tr);
23583
23791
  this._fireMoveEvents(eventData);
23584
23792
  }, () => {
23585
23793
  if (this.terrain && options.freezeElevation) this._finalizeElevation();
23794
+ else this.elevationFreeze = false;
23586
23795
  this._afterEase(eventData);
23587
23796
  }, options);
23588
23797
  return this;
@@ -25310,7 +25519,7 @@ var Map$1 = class extends Evented {
25310
25519
  * ```
25311
25520
  */
25312
25521
  setMaxBounds(bounds) {
25313
- this._camera.transform.setMaxBounds(LngLatBounds.convert(bounds));
25522
+ this._camera.applyTransformChange((tr) => tr.setMaxBounds(LngLatBounds.convert(bounds)));
25314
25523
  return this._update();
25315
25524
  }
25316
25525
  /**
@@ -25337,9 +25546,7 @@ var Map$1 = class extends Evented {
25337
25546
  minZoom = minZoom === null || minZoom === void 0 ? defaultMinZoom : minZoom;
25338
25547
  if (minZoom >= defaultMinZoom && minZoom <= this._camera.transform.maxZoom) {
25339
25548
  const zoomBefore = this._camera.transform.zoom;
25340
- const tr = this._camera.getTransformForUpdate();
25341
- tr.setMinZoom(minZoom);
25342
- this._camera.applyUpdatedTransform(tr);
25549
+ this._camera.applyTransformChange((tr) => tr.setMinZoom(minZoom));
25343
25550
  this._update();
25344
25551
  if (zoomBefore !== this._camera.transform.zoom) this.fire(new MapMovementEvent("zoomstart")).fire(new MapMovementEvent("zoom")).fire(new MapMovementEvent("zoomend")).fire(new MapMovementEvent("movestart")).fire(new MapMovementEvent("move")).fire(new MapMovementEvent("moveend"));
25345
25552
  return this;
@@ -25379,9 +25586,7 @@ var Map$1 = class extends Evented {
25379
25586
  maxZoom = maxZoom === null || maxZoom === void 0 ? defaultMaxZoom : maxZoom;
25380
25587
  if (maxZoom >= this._camera.transform.minZoom) {
25381
25588
  const zoomBefore = this._camera.transform.zoom;
25382
- const tr = this._camera.getTransformForUpdate();
25383
- tr.setMaxZoom(maxZoom);
25384
- this._camera.applyUpdatedTransform(tr);
25589
+ this._camera.applyTransformChange((tr) => tr.setMaxZoom(maxZoom));
25385
25590
  this._update();
25386
25591
  if (zoomBefore !== this._camera.transform.zoom) this.fire(new MapMovementEvent("zoomstart")).fire(new MapMovementEvent("zoom")).fire(new MapMovementEvent("zoomend")).fire(new MapMovementEvent("movestart")).fire(new MapMovementEvent("move")).fire(new MapMovementEvent("moveend"));
25387
25592
  return this;
@@ -25415,9 +25620,7 @@ var Map$1 = class extends Evented {
25415
25620
  if (minPitch < defaultMinPitch) throw new Error(`minPitch must be greater than or equal to ${defaultMinPitch}`);
25416
25621
  if (minPitch >= defaultMinPitch && minPitch <= this._camera.transform.maxPitch) {
25417
25622
  const pitchBefore = this._camera.transform.pitch;
25418
- const tr = this._camera.getTransformForUpdate();
25419
- tr.setMinPitch(minPitch);
25420
- this._camera.applyUpdatedTransform(tr);
25623
+ this._camera.applyTransformChange((tr) => tr.setMinPitch(minPitch));
25421
25624
  this._update();
25422
25625
  if (pitchBefore !== this._camera.transform.pitch) this.fire(new MapMovementEvent("pitchstart")).fire(new MapMovementEvent("pitch")).fire(new MapMovementEvent("pitchend")).fire(new MapMovementEvent("movestart")).fire(new MapMovementEvent("move")).fire(new MapMovementEvent("moveend"));
25423
25626
  return this;
@@ -25447,9 +25650,7 @@ var Map$1 = class extends Evented {
25447
25650
  if (maxPitch > maxPitchThreshold) throw new Error(`maxPitch must be less than or equal to ${maxPitchThreshold}`);
25448
25651
  if (maxPitch >= this._camera.transform.minPitch) {
25449
25652
  const pitchBefore = this._camera.transform.pitch;
25450
- const tr = this._camera.getTransformForUpdate();
25451
- tr.setMaxPitch(maxPitch);
25452
- this._camera.applyUpdatedTransform(tr);
25653
+ this._camera.applyTransformChange((tr) => tr.setMaxPitch(maxPitch));
25453
25654
  this._update();
25454
25655
  if (pitchBefore !== this._camera.transform.pitch) this.fire(new MapMovementEvent("pitchstart")).fire(new MapMovementEvent("pitch")).fire(new MapMovementEvent("pitchend")).fire(new MapMovementEvent("movestart")).fire(new MapMovementEvent("move")).fire(new MapMovementEvent("moveend"));
25455
25656
  return this;
@@ -25532,7 +25733,7 @@ var Map$1 = class extends Evented {
25532
25733
  * @see [Render world copies](https://maplibre.org/maplibre-gl-js/docs/examples/render-world-copies/)
25533
25734
  */
25534
25735
  setRenderWorldCopies(renderWorldCopies) {
25535
- this._camera.transform.setRenderWorldCopies(renderWorldCopies);
25736
+ this._camera.applyTransformChange((tr) => tr.setRenderWorldCopies(renderWorldCopies));
25536
25737
  return this._update();
25537
25738
  }
25538
25739
  /** Sets or clears the callback overriding how the map constrains the viewport's lnglat and zoom to respect the longitude and latitude bounds.
@@ -25550,7 +25751,7 @@ var Map$1 = class extends Evented {
25550
25751
  * @see [Customize the map transform constrain](https://maplibre.org/maplibre-gl-js/docs/examples/customize-the-map-transform-constrain/)
25551
25752
  */
25552
25753
  setTransformConstrain(constrain) {
25553
- this._camera.transform.setConstrainOverride(constrain);
25754
+ this._camera.applyTransformChange((tr) => tr.setConstrainOverride(constrain));
25554
25755
  return this._update();
25555
25756
  }
25556
25757
  /**
@@ -25622,7 +25823,7 @@ var Map$1 = class extends Evented {
25622
25823
  isRotating() {
25623
25824
  return this._camera.isRotating() || this._handlers?.isRotating() || false;
25624
25825
  }
25625
- _createDelegatedListener(type, layerIds, listener) {
25826
+ _createDelegates(type, layerIds, listener) {
25626
25827
  if (type === "mouseenter" || type === "mouseover") {
25627
25828
  let mousein = false;
25628
25829
  const mousemove = (e) => {
@@ -25638,12 +25839,8 @@ var Map$1 = class extends Evented {
25638
25839
  mousein = false;
25639
25840
  };
25640
25841
  return {
25641
- layers: layerIds,
25642
- listener,
25643
- delegates: {
25644
- mousemove,
25645
- mouseout
25646
- }
25842
+ mousemove,
25843
+ mouseout
25647
25844
  };
25648
25845
  } else if (type === "mouseleave" || type === "mouseout") {
25649
25846
  let mousein = false;
@@ -25662,12 +25859,8 @@ var Map$1 = class extends Evented {
25662
25859
  }
25663
25860
  };
25664
25861
  return {
25665
- layers: layerIds,
25666
- listener,
25667
- delegates: {
25668
- mousemove,
25669
- mouseout
25670
- }
25862
+ mousemove,
25863
+ mouseout
25671
25864
  };
25672
25865
  } else {
25673
25866
  const delegate = (e) => {
@@ -25679,17 +25872,14 @@ var Map$1 = class extends Evented {
25679
25872
  delete e.features;
25680
25873
  }
25681
25874
  };
25682
- return {
25683
- layers: layerIds,
25684
- listener,
25685
- delegates: { [type]: delegate }
25686
- };
25875
+ return { [type]: delegate };
25687
25876
  }
25688
25877
  }
25689
- _saveDelegatedListener(type, delegatedListener) {
25878
+ _addDelegatedListener(type, delegatedListener) {
25690
25879
  this._delegatedListeners ||= {};
25691
25880
  this._delegatedListeners[type] ||= [];
25692
25881
  this._delegatedListeners[type].push(delegatedListener);
25882
+ for (const event in delegatedListener.delegates) this.on(event, delegatedListener.delegates[event]);
25693
25883
  }
25694
25884
  _removeDelegatedListener(type, layerIds, listener) {
25695
25885
  if (!this._delegatedListeners?.[type]) return;
@@ -25706,9 +25896,12 @@ var Map$1 = class extends Evented {
25706
25896
  on(type, layerIdsOrListener, listener) {
25707
25897
  if (listener === void 0) return super.on(type, layerIdsOrListener);
25708
25898
  const layerIds = typeof layerIdsOrListener === "string" ? [layerIdsOrListener] : layerIdsOrListener;
25709
- const delegatedListener = this._createDelegatedListener(type, layerIds, listener);
25710
- this._saveDelegatedListener(type, delegatedListener);
25711
- for (const event in delegatedListener.delegates) this.on(event, delegatedListener.delegates[event]);
25899
+ const delegatedListener = {
25900
+ layers: layerIds,
25901
+ listener,
25902
+ delegates: this._createDelegates(type, layerIds, listener)
25903
+ };
25904
+ this._addDelegatedListener(type, delegatedListener);
25712
25905
  return { unsubscribe: () => {
25713
25906
  this._removeDelegatedListener(type, layerIds, listener);
25714
25907
  } };
@@ -25716,16 +25909,15 @@ var Map$1 = class extends Evented {
25716
25909
  once(type, layerIdsOrListener, listener) {
25717
25910
  if (listener === void 0) return super.once(type, layerIdsOrListener);
25718
25911
  const layerIds = typeof layerIdsOrListener === "string" ? [layerIdsOrListener] : layerIdsOrListener;
25719
- const delegatedListener = this._createDelegatedListener(type, layerIds, listener);
25720
- for (const key in delegatedListener.delegates) {
25721
- const delegate = delegatedListener.delegates[key];
25722
- delegatedListener.delegates[key] = (...args) => {
25912
+ const delegatedListener = {
25913
+ layers: layerIds,
25914
+ listener,
25915
+ delegates: this._createDelegates(type, layerIds, (e) => {
25723
25916
  this._removeDelegatedListener(type, layerIds, listener);
25724
- delegate(...args);
25725
- };
25726
- }
25727
- this._saveDelegatedListener(type, delegatedListener);
25728
- for (const event in delegatedListener.delegates) this.once(event, delegatedListener.delegates[event]);
25917
+ listener.call(this, e);
25918
+ })
25919
+ };
25920
+ this._addDelegatedListener(type, delegatedListener);
25729
25921
  return this;
25730
25922
  }
25731
25923
  off(type, layerIdsOrListener, listener) {
@@ -26155,9 +26347,7 @@ var Map$1 = class extends Evented {
26155
26347
  this.terrain = null;
26156
26348
  this.painter.renderToTexture = null;
26157
26349
  this.painter.destroyRTTResources();
26158
- this._camera.terrain = null;
26159
- this._camera.transform.setMinElevationForCurrentTile(0);
26160
- if (this.getCenterClampedToGround()) this._camera.transform.setElevation(0);
26350
+ this._camera.setTerrain(null);
26161
26351
  } else {
26162
26352
  const tileManager = this.style.tileManagers[options.source];
26163
26353
  if (!tileManager) throw new Error(`cannot load terrain, because there exists no source with ID: ${options.source}`);
@@ -26170,9 +26360,7 @@ var Map$1 = class extends Evented {
26170
26360
  if (this.terrain) this.terrain.destroy();
26171
26361
  this.terrain = new Terrain(this.painter, tileManager, options, this._terrainSkirtLength);
26172
26362
  this.painter.renderToTexture = new RenderToTexture(this.painter, this.terrain);
26173
- this._camera.terrain = this.terrain;
26174
- this._camera.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this._camera.transform.center, this._camera.transform.tileZoom));
26175
- this._camera.transform.setElevation(this.terrain.getElevationForLngLat(this._camera.transform.center, this._camera.transform));
26363
+ this._camera.setTerrain(this.terrain);
26176
26364
  this._terrainDataCallback = (e) => this._handleTerrainDataEvent(e, options.source);
26177
26365
  this.style.on("data", this._terrainDataCallback);
26178
26366
  }
@@ -26190,10 +26378,9 @@ var Map$1 = class extends Evented {
26190
26378
  this.terrain.resetElevationCache();
26191
26379
  this.style.triggerSymbolPlacement();
26192
26380
  }
26193
- if (isTerrainSourceEvent && event.tile) this.painter.markTerrainDepthDirty();
26194
- if (isTerrainSourceEvent && event.tile && !this._camera.elevationFreeze) {
26195
- this._camera.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this._camera.transform.center, this._camera.transform.tileZoom));
26196
- if (this.getCenterClampedToGround()) this._camera.transform.setElevation(this.terrain.getElevationForLngLat(this._camera.transform.center, this._camera.transform));
26381
+ if (isTerrainSourceEvent && event.tile) {
26382
+ this.painter.markTerrainDepthDirty();
26383
+ this._camera.applyTerrainChange();
26197
26384
  }
26198
26385
  if (!event.tile) return;
26199
26386
  if (event.source?.type === "image") {
@@ -27150,12 +27337,16 @@ var Map$1 = class extends Evented {
27150
27337
  getCanvas() {
27151
27338
  return this._canvas;
27152
27339
  }
27340
+ /**
27341
+ * @internal
27342
+ * The viewport in whole CSS pixels, shared by the canvas, the painter and the transform.
27343
+ */
27153
27344
  _containerDimensions() {
27154
27345
  let width = 0;
27155
27346
  let height = 0;
27156
27347
  if (this._container) {
27157
- width = this._container.clientWidth || 400;
27158
- height = this._container.clientHeight || 300;
27348
+ width = Math.floor(this._container.clientWidth) || 400;
27349
+ height = Math.floor(this._container.clientHeight) || 300;
27159
27350
  }
27160
27351
  return [width, height];
27161
27352
  }
@@ -27243,11 +27434,19 @@ var Map$1 = class extends Evented {
27243
27434
  this._container.removeEventListener("scroll", this._onMapScroll, false);
27244
27435
  this._container.classList.remove("maplibregl-map");
27245
27436
  }
27437
+ /**
27438
+ * @internal
27439
+ * Sizes the backing store to whole device pixels and the CSS box to cover exactly that many, so
27440
+ * the compositor never rescales the canvas. A pixel ratio can sit a fraction below a whole
27441
+ * number, where truncating the count would cost a whole pixel per axis.
27442
+ */
27246
27443
  _resizeCanvas(width, height, pixelRatio) {
27247
- this._canvas.width = Math.floor(pixelRatio * width);
27248
- this._canvas.height = Math.floor(pixelRatio * height);
27249
- this._canvas.style.width = `${width}px`;
27250
- this._canvas.style.height = `${height}px`;
27444
+ const canvasWidth = Math.round(pixelRatio * width);
27445
+ const canvasHeight = Math.round(pixelRatio * height);
27446
+ this._canvas.width = canvasWidth;
27447
+ this._canvas.height = canvasHeight;
27448
+ this._canvas.style.width = `${canvasWidth / pixelRatio}px`;
27449
+ this._canvas.style.height = `${canvasHeight / pixelRatio}px`;
27251
27450
  }
27252
27451
  /**
27253
27452
  * @internal