maplibre-gl 6.6.0 → 6.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/generate-unicode-data.ts +83 -0
- package/dist/maplibre-gl-dev.mjs +480 -80
- package/dist/maplibre-gl-dev.mjs.map +1 -1
- package/dist/maplibre-gl-shared-dev.mjs +1578 -983
- package/dist/maplibre-gl-shared-dev.mjs.map +1 -1
- package/dist/maplibre-gl-shared.mjs +2 -2
- package/dist/maplibre-gl-shared.mjs.map +1 -1
- package/dist/maplibre-gl-worker-dev.mjs +7 -6
- package/dist/maplibre-gl-worker-dev.mjs.map +1 -1
- package/dist/maplibre-gl-worker.mjs +2 -2
- package/dist/maplibre-gl-worker.mjs.map +1 -1
- package/dist/maplibre-gl.d.ts +306 -66
- package/dist/maplibre-gl.mjs +36 -36
- package/dist/maplibre-gl.mjs.map +1 -1
- package/package.json +23 -18
- package/src/data/bucket/symbol_bucket.test.ts +45 -12
- package/src/data/bucket/symbol_bucket.ts +39 -18
- package/src/data/bucket.ts +11 -3
- package/src/data/feature_index.ts +35 -6
- package/src/geo/projection/mercator_transform.test.ts +24 -4
- package/src/geo/projection/mercator_transform.ts +1 -1
- package/src/geo/projection/vertical_perspective_transform.ts +1 -1
- package/src/render/font_face_manager.test.ts +295 -0
- package/src/render/font_face_manager.ts +249 -0
- package/src/render/glyph_atlas.ts +3 -7
- package/src/render/glyph_manager.test.ts +251 -94
- package/src/render/glyph_manager.ts +195 -52
- package/src/render/terrain.test.ts +36 -0
- package/src/render/terrain.ts +17 -7
- package/src/source/geojson_worker_source.test.ts +4 -53
- package/src/source/image_source.test.ts +53 -0
- package/src/source/image_source.ts +24 -2
- package/src/source/vector_tile_worker_source.test.ts +46 -38
- package/src/source/vector_tile_worker_source.ts +6 -1
- package/src/source/worker_source.ts +1 -5
- package/src/source/worker_tile.test.ts +6 -22
- package/src/source/worker_tile.ts +8 -5
- package/src/style/style.test.ts +92 -0
- package/src/style/style.ts +67 -3
- package/src/style/style_image.ts +5 -1
- package/src/style/style_layer/symbol_style_layer.ts +7 -7
- package/src/style/style_layer/symbol_style_layer_properties.g.ts +3 -3
- package/src/symbol/shaping.test.ts +436 -2
- package/src/symbol/shaping.ts +298 -113
- package/src/symbol/symbol_layout.ts +1 -1
- package/src/symbol/tagged_string.test.ts +29 -6
- package/src/symbol/tagged_string.ts +151 -89
- package/src/tile/terrain_tile_manager.test.ts +23 -0
- package/src/tile/terrain_tile_manager.ts +6 -1
- package/src/ui/camera.test.ts +6 -5
- package/src/ui/camera.ts +3 -3
- package/src/ui/control/navigation_control.test.ts +32 -0
- package/src/ui/control/navigation_control.ts +3 -3
- package/src/ui/map.ts +87 -21
- package/src/ui/map_tests/map_render.test.ts +139 -1
- package/src/ui/map_tests/map_style.test.ts +17 -0
- package/src/ui/map_tests/map_terrain.test.ts +60 -1
- package/src/ui/map_tests/map_webgl.test.ts +41 -11
- package/src/ui/map_tests/map_zoom.test.ts +11 -0
- package/src/util/actor_messages.ts +3 -6
- package/src/util/gpu_initialization_error.ts +2 -1
- package/src/util/graphemes.test.ts +79 -0
- package/src/util/graphemes.ts +79 -0
- package/src/util/test/util.ts +46 -1
- package/src/util/unicode_properties.g.ts +28 -0
- package/src/util/util.ts +6 -2
- package/src/webgl/draw/draw_symbol.ts +1 -1
- package/src/style/load_glyph_range.test.ts +0 -74
- package/src/style/load_glyph_range.ts +0 -32
package/dist/maplibre-gl-dev.mjs
CHANGED
|
@@ -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.
|
|
3
|
+
* @license 3-Clause BSD. Full text of license: https://github.com/maplibre/maplibre-gl-js/blob/v6.7.0/LICENSE.txt
|
|
4
4
|
*/
|
|
5
|
-
import { $ as
|
|
5
|
+
import { $ as maxMercatorHorizonAngle, $n as createVec3f64, $r as multiply$1, $t as DataConstantProperty, A as evaluateSizeForFeature, Ai as multiply, An as getJSON, Ar as rayPlaneIntersection, At as isCircleStyleLayer, Bi as create$2, Bn as isAbortError, Br as uniqueId, Bt as UniformFloatArray, C as isCustomStyleLayer, Ci as copy, Cn as latest, Cr as mapObject, Ct as Texture, D as SymbolBucket, Di as fromScaling, Dn as AJAXError, Dr as pick, Dt as renderColorRamp, E as isSymbolStyleLayer, Ei as exactEquals, En as Evented, Er as parseCacheControl, Et as isHeatmapStyleLayer, F as ImagePosition, Fi as rotateZ, Fn as addProtocol, Fr as scaleZoom, Ft as Uniform2f, Gi as offscreenCanvasSupported, Gn as arrayBufferToImage, Gr as EXTENT, Gt as LineStripIndexArray, H as collisionCircleLayout, Hi as invert, Hr as wrap, Ht as SegmentVector, I as potpack, Ii as scale, In as getProtocol, Ir as sphericalToCartesian, It as Uniform3f, J as calculateTileMatrix, Jn as clamp, Jr as scale$1, Jt as QuadTriangleArray, K as isFillExtrusionStyleLayer, Ki as Point, Kn as arrayBufferToImageBitmap, Kr as create$3, Kt as Pos3dArray, L as isStyleImageWebGLData, Li as translate, Ln as removeProtocol, Lr as subscribe, Lt as Uniform4f, Mi as perspective, Mn as getVideo, Mr as remapSaturate, Mt as toEvaluationFeature, N as getAnchorAlignment, Ni as rotateX, Nn as makeRequest, Nr as rollPitchBearingEqual, Nt as Uniform1f, O as addDynamicAttributes, Oi as identity, On as GLOBAL_DISPATCHER_ID, Or as pointPlaneSignedDistance, Ot as AlphaImage, Pi as rotateY$1, Pn as sameOrigin, Pr as rollPitchBearingToQuat, Pt as Uniform1i, Q as getMercatorHorizon, Qn as createMat4f64, Qr as fromValues, Qt as isRasterStyleLayer, R as renderStyleImage, Ri as create$4, Rn as config, Rr as threePlaneIntersection, Rt as UniformColor, S as createStyleLayer, Si as clone$1, Sn as derefLayers, Sr as lerp, T as isBackgroundStyleLayer, Ti as equals, Tn as Event, Tr as nextPowerOfTwo, Tt as HEATMAP_FULL_RENDER_FBO_KEY, U as isLineStyleLayer, Ui as rotate, Un as MAX_VALID_LATITUDE, Ur as zoomScale, Ut as CollisionBoxArray, V as isCluster, Vi as determinant, Vn as throwIfAborted, Vr as warnOnce, Vt as UniformMatrix4f, Wi as isOffscreenCanvasDistorted, Wn as angleToRotateBetweenVectors2D, Wr as pixelsToTileUnits, Wt as CollisionCircleLayoutArray, X as cameraMercatorCoordinate, Xn as createIdentityMat4f32, Xr as zero, Xt as TriangleIndexArray, Y as cameraDirectionFromPitchBearing, Yn as clone$2, Yr as sqrLen, Yt as RasterBoundsArray, Z as cameraMercatorCoordinateFromCenterAndRotation, Zn as createIdentityMat4f64, Zr as fromEuler, Zt as createLayout, _ as UnwrappedTileID, _i as sub, _n as interpolateFactory, _r as isPointableEvent, _t as NORTH_POLE_Y, a as clipLine, ai as clone, an as codePointUsesLocalIdeographFontFamily, ar as distanceOfAnglesRadians, at as latFromMercatorY, b as isInBoundsForZoomLngLat, bi as transformQuat, bn as ValidationError, br as isTouchableOrPointableType, bt as SubdivisionGranularitySetting, ci as len, cn as SPEC_SOURCE_TYPES, cr as extend, ct as mercatorYfromLat, di as normalize, dn as validateStyle, dr as getAABB, dt as earthRadius, ei as slerp, en as Properties, er as createVec4f64, et as projectToWorldCoordinates, f as GEOJSON_TILE_LAYER_NAME, fi as rotateX$1, fn as validateStyleAndEmit, fr as getAngleDelta, g as OverscaledTileID, gi as scaleAndAdd, gr as isImageBitmap, h as CanonicalTileID, hi as scale$2, hn as featureFilter, hr as getRollPitchBearing, ht as isFillStyleLayer, ii as add, ir as differenceOfAnglesDegrees, it as altitudeFromMercatorZ, j as evaluateSizeForZoom, ji as ortho, jn as getReferrer, jr as readImageUsingVideoFrame, jt as polygonIntersectsPolygon, k as getOverlapMode, ki as invert$1, kn as getArrayBuffer, kr as radiansToDegrees, kt as RGBAImage, li as length$1, ln as emitValidationErrors, lr as filterObject, lt as mercatorZfromAltitude, mi as rotateZ$1, mt as Bounds, ni as scale$3, nn as Transitionable, nr as defaultEasing, nt as unprojectFromWorldCoordinates, oi as cross, on as ZoomHistory, or as ensureError, ot as lngFromMercatorX, pi as rotateY, pn as emptyStyle, pr as getEdgeTiles, pt as EXTENT_BOUNDS, qn as bezier, qr as length, qt as PosArray, r as TextAnchorEnum, ri as transformMat4, rn as EvaluationParameters, rr as degreesToRadians, rt as MercatorCoordinate, s as TileCache, si as dot, sr as evaluateZoomSnap, st as mercatorXfromLng, t as getAnchorJustification, ti as mul, tn as TRANSITION_SUFFIX, tr as deepEqual, tt as tileCoordinatesToMercatorCoordinates, u as GeoJSONFeature, ui as negate, un as validateAndEmit, ur as findLineIntersection, ut as LngLat, v as calculateTileKey, vi as transformMat3, vn as ProjectionDefinition, vr as isSafari, vt as SOUTH_POLE_Y, w as validateCustomStyleLayer, wi as create$1, wn as ErrorEvent, wr as mod, wt as isHillshadeStyleLayer, x as Actor, xi as zero$1, xn as diff, xr as isWorker, xt as isColorReliefStyleLayer, y as compareTileId, yi as transformMat4$1, yn as Color, yr as isTouchableEvent, yt as SubdivisionGranularityExpression, z as parseGlyphPbf, zi as fromRotation, zn as AbortError, zr as translatePosition, zt as UniformColorArray } from "./maplibre-gl-shared-dev.mjs";
|
|
6
6
|
//#region package.json
|
|
7
|
-
var version$2 = "6.
|
|
7
|
+
var version$2 = "6.7.0";
|
|
8
8
|
//#endregion
|
|
9
9
|
//#region src/util/browser.ts
|
|
10
10
|
let linkEl;
|
|
@@ -1165,17 +1165,183 @@ var PatternAtlas = class {
|
|
|
1165
1165
|
}
|
|
1166
1166
|
};
|
|
1167
1167
|
//#endregion
|
|
1168
|
-
//#region src/
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1168
|
+
//#region src/render/font_face_manager.ts
|
|
1169
|
+
const MAX_CODE_POINT = 1114111;
|
|
1170
|
+
/**
|
|
1171
|
+
* What a font file covers when the style does not narrow it down with `unicode-range`.
|
|
1172
|
+
*/
|
|
1173
|
+
const DEFAULT_UNICODE_RANGE = {
|
|
1174
|
+
start: 0,
|
|
1175
|
+
end: MAX_CODE_POINT
|
|
1176
|
+
};
|
|
1177
|
+
/**
|
|
1178
|
+
* Makes the family names unique across every map on the page, since `document.fonts` is shared.
|
|
1179
|
+
*/
|
|
1180
|
+
let nextFamilyId = 0;
|
|
1181
|
+
/**
|
|
1182
|
+
* Parses one `unicode-range` entry, in the CSS grammar the style specification borrows: `U+A5`,
|
|
1183
|
+
* `U+0-10FFFF` or `U+4??`.
|
|
1184
|
+
* @param value - a single `unicode-range` entry
|
|
1185
|
+
* @returns the codepoints it covers, or `null` if it is not a range this can make sense of
|
|
1186
|
+
*/
|
|
1187
|
+
function parseUnicodeRange(value) {
|
|
1188
|
+
const wildcard = /^u\+([0-9a-f]*)(\?+)$/i.exec(value);
|
|
1189
|
+
if (wildcard) {
|
|
1190
|
+
const [, prefix, questionMarks] = wildcard;
|
|
1191
|
+
if (prefix.length + questionMarks.length > 6) return null;
|
|
1192
|
+
return clampRange(parseInt(`${prefix}${"0".repeat(questionMarks.length)}`, 16), parseInt(`${prefix}${"f".repeat(questionMarks.length)}`, 16));
|
|
1193
|
+
}
|
|
1194
|
+
const explicit = /^u\+([0-9a-f]{1,6})(?:-([0-9a-f]{1,6}))?$/i.exec(value);
|
|
1195
|
+
if (!explicit) return null;
|
|
1196
|
+
const start = parseInt(explicit[1], 16);
|
|
1197
|
+
return clampRange(start, explicit[2] === void 0 ? start : parseInt(explicit[2], 16));
|
|
1198
|
+
}
|
|
1199
|
+
function clampRange(start, end) {
|
|
1200
|
+
if (start > end || start > MAX_CODE_POINT) return null;
|
|
1201
|
+
return {
|
|
1202
|
+
start,
|
|
1203
|
+
end: Math.min(end, MAX_CODE_POINT)
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
1206
|
+
/**
|
|
1207
|
+
* Whether the style lets this file cover the given codepoint.
|
|
1208
|
+
*/
|
|
1209
|
+
function covers(face, codePoint) {
|
|
1210
|
+
return face.ranges.some(({ start, end }) => codePoint >= start && codePoint <= end);
|
|
1178
1211
|
}
|
|
1212
|
+
/**
|
|
1213
|
+
* Keeps the font files a style declares in its
|
|
1214
|
+
* [`font-faces`](https://maplibre.org/maplibre-style-spec/root/#font-faces) property, and hands the
|
|
1215
|
+
* {@link GlyphManager} the CSS family to draw a given codepoint with.
|
|
1216
|
+
*
|
|
1217
|
+
* The files go to the browser's CSS Font Loading API, so any format it can render text with works.
|
|
1218
|
+
* Each is registered under a family name of our own making, so that a style cannot restyle the
|
|
1219
|
+
* surrounding page and a codepoint can be pinned to one file rather than to whatever font matching
|
|
1220
|
+
* picks.
|
|
1221
|
+
*
|
|
1222
|
+
* Rasterizing happens a grapheme cluster at a time, so a letter reaches the browser's text engine
|
|
1223
|
+
* with its marks. Shaping across cluster boundaries still needs the RTL text plugin.
|
|
1224
|
+
*/
|
|
1225
|
+
var FontFaceManager = class {
|
|
1226
|
+
constructor(requestManager) {
|
|
1227
|
+
this.requestManager = requestManager;
|
|
1228
|
+
this._faces = {};
|
|
1229
|
+
this._registered = /* @__PURE__ */ new Set();
|
|
1230
|
+
}
|
|
1231
|
+
/**
|
|
1232
|
+
* Replaces the declared font faces. Nothing is downloaded here: each file waits until a
|
|
1233
|
+
* codepoint it covers is actually drawn.
|
|
1234
|
+
*/
|
|
1235
|
+
setFontFaces(fontFaces) {
|
|
1236
|
+
this._unregisterAll();
|
|
1237
|
+
this._faces = {};
|
|
1238
|
+
for (const [fontName, declaration] of Object.entries(fontFaces ?? {})) {
|
|
1239
|
+
const declarations = Array.isArray(declaration) ? declaration : [declaration];
|
|
1240
|
+
this._faces[fontName] = declarations.map((entry) => this._declareFontFace(fontName, entry)).filter((face) => face !== null);
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
/**
|
|
1244
|
+
* Whether the style declared any font face at all, so that the common case of a style without
|
|
1245
|
+
* `font-faces` costs nothing.
|
|
1246
|
+
*/
|
|
1247
|
+
hasFontFaces() {
|
|
1248
|
+
return Object.keys(this._faces).length > 0;
|
|
1249
|
+
}
|
|
1250
|
+
/**
|
|
1251
|
+
* Finds the font file to draw a codepoint with: each name of the `text-font` stack in turn, and
|
|
1252
|
+
* within a name each declared file, until one covers it. A file that fails to load is skipped,
|
|
1253
|
+
* the specification asking for unsupported fonts to be ignored.
|
|
1254
|
+
*
|
|
1255
|
+
* @returns the CSS family to draw with, or `null` to leave the codepoint to the `glyphs` URL
|
|
1256
|
+
*/
|
|
1257
|
+
async getFontFamily(fontStack, codePoint) {
|
|
1258
|
+
for (const fontName of fontStack.split(",")) for (const face of this._faces[fontName.trim()] ?? []) {
|
|
1259
|
+
if (!covers(face, codePoint)) continue;
|
|
1260
|
+
face.loaded ??= this._loadFontFace(face);
|
|
1261
|
+
if (await face.loaded) return face.family;
|
|
1262
|
+
}
|
|
1263
|
+
return null;
|
|
1264
|
+
}
|
|
1265
|
+
/**
|
|
1266
|
+
* Turns one declaration into a face to draw with, or `null` if there is nothing usable in it.
|
|
1267
|
+
*
|
|
1268
|
+
* A bare URL is the same as a face that names no `unicode-range`: it covers every codepoint.
|
|
1269
|
+
*/
|
|
1270
|
+
_declareFontFace(fontName, declaration) {
|
|
1271
|
+
const face = typeof declaration === "string" ? { url: declaration } : declaration;
|
|
1272
|
+
if (typeof face?.url !== "string") {
|
|
1273
|
+
warnOnce(`Ignoring the font face declared for "${fontName}": it has no URL.`);
|
|
1274
|
+
return null;
|
|
1275
|
+
}
|
|
1276
|
+
const family = `maplibre-gl-font-face-${nextFamilyId++}`;
|
|
1277
|
+
const unicodeRange = face["unicode-range"];
|
|
1278
|
+
if (!unicodeRange?.length) return {
|
|
1279
|
+
url: face.url,
|
|
1280
|
+
ranges: [DEFAULT_UNICODE_RANGE],
|
|
1281
|
+
family
|
|
1282
|
+
};
|
|
1283
|
+
const ranges = [];
|
|
1284
|
+
for (const entry of unicodeRange) {
|
|
1285
|
+
const range = parseUnicodeRange(entry);
|
|
1286
|
+
if (!range) {
|
|
1287
|
+
warnOnce(`Ignoring the unicode range "${entry}" of the font face at ${face.url}: it is not a valid range.`);
|
|
1288
|
+
continue;
|
|
1289
|
+
}
|
|
1290
|
+
ranges.push(range);
|
|
1291
|
+
}
|
|
1292
|
+
if (!ranges.length) return null;
|
|
1293
|
+
return {
|
|
1294
|
+
url: face.url,
|
|
1295
|
+
ranges,
|
|
1296
|
+
family
|
|
1297
|
+
};
|
|
1298
|
+
}
|
|
1299
|
+
/**
|
|
1300
|
+
* Downloads a declared file and hands it to the browser, reporting whether it can be drawn with.
|
|
1301
|
+
* A failure is not an error: its codepoints fall through to the next file, then to `glyphs`.
|
|
1302
|
+
*/
|
|
1303
|
+
async _loadFontFace(face) {
|
|
1304
|
+
if (typeof FontFace === "undefined" || typeof document === "undefined" || !document.fonts) {
|
|
1305
|
+
warnOnce(`Ignoring the font face at ${face.url}: this environment has no CSS Font Loading API.`);
|
|
1306
|
+
return false;
|
|
1307
|
+
}
|
|
1308
|
+
let fontFace;
|
|
1309
|
+
try {
|
|
1310
|
+
fontFace = new FontFace(face.family, await this._downloadFontFile(face.url));
|
|
1311
|
+
if (!Object.values(this._faces).some((faces) => faces.includes(face))) return false;
|
|
1312
|
+
document.fonts.add(fontFace);
|
|
1313
|
+
this._registered.add(fontFace);
|
|
1314
|
+
await fontFace.load();
|
|
1315
|
+
return true;
|
|
1316
|
+
} catch (e) {
|
|
1317
|
+
if (fontFace) this._unregister(fontFace);
|
|
1318
|
+
warnOnce(`Ignoring the font face at ${face.url}: ${ensureError(e).message}`);
|
|
1319
|
+
return false;
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
/**
|
|
1323
|
+
* Downloads a font file, giving the map's `transformRequest` a chance to rewrite the request the
|
|
1324
|
+
* same way it gets one for a glyph range.
|
|
1325
|
+
*/
|
|
1326
|
+
async _downloadFontFile(url) {
|
|
1327
|
+
const request = await this.requestManager.transformRequest(url, "Glyphs");
|
|
1328
|
+
const response = await getArrayBuffer(request, new AbortController());
|
|
1329
|
+
if (!response?.data) throw new Error(`the response was empty for the font file at ${url}`);
|
|
1330
|
+
return response.data;
|
|
1331
|
+
}
|
|
1332
|
+
_unregister(fontFace) {
|
|
1333
|
+
document.fonts?.delete(fontFace);
|
|
1334
|
+
this._registered.delete(fontFace);
|
|
1335
|
+
}
|
|
1336
|
+
_unregisterAll() {
|
|
1337
|
+
for (const fontFace of this._registered) document.fonts?.delete(fontFace);
|
|
1338
|
+
this._registered.clear();
|
|
1339
|
+
}
|
|
1340
|
+
destroy() {
|
|
1341
|
+
this._unregisterAll();
|
|
1342
|
+
this._faces = {};
|
|
1343
|
+
}
|
|
1344
|
+
};
|
|
1179
1345
|
//#endregion
|
|
1180
1346
|
//#region node_modules/@mapbox/tiny-sdf/index.js
|
|
1181
1347
|
const INF = 0x56bc75e2d63100000;
|
|
@@ -1305,22 +1471,38 @@ const defaultGenericFontFamily = "sans-serif";
|
|
|
1305
1471
|
* Client-generated glyphs are rendered at 2× because CJK glyphs are more detailed than others.
|
|
1306
1472
|
*/
|
|
1307
1473
|
const textureScale = 2;
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1474
|
+
const defaultCreateRasterizer = (options, padding) => {
|
|
1475
|
+
const tinySDF = new TinySDF(options);
|
|
1476
|
+
tinySDF.buffer = padding;
|
|
1477
|
+
return tinySDF;
|
|
1478
|
+
};
|
|
1479
|
+
/**
|
|
1480
|
+
* How wide a grapheme cluster may be drawn, in multiples of the font size.
|
|
1481
|
+
*
|
|
1482
|
+
* TinySDF sizes its canvas for a single character and cuts off the rest, but a cluster is a whole
|
|
1483
|
+
* syllable: Burmese `လား` is nearly twice the font size. Three ems fits the ones that occur.
|
|
1484
|
+
*/
|
|
1485
|
+
const clusterEmsWide = 3;
|
|
1486
|
+
var GlyphManager = class {
|
|
1487
|
+
constructor(requestManager, localIdeographFontFamily, lang, createRasterizer = defaultCreateRasterizer) {
|
|
1316
1488
|
this.requestManager = requestManager;
|
|
1317
1489
|
this.localIdeographFontFamily = localIdeographFontFamily;
|
|
1318
1490
|
this.entries = {};
|
|
1319
1491
|
this.lang = lang;
|
|
1492
|
+
this.fontFaceManager = new FontFaceManager(requestManager);
|
|
1493
|
+
this.createRasterizer = createRasterizer;
|
|
1320
1494
|
}
|
|
1321
1495
|
setURL(url) {
|
|
1322
1496
|
this.url = url;
|
|
1323
1497
|
}
|
|
1498
|
+
/**
|
|
1499
|
+
* Replaces the font files the style declares in its `font-faces` property, dropping every glyph
|
|
1500
|
+
* drawn with the previous ones.
|
|
1501
|
+
*/
|
|
1502
|
+
setFontFaces(fontFaces) {
|
|
1503
|
+
this.fontFaceManager.setFontFaces(fontFaces);
|
|
1504
|
+
this.entries = {};
|
|
1505
|
+
}
|
|
1324
1506
|
async getGlyphs(glyphs) {
|
|
1325
1507
|
const glyphsPromises = [];
|
|
1326
1508
|
for (const stack in glyphs) for (const id of glyphs[stack]) glyphsPromises.push(this._getAndCacheGlyphsPromise(stack, id));
|
|
@@ -1336,6 +1518,14 @@ var GlyphManager = class GlyphManager {
|
|
|
1336
1518
|
}
|
|
1337
1519
|
return result;
|
|
1338
1520
|
}
|
|
1521
|
+
/**
|
|
1522
|
+
* Gets one glyph, asked for by grapheme cluster so that a letter and its marks are drawn as the
|
|
1523
|
+
* one shape they are written as.
|
|
1524
|
+
*
|
|
1525
|
+
* Only a file the style pinned with `font-faces` can draw a cluster -- a glyphs URL serves
|
|
1526
|
+
* codepoints -- so where none covers it this returns nothing and layout falls back to codepoints.
|
|
1527
|
+
* For a single codepoint a declared file still wins over the glyphs URL and the local fallbacks.
|
|
1528
|
+
*/
|
|
1339
1529
|
async _getAndCacheGlyphsPromise(stack, id) {
|
|
1340
1530
|
this.entries[stack] ??= {
|
|
1341
1531
|
glyphs: {},
|
|
@@ -1349,7 +1539,25 @@ var GlyphManager = class GlyphManager {
|
|
|
1349
1539
|
id,
|
|
1350
1540
|
glyph
|
|
1351
1541
|
};
|
|
1352
|
-
|
|
1542
|
+
const codePoint = id.codePointAt(0);
|
|
1543
|
+
const fontFaceFamily = this.fontFaceManager.hasFontFaces() ? await this.fontFaceManager.getFontFamily(stack, codePoint) : null;
|
|
1544
|
+
if (fontFaceFamily) {
|
|
1545
|
+
glyph = entry.glyphs[id] = await this._drawGlyph(entry, stack, id, fontFaceFamily);
|
|
1546
|
+
return {
|
|
1547
|
+
stack,
|
|
1548
|
+
id,
|
|
1549
|
+
glyph
|
|
1550
|
+
};
|
|
1551
|
+
}
|
|
1552
|
+
if (isCluster(id)) {
|
|
1553
|
+
glyph = entry.glyphs[id] = null;
|
|
1554
|
+
return {
|
|
1555
|
+
stack,
|
|
1556
|
+
id,
|
|
1557
|
+
glyph
|
|
1558
|
+
};
|
|
1559
|
+
}
|
|
1560
|
+
if (!this.url || this._charUsesLocalIdeographFontFamily(codePoint)) {
|
|
1353
1561
|
glyph = entry.glyphs[id] = await this._drawGlyph(entry, stack, id);
|
|
1354
1562
|
return {
|
|
1355
1563
|
stack,
|
|
@@ -1359,27 +1567,34 @@ var GlyphManager = class GlyphManager {
|
|
|
1359
1567
|
}
|
|
1360
1568
|
return await this._downloadAndCacheRangePromise(stack, id);
|
|
1361
1569
|
}
|
|
1570
|
+
/**
|
|
1571
|
+
* Gets a glyph from the server-side cache, downloading the PBF range it falls in if need be.
|
|
1572
|
+
*
|
|
1573
|
+
* Only reached for a single codepoint. What comes back is keyed by codepoint, as the file is,
|
|
1574
|
+
* and is cached by cluster -- which for one codepoint is the character itself.
|
|
1575
|
+
*/
|
|
1362
1576
|
async _downloadAndCacheRangePromise(stack, id) {
|
|
1577
|
+
const codePoint = id.codePointAt(0);
|
|
1363
1578
|
const entry = this.entries[stack];
|
|
1364
|
-
const range = Math.floor(
|
|
1579
|
+
const range = Math.floor(codePoint / 256);
|
|
1365
1580
|
if (entry.ranges[range]) return {
|
|
1366
1581
|
stack,
|
|
1367
1582
|
id,
|
|
1368
1583
|
glyph: null
|
|
1369
1584
|
};
|
|
1370
|
-
entry.requests[range] ||=
|
|
1585
|
+
entry.requests[range] ||= this._loadGlyphRange(stack, range);
|
|
1371
1586
|
try {
|
|
1372
1587
|
const response = await entry.requests[range];
|
|
1373
|
-
for (const
|
|
1588
|
+
for (const responseId in response) entry.glyphs[String.fromCodePoint(+responseId)] = response[+responseId];
|
|
1374
1589
|
entry.ranges[range] = true;
|
|
1375
1590
|
return {
|
|
1376
1591
|
stack,
|
|
1377
1592
|
id,
|
|
1378
|
-
glyph: response[
|
|
1593
|
+
glyph: response[codePoint] || null
|
|
1379
1594
|
};
|
|
1380
1595
|
} catch (e) {
|
|
1381
1596
|
const glyph = entry.glyphs[id] = await this._drawGlyph(entry, stack, id);
|
|
1382
|
-
this._warnOnMissingGlyphRange(glyph, range,
|
|
1597
|
+
this._warnOnMissingGlyphRange(glyph, range, codePoint, ensureError(e));
|
|
1383
1598
|
return {
|
|
1384
1599
|
stack,
|
|
1385
1600
|
id,
|
|
@@ -1387,6 +1602,19 @@ var GlyphManager = class GlyphManager {
|
|
|
1387
1602
|
};
|
|
1388
1603
|
}
|
|
1389
1604
|
}
|
|
1605
|
+
/**
|
|
1606
|
+
* Downloads one range of 256 codepoints from the glyphs URL and parses the glyphs out of it.
|
|
1607
|
+
*/
|
|
1608
|
+
async _loadGlyphRange(fontstack, range) {
|
|
1609
|
+
const begin = range * 256;
|
|
1610
|
+
const end = begin + 255;
|
|
1611
|
+
const request = await this.requestManager.transformRequest(this.url.replace("{fontstack}", fontstack).replace("{range}", `${begin}-${end}`), "Glyphs");
|
|
1612
|
+
const response = await getArrayBuffer(request, new AbortController());
|
|
1613
|
+
if (!response?.data) throw new Error(`Could not load glyph range. range: ${range}, ${begin}-${end}`);
|
|
1614
|
+
const glyphs = {};
|
|
1615
|
+
for (const glyph of parseGlyphPbf(response.data)) glyphs[glyph.id] = glyph;
|
|
1616
|
+
return glyphs;
|
|
1617
|
+
}
|
|
1390
1618
|
_warnOnMissingGlyphRange(glyph, range, id, err) {
|
|
1391
1619
|
const begin = range * 256;
|
|
1392
1620
|
const end = begin + 255;
|
|
@@ -1400,13 +1628,13 @@ var GlyphManager = class GlyphManager {
|
|
|
1400
1628
|
return !!this.localIdeographFontFamily && codePointUsesLocalIdeographFontFamily(id);
|
|
1401
1629
|
}
|
|
1402
1630
|
/**
|
|
1403
|
-
* Draws a glyph offscreen using TinySDF,
|
|
1631
|
+
* Draws a glyph offscreen using TinySDF, created lazily. The whole cluster goes to TinySDF, which
|
|
1632
|
+
* is what lets the browser's text engine place a letter's marks on it.
|
|
1633
|
+
*
|
|
1634
|
+
* @param fontFaceFamily - the CSS family of the `font-faces` file covering this codepoint, if any
|
|
1404
1635
|
*/
|
|
1405
|
-
async _drawGlyph(entry, stack, id) {
|
|
1406
|
-
const
|
|
1407
|
-
const tinySDFKey = usesLocalIdeographFontFamily ? "ideographTinySDF" : "tinySDF";
|
|
1408
|
-
entry[tinySDFKey] ||= this._createTinySDF(usesLocalIdeographFontFamily ? this.localIdeographFontFamily : stack);
|
|
1409
|
-
const char = (await entry[tinySDFKey]).draw(String.fromCodePoint(id));
|
|
1636
|
+
async _drawGlyph(entry, stack, id, fontFaceFamily) {
|
|
1637
|
+
const char = (await this._getTinySDF(entry, stack, id, fontFaceFamily)).draw(id);
|
|
1410
1638
|
/**
|
|
1411
1639
|
* TinySDF's "top" is the distance from the alphabetic baseline to the top of the glyph.
|
|
1412
1640
|
* Server-generated fonts specify "top" relative to an origin above the em box (the origin
|
|
@@ -1422,9 +1650,9 @@ var GlyphManager = class GlyphManager {
|
|
|
1422
1650
|
*/
|
|
1423
1651
|
const topAdjustment = 27.5;
|
|
1424
1652
|
const leftAdjustment = .5;
|
|
1425
|
-
const isControl = /^\p{gc=Cf}+$/u.test(
|
|
1653
|
+
const isControl = /^\p{gc=Cf}+$/u.test(id);
|
|
1426
1654
|
return {
|
|
1427
|
-
id,
|
|
1655
|
+
id: id.codePointAt(0),
|
|
1428
1656
|
bitmap: new AlphaImage({
|
|
1429
1657
|
width: char.width || 60,
|
|
1430
1658
|
height: char.height || 60
|
|
@@ -1439,28 +1667,59 @@ var GlyphManager = class GlyphManager {
|
|
|
1439
1667
|
}
|
|
1440
1668
|
};
|
|
1441
1669
|
}
|
|
1442
|
-
|
|
1670
|
+
/**
|
|
1671
|
+
* Returns the TinySDF that draws this grapheme, created lazily. A stack keeps one per font
|
|
1672
|
+
* selection, so a fallback cannot bleed into the rest of the text, and one more per font file
|
|
1673
|
+
* for the clusters drawn from it, which need a wider canvas.
|
|
1674
|
+
*
|
|
1675
|
+
* A font file carries its own weight and style, so neither is sniffed out of the family name.
|
|
1676
|
+
* Where no file covers the grapheme, `localIdeographFontFamily` beats the last resort fontstack.
|
|
1677
|
+
*/
|
|
1678
|
+
_getTinySDF(entry, stack, id, fontFaceFamily) {
|
|
1679
|
+
if (fontFaceFamily) {
|
|
1680
|
+
const cluster = isCluster(id);
|
|
1681
|
+
const cache = cluster ? "clusterTinySDFs" : "fontFaceTinySDFs";
|
|
1682
|
+
entry[cache] ??= {};
|
|
1683
|
+
entry[cache][fontFaceFamily] ||= this._createTinySDF(fontFaceFamily, false, cluster ? clusterEmsWide : 1);
|
|
1684
|
+
return entry[cache][fontFaceFamily];
|
|
1685
|
+
}
|
|
1686
|
+
const usesLocalIdeographFontFamily = stack === defaultStack && this.localIdeographFontFamily !== "" && this._charUsesLocalIdeographFontFamily(id.codePointAt(0));
|
|
1687
|
+
const cache = usesLocalIdeographFontFamily ? "ideographTinySDF" : "tinySDF";
|
|
1688
|
+
entry[cache] ||= this._createTinySDF(usesLocalIdeographFontFamily ? this.localIdeographFontFamily : stack);
|
|
1689
|
+
return entry[cache];
|
|
1690
|
+
}
|
|
1691
|
+
/**
|
|
1692
|
+
* Builds the TinySDF that draws with a given font selection.
|
|
1693
|
+
*
|
|
1694
|
+
* TinySDF derives its canvas from `fontSize + buffer * 4`, so the buffer is the only way in to a
|
|
1695
|
+
* wider one. It also stands for the padding the atlas expects to be `GLYPH_PBF_BORDER`, so the
|
|
1696
|
+
* two are passed separately.
|
|
1697
|
+
*
|
|
1698
|
+
* @param emsWide - how wide the glyphs may be, in font sizes, before TinySDF cuts them off
|
|
1699
|
+
*/
|
|
1700
|
+
async _createTinySDF(stack, sniffFontStyles = true, emsWide = 1) {
|
|
1443
1701
|
const fontFamilies = stack ? stack.split(",") : [];
|
|
1444
1702
|
fontFamilies.push(defaultGenericFontFamily);
|
|
1445
1703
|
const fontFamily = fontFamilies.map((fontName) => /[-\w]+/.test(fontName) ? fontName : `'${CSS.escape(fontName)}'`).join(",");
|
|
1446
1704
|
const fontSize = 48;
|
|
1447
|
-
const fontWeight = this._fontWeight(fontFamilies[0]);
|
|
1448
|
-
const fontStyle = this._fontStyle(fontFamilies[0]);
|
|
1705
|
+
const fontWeight = sniffFontStyles ? this._fontWeight(fontFamilies[0]) : void 0;
|
|
1706
|
+
const fontStyle = sniffFontStyles ? this._fontStyle(fontFamilies[0]) : "normal";
|
|
1449
1707
|
if (typeof document !== "undefined" && document.fonts?.load) try {
|
|
1450
1708
|
await document.fonts.load(`${fontStyle} ${fontWeight || "normal"} ${fontSize}px ${fontFamily}`);
|
|
1451
1709
|
} catch (e) {
|
|
1452
1710
|
warnOnce(`Failed to load font "${fontFamily}": ${ensureError(e).message}`);
|
|
1453
1711
|
}
|
|
1454
|
-
|
|
1712
|
+
const padding = 6;
|
|
1713
|
+
return this.createRasterizer({
|
|
1455
1714
|
fontSize,
|
|
1456
|
-
buffer:
|
|
1715
|
+
buffer: Math.max(padding, Math.ceil(fontSize * (emsWide - 1) / 4)),
|
|
1457
1716
|
radius: 16,
|
|
1458
1717
|
cutoff: .25,
|
|
1459
1718
|
fontFamily,
|
|
1460
1719
|
fontWeight,
|
|
1461
1720
|
fontStyle,
|
|
1462
1721
|
lang: this.lang
|
|
1463
|
-
});
|
|
1722
|
+
}, padding);
|
|
1464
1723
|
}
|
|
1465
1724
|
/**
|
|
1466
1725
|
* Sniffs the font style out of a font family name.
|
|
@@ -1502,11 +1761,13 @@ var GlyphManager = class GlyphManager {
|
|
|
1502
1761
|
const entry = this.entries[stack];
|
|
1503
1762
|
entry.tinySDF = null;
|
|
1504
1763
|
entry.ideographTinySDF = null;
|
|
1764
|
+
entry.fontFaceTinySDFs = {};
|
|
1505
1765
|
entry.glyphs = {};
|
|
1506
1766
|
entry.requests = {};
|
|
1507
1767
|
entry.ranges = {};
|
|
1508
1768
|
}
|
|
1509
1769
|
this.entries = {};
|
|
1770
|
+
this.fontFaceManager.destroy();
|
|
1510
1771
|
}
|
|
1511
1772
|
};
|
|
1512
1773
|
//#endregion
|
|
@@ -4063,6 +4324,18 @@ const BILINEAR_FORESHORTENING = 512;
|
|
|
4063
4324
|
* const bitmap = await createImageBitmap(myCanvas);
|
|
4064
4325
|
* mySource.updateImage({image: bitmap});
|
|
4065
4326
|
*
|
|
4327
|
+
* // create an empty source (no url), then feed it entirely via updateImage
|
|
4328
|
+
* map.addSource('empty id', {
|
|
4329
|
+
* type: 'image',
|
|
4330
|
+
* coordinates: [
|
|
4331
|
+
* [-76.54, 39.18],
|
|
4332
|
+
* [-76.52, 39.18],
|
|
4333
|
+
* [-76.52, 39.17],
|
|
4334
|
+
* [-76.54, 39.17]
|
|
4335
|
+
* ]
|
|
4336
|
+
* });
|
|
4337
|
+
* (map.getSource('empty id') as ImageSource).updateImage({image: bitmap});
|
|
4338
|
+
*
|
|
4066
4339
|
* map.removeSource('some id'); // remove
|
|
4067
4340
|
* ```
|
|
4068
4341
|
*/
|
|
@@ -4089,6 +4362,11 @@ var ImageSource = class extends Evented {
|
|
|
4089
4362
|
this.options = options;
|
|
4090
4363
|
}
|
|
4091
4364
|
async load(newCoordinates) {
|
|
4365
|
+
if (this.options.url === void 0) {
|
|
4366
|
+
this._loaded = true;
|
|
4367
|
+
this._finishLoading();
|
|
4368
|
+
return;
|
|
4369
|
+
}
|
|
4092
4370
|
this._loaded = false;
|
|
4093
4371
|
this.fire(new MapSourceDataEvent("dataloading"));
|
|
4094
4372
|
this.url = this.options.url;
|
|
@@ -4271,11 +4549,12 @@ var ImageSource = class extends Evented {
|
|
|
4271
4549
|
} else tile.state = "errored";
|
|
4272
4550
|
}
|
|
4273
4551
|
serialize() {
|
|
4274
|
-
|
|
4552
|
+
const spec = {
|
|
4275
4553
|
type: "image",
|
|
4276
|
-
url: this.options.url,
|
|
4277
4554
|
coordinates: this.coordinates
|
|
4278
4555
|
};
|
|
4556
|
+
if (this.options.url !== void 0) spec.url = this.options.url;
|
|
4557
|
+
return spec;
|
|
4279
4558
|
}
|
|
4280
4559
|
hasTransition() {
|
|
4281
4560
|
return false;
|
|
@@ -9968,11 +10247,13 @@ var TerrainTileManager = class extends Evented {
|
|
|
9968
10247
|
* Load Terrain Tiles, create internal render-to-texture tiles, free GPU memory.
|
|
9969
10248
|
* @param transform - the operation to do
|
|
9970
10249
|
* @param terrain - the terrain
|
|
10250
|
+
* @returns true when the set of renderable tiles changed
|
|
9971
10251
|
*/
|
|
9972
10252
|
update(transform, terrain) {
|
|
9973
10253
|
this.tileManager.update(transform, terrain);
|
|
9974
10254
|
this._renderableTilesKeys = [];
|
|
9975
10255
|
const keys = {};
|
|
10256
|
+
let changed = false;
|
|
9976
10257
|
for (const tileID of coveringTiles(transform, {
|
|
9977
10258
|
tileSize: this.tileSize,
|
|
9978
10259
|
minzoom: this.minzoom,
|
|
@@ -9988,12 +10269,15 @@ var TerrainTileManager = class extends Evented {
|
|
|
9988
10269
|
ortho(tileID.terrainRttPosMatrix32f, 0, EXTENT, EXTENT, 0, 0, 1);
|
|
9989
10270
|
this._tiles[tileID.key] = new Tile(tileID, this.tileSize);
|
|
9990
10271
|
this._lastTilesetChange = now();
|
|
10272
|
+
changed = true;
|
|
9991
10273
|
}
|
|
9992
10274
|
}
|
|
9993
10275
|
for (const key in this._tiles) if (!keys[key]) {
|
|
9994
10276
|
this._tiles[key].releaseRTT(this.tileManager.map.painter);
|
|
9995
10277
|
delete this._tiles[key];
|
|
10278
|
+
changed = true;
|
|
9996
10279
|
}
|
|
10280
|
+
return changed;
|
|
9997
10281
|
}
|
|
9998
10282
|
/**
|
|
9999
10283
|
* Release the RTT objects for `tileID` (and its ancestors/descendants),
|
|
@@ -10280,11 +10564,19 @@ var Terrain = class {
|
|
|
10280
10564
|
}
|
|
10281
10565
|
/**
|
|
10282
10566
|
* Get the elevation for given {@link LngLat} in respect of exaggeration.
|
|
10283
|
-
*
|
|
10567
|
+
* Where the location is covered by a rendered tile with loaded DEM data this samples the
|
|
10568
|
+
* rendered surface, so the result agrees with what is drawn; elsewhere it traverses up the
|
|
10569
|
+
* zoom levels to find the first tile with data to return.
|
|
10284
10570
|
* @param lnglat - the location
|
|
10285
10571
|
* @returns the elevation
|
|
10286
10572
|
*/
|
|
10287
10573
|
getElevationForLngLat(lnglat, transform) {
|
|
10574
|
+
const index = this.getCoverageIndex();
|
|
10575
|
+
if (index) {
|
|
10576
|
+
const mercator = MercatorCoordinate.fromLngLat(lnglat);
|
|
10577
|
+
const sample = sampleAt(index, this.exaggeration, mercator.x, mercator.y);
|
|
10578
|
+
if (sample.demLoaded) return sample.elevation;
|
|
10579
|
+
}
|
|
10288
10580
|
const terrainCoveringTiles = coveringTiles(transform, {
|
|
10289
10581
|
maxzoom: this.tileManager.maxzoom,
|
|
10290
10582
|
minzoom: this.tileManager.minzoom,
|
|
@@ -10591,6 +10883,7 @@ var Terrain = class {
|
|
|
10591
10883
|
};
|
|
10592
10884
|
const NOT_COVERED = {
|
|
10593
10885
|
covered: false,
|
|
10886
|
+
demLoaded: false,
|
|
10594
10887
|
elevation: 0
|
|
10595
10888
|
};
|
|
10596
10889
|
/**
|
|
@@ -10612,10 +10905,12 @@ function sampleAt(index, exaggeration, mercatorX, mercatorY) {
|
|
|
10612
10905
|
const sampler = index.samplerPerTile.get(key);
|
|
10613
10906
|
if (!sampler) return {
|
|
10614
10907
|
covered: true,
|
|
10908
|
+
demLoaded: false,
|
|
10615
10909
|
elevation: 0
|
|
10616
10910
|
};
|
|
10617
10911
|
return {
|
|
10618
10912
|
covered: true,
|
|
10913
|
+
demLoaded: true,
|
|
10619
10914
|
elevation: sampler(Math.min((scaledX - tileX) * EXTENT, MAX_TILE_COORD), Math.min((scaledY - tileY) * EXTENT, MAX_TILE_COORD), EXTENT) * exaggeration
|
|
10620
10915
|
};
|
|
10621
10916
|
}
|
|
@@ -10960,7 +11255,7 @@ var MercatorTransform = class MercatorTransform {
|
|
|
10960
11255
|
}
|
|
10961
11256
|
recalculateZoomAndCenter(terrain) {
|
|
10962
11257
|
const center = this.screenPointToLocation(this.centerPoint, terrain);
|
|
10963
|
-
const elevation = terrain ? terrain.
|
|
11258
|
+
const elevation = terrain ? terrain.getElevationForLngLat(center, this) : 0;
|
|
10964
11259
|
this._helper.recalculateZoomAndCenter(elevation);
|
|
10965
11260
|
}
|
|
10966
11261
|
/**
|
|
@@ -13301,7 +13596,7 @@ function globeSampleAt(ray, t) {
|
|
|
13301
13596
|
const elevation = Math.abs(lngLat.lat) > 85.051129 ? 0 : sample.elevation;
|
|
13302
13597
|
return {
|
|
13303
13598
|
sample: {
|
|
13304
|
-
|
|
13599
|
+
...sample,
|
|
13305
13600
|
elevation
|
|
13306
13601
|
},
|
|
13307
13602
|
radius,
|
|
@@ -14181,6 +14476,8 @@ var Style = class extends Evented {
|
|
|
14181
14476
|
this._glyphsDidChange = false;
|
|
14182
14477
|
this._updatedPaintProps = {};
|
|
14183
14478
|
this._layerOrderChanged = false;
|
|
14479
|
+
this._symbolPlacementTriggered = false;
|
|
14480
|
+
this._placedProjectionTransition = void 0;
|
|
14184
14481
|
this.crossTileSymbolIndex = new ((this.crossTileSymbolIndex?.constructor) || Object)();
|
|
14185
14482
|
this.pauseablePlacement = void 0;
|
|
14186
14483
|
this.placement = void 0;
|
|
@@ -14275,6 +14572,7 @@ var Style = class extends Evented {
|
|
|
14275
14572
|
if (nextState.sprite) this._loadSprite(nextState.sprite);
|
|
14276
14573
|
else this.imageManager.setLoaded(true);
|
|
14277
14574
|
this.glyphManager.setURL(nextState.glyphs);
|
|
14575
|
+
this.glyphManager.setFontFaces(nextState["font-faces"]);
|
|
14278
14576
|
this._createLayers();
|
|
14279
14577
|
this.light = new Light(this.stylesheet.light ?? {}, this._globalState);
|
|
14280
14578
|
this._setProjectionInternal(this.stylesheet.projection?.type || "mercator");
|
|
@@ -14538,6 +14836,9 @@ var Style = class extends Evented {
|
|
|
14538
14836
|
case "setGlyphs":
|
|
14539
14837
|
operations.push(() => this.setGlyphs.apply(this, op.args));
|
|
14540
14838
|
break;
|
|
14839
|
+
case "setFontFaces":
|
|
14840
|
+
operations.push(() => this.setFontFaces.apply(this, op.args));
|
|
14841
|
+
break;
|
|
14541
14842
|
case "setSprite":
|
|
14542
14843
|
operations.push(() => this.setSprite.apply(this, op.args));
|
|
14543
14844
|
break;
|
|
@@ -14863,6 +15164,7 @@ var Style = class extends Evented {
|
|
|
14863
15164
|
if (isRasterStyleLayer(layer) && name === "raster-fade-duration") this.tileManagers[layer.source].setRasterFadeDuration(value);
|
|
14864
15165
|
this._changed = true;
|
|
14865
15166
|
this._updatedPaintProps[layer.id] = true;
|
|
15167
|
+
if (layer.type === "symbol") this.triggerSymbolPlacement();
|
|
14866
15168
|
this._serializedLayers = null;
|
|
14867
15169
|
}
|
|
14868
15170
|
getPaintProperty(layer, name) {
|
|
@@ -14961,8 +15263,10 @@ var Style = class extends Evented {
|
|
|
14961
15263
|
pitch: myStyleSheet.pitch,
|
|
14962
15264
|
sprite: myStyleSheet.sprite,
|
|
14963
15265
|
glyphs: myStyleSheet.glyphs,
|
|
15266
|
+
"font-faces": myStyleSheet["font-faces"],
|
|
14964
15267
|
transition: myStyleSheet.transition,
|
|
14965
15268
|
projection: myStyleSheet.projection,
|
|
15269
|
+
state: myStyleSheet.state,
|
|
14966
15270
|
sources,
|
|
14967
15271
|
layers,
|
|
14968
15272
|
terrain
|
|
@@ -15120,6 +15424,7 @@ var Style = class extends Evented {
|
|
|
15120
15424
|
for (const key in this.tileManagers) this.tileManagers[key].reload();
|
|
15121
15425
|
}
|
|
15122
15426
|
_validate(validate, key, value, props, options = {}) {
|
|
15427
|
+
if (options.validate === false) return false;
|
|
15123
15428
|
return validateAndEmit(this, validate, {
|
|
15124
15429
|
key,
|
|
15125
15430
|
style: this.serialize(),
|
|
@@ -15165,6 +15470,26 @@ var Style = class extends Evented {
|
|
|
15165
15470
|
_generateCollisionBoxes() {
|
|
15166
15471
|
for (const id in this.tileManagers) this._reloadSource(id);
|
|
15167
15472
|
}
|
|
15473
|
+
/**
|
|
15474
|
+
* Re-places symbols on the next frame.
|
|
15475
|
+
* Placement is skipped while its inputs look unchanged, so call this when something
|
|
15476
|
+
* the map cannot see for itself has moved symbols.
|
|
15477
|
+
*/
|
|
15478
|
+
triggerSymbolPlacement() {
|
|
15479
|
+
this._symbolPlacementTriggered = true;
|
|
15480
|
+
}
|
|
15481
|
+
/**
|
|
15482
|
+
* Whether re-running symbol placement could produce a different result than the last run.
|
|
15483
|
+
* Every camera and viewport parameter placement can observe perturbs the model-view-projection
|
|
15484
|
+
* matrix, so comparing the matrix covers them all. Inputs the matrix cannot see arrive as
|
|
15485
|
+
* {@link triggerSymbolPlacement} calls.
|
|
15486
|
+
*
|
|
15487
|
+
* @internal
|
|
15488
|
+
*/
|
|
15489
|
+
_placementInputsChanged(transform, showCollisionBoxes, crossSourceCollisions) {
|
|
15490
|
+
const lastPlacement = this.pauseablePlacement;
|
|
15491
|
+
return !lastPlacement || this._symbolPlacementTriggered || this._placedProjectionTransition !== this.projection?.transitionState || lastPlacement._showCollisionBoxes !== showCollisionBoxes || lastPlacement.placement.collisionGroups.crossSourceCollisions !== crossSourceCollisions || lastPlacement.placement.transform.renderWorldCopies !== transform.renderWorldCopies || !exactEquals(lastPlacement.placement.transform.modelViewProjectionMatrix, transform.modelViewProjectionMatrix);
|
|
15492
|
+
}
|
|
15168
15493
|
_updatePlacement(transform, showCollisionBoxes, fadeDuration, crossSourceCollisions, forceFullPlacement = false) {
|
|
15169
15494
|
let symbolBucketsChanged = false;
|
|
15170
15495
|
let placementCommitted = false;
|
|
@@ -15181,12 +15506,17 @@ var Style = class extends Evented {
|
|
|
15181
15506
|
}
|
|
15182
15507
|
this.crossTileSymbolIndex.pruneUnusedLayers(this._order);
|
|
15183
15508
|
forceFullPlacement ||= this._layerOrderChanged || fadeDuration === 0;
|
|
15184
|
-
|
|
15509
|
+
const placementInputsChanged = symbolBucketsChanged || this._placementInputsChanged(transform, showCollisionBoxes, crossSourceCollisions);
|
|
15510
|
+
const placementSettled = this.pauseablePlacement?.isDone() && !this.placement.stillRecent(now(), transform.zoom);
|
|
15511
|
+
if (forceFullPlacement || !this.pauseablePlacement || placementSettled && (placementInputsChanged || this.placement.stale)) {
|
|
15512
|
+
this._symbolPlacementTriggered = false;
|
|
15513
|
+
this._placedProjectionTransition = this.projection?.transitionState;
|
|
15185
15514
|
this.pauseablePlacement = new PauseablePlacement(transform, this.map.terrain, this._order, forceFullPlacement, showCollisionBoxes, fadeDuration, crossSourceCollisions, this.placement);
|
|
15186
15515
|
this._layerOrderChanged = false;
|
|
15187
15516
|
}
|
|
15188
|
-
if (this.pauseablePlacement.isDone())
|
|
15189
|
-
|
|
15517
|
+
if (this.pauseablePlacement.isDone()) {
|
|
15518
|
+
if (placementInputsChanged) this.placement.setStale();
|
|
15519
|
+
} else {
|
|
15190
15520
|
this.pauseablePlacement.continuePlacement(this._order, this._layers, layerTiles);
|
|
15191
15521
|
if (this.pauseablePlacement.isDone()) {
|
|
15192
15522
|
this.placement = this.pauseablePlacement.commit(now());
|
|
@@ -15223,11 +15553,22 @@ var Style = class extends Evented {
|
|
|
15223
15553
|
setGlyphs(glyphsUrl, options = {}) {
|
|
15224
15554
|
this._checkLoaded();
|
|
15225
15555
|
if (glyphsUrl && this._validate(validateStyle.glyphs, "glyphs", glyphsUrl, null, options)) return;
|
|
15556
|
+
this._changed = true;
|
|
15226
15557
|
this._glyphsDidChange = true;
|
|
15227
15558
|
this.stylesheet.glyphs = glyphsUrl;
|
|
15228
15559
|
this.glyphManager.entries = {};
|
|
15229
15560
|
this.glyphManager.setURL(glyphsUrl);
|
|
15230
15561
|
}
|
|
15562
|
+
getFontFaces() {
|
|
15563
|
+
return this.stylesheet["font-faces"] || null;
|
|
15564
|
+
}
|
|
15565
|
+
setFontFaces(fontFaces) {
|
|
15566
|
+
this._checkLoaded();
|
|
15567
|
+
this._changed = true;
|
|
15568
|
+
this._glyphsDidChange = true;
|
|
15569
|
+
this.stylesheet["font-faces"] = fontFaces;
|
|
15570
|
+
this.glyphManager.setFontFaces(fontFaces);
|
|
15571
|
+
}
|
|
15231
15572
|
async getDashes(mapId, params) {
|
|
15232
15573
|
const result = {};
|
|
15233
15574
|
for (const [key, dash] of Object.entries(params.dashes)) result[key] = this.lineAtlas.getDash(dash.dasharray, dash.round);
|
|
@@ -17271,7 +17612,7 @@ function drawSymbols(painter, tileManager, layer, coords, variableOffsets, rende
|
|
|
17271
17612
|
const stencilMode = StencilMode.disabled;
|
|
17272
17613
|
const colorMode = painter.colorModeForRenderPass();
|
|
17273
17614
|
if (layer._unevaluatedLayout.hasValue("text-variable-anchor") || layer._unevaluatedLayout.hasValue("text-variable-anchor-offset")) updateVariableAnchors(coords, painter, layer, tileManager, layer.layout.get("text-rotation-alignment"), layer.layout.get("text-pitch-alignment"), layer.paint.get("text-translate"), layer.paint.get("text-translate-anchor"), variableOffsets);
|
|
17274
|
-
if (layer.paint.get("icon-opacity").constantOr(1) !== 0) drawLayerSymbols(painter, tileManager, layer, coords, false, layer.paint.get("icon-translate"), layer.paint.get("icon-translate-anchor"), layer.layout.get("icon-rotation-alignment"), layer.layout.get("icon-pitch-alignment"), layer.layout.get("icon-keep-upright"), stencilMode, colorMode, isRenderingToTexture);
|
|
17615
|
+
if (layer.paint.get("icon-opacity").constantOr(1) !== 0) drawLayerSymbols(painter, tileManager, layer, coords, false, layer.paint.get("icon-translate"), layer.paint.get("icon-translate-anchor"), layer.layout.get("icon-rotation-alignment").constantOr("viewport"), layer.layout.get("icon-pitch-alignment"), layer.layout.get("icon-keep-upright"), stencilMode, colorMode, isRenderingToTexture);
|
|
17275
17616
|
if (layer.paint.get("text-opacity").constantOr(1) !== 0) drawLayerSymbols(painter, tileManager, layer, coords, true, layer.paint.get("text-translate"), layer.paint.get("text-translate-anchor"), layer.layout.get("text-rotation-alignment"), layer.layout.get("text-pitch-alignment"), layer.layout.get("text-keep-upright"), stencilMode, colorMode, isRenderingToTexture);
|
|
17276
17617
|
if (tileManager.map.showCollisionBoxes) {
|
|
17277
17618
|
drawCollisionDebug(painter, tileManager, layer, coords, true);
|
|
@@ -19382,7 +19723,8 @@ var Painter = class Painter {
|
|
|
19382
19723
|
//#endregion
|
|
19383
19724
|
//#region src/util/gpu_initialization_error.ts
|
|
19384
19725
|
/**
|
|
19385
|
-
* Thrown
|
|
19726
|
+
* Thrown by the `Map` constructor when a GPU rendering context cannot be created,
|
|
19727
|
+
* or fired via the map's `error` event when recreating the context after a `webglcontextrestored` fails.
|
|
19386
19728
|
*
|
|
19387
19729
|
* Carries the canvas attributes that were requested and, when the browser provided one,
|
|
19388
19730
|
* the originating `webglcontextcreationerror` `statusMessage`.
|
|
@@ -22261,7 +22603,7 @@ var Camera = class extends Evented {
|
|
|
22261
22603
|
let bearingChanged = false, pitchChanged = false;
|
|
22262
22604
|
let rollChanged = false;
|
|
22263
22605
|
const oldZoom = tr.zoom;
|
|
22264
|
-
if (this.terrain) tr.setElevation(this.terrain.
|
|
22606
|
+
if (this.terrain) tr.setElevation(this.terrain.getElevationForLngLat(options.center ? LngLat.convert(options.center) : tr.center, tr));
|
|
22265
22607
|
this.cameraHelper.handleJumpToCenterZoom(tr, options);
|
|
22266
22608
|
const zoomChanged = tr.zoom !== oldZoom;
|
|
22267
22609
|
if ("elevation" in options && tr.elevation !== +options.elevation) tr.setElevation(+options.elevation);
|
|
@@ -22384,13 +22726,13 @@ var Camera = class extends Evented {
|
|
|
22384
22726
|
_prepareElevation(center) {
|
|
22385
22727
|
this._elevationCenter = center;
|
|
22386
22728
|
this._elevationStart = this.transform.elevation;
|
|
22387
|
-
this._elevationTarget = this.terrain.
|
|
22729
|
+
this._elevationTarget = this.terrain.getElevationForLngLat(center, this.transform);
|
|
22388
22730
|
this.elevationFreeze = true;
|
|
22389
22731
|
}
|
|
22390
22732
|
_updateElevation(k) {
|
|
22391
22733
|
if (this._elevationStart === void 0 || this._elevationCenter === void 0) this._prepareElevation(this.transform.center);
|
|
22392
22734
|
this.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this._elevationCenter, this.transform.tileZoom));
|
|
22393
|
-
const elevation = this.terrain.
|
|
22735
|
+
const elevation = this.terrain.getElevationForLngLat(this._elevationCenter, this.transform);
|
|
22394
22736
|
if (k < 1 && elevation !== this._elevationTarget) {
|
|
22395
22737
|
const pitch1 = this._elevationTarget - this._elevationStart;
|
|
22396
22738
|
const pitch2 = (elevation - (pitch1 * k + this._elevationStart)) / (1 - k);
|
|
@@ -23200,8 +23542,12 @@ var Map$1 = class extends Evented {
|
|
|
23200
23542
|
style: null,
|
|
23201
23543
|
images: null
|
|
23202
23544
|
};
|
|
23203
|
-
|
|
23204
|
-
|
|
23545
|
+
try {
|
|
23546
|
+
this._setupPainter();
|
|
23547
|
+
} catch (error) {
|
|
23548
|
+
this.fire(new ErrorEvent(error));
|
|
23549
|
+
return;
|
|
23550
|
+
}
|
|
23205
23551
|
this.resize();
|
|
23206
23552
|
this._update();
|
|
23207
23553
|
this._resizeInternal();
|
|
@@ -23266,13 +23612,17 @@ var Map$1 = class extends Evented {
|
|
|
23266
23612
|
this.cancelPendingTileRequestsWhileZooming = resolvedOptions.cancelPendingTileRequestsWhileZooming === true;
|
|
23267
23613
|
this.setAnisotropicFilterPitch(resolvedOptions.anisotropicFilterPitch);
|
|
23268
23614
|
if (resolvedOptions.reduceMotion !== void 0) browser.prefersReducedMotion = resolvedOptions.reduceMotion;
|
|
23269
|
-
this._imageQueueHandle = ImageRequest.addThrottleControl(() => this.isMoving());
|
|
23270
23615
|
this._requestManager = new RequestManager(resolvedOptions.transformRequest);
|
|
23271
23616
|
this._container = this._resolveContainer(resolvedOptions.container);
|
|
23272
23617
|
if (resolvedOptions.maxBounds) this.setMaxBounds(resolvedOptions.maxBounds);
|
|
23273
23618
|
this._setupContainer();
|
|
23274
|
-
|
|
23275
|
-
|
|
23619
|
+
try {
|
|
23620
|
+
this._setupPainter();
|
|
23621
|
+
} catch (error) {
|
|
23622
|
+
this._cleanupContainer();
|
|
23623
|
+
throw error;
|
|
23624
|
+
}
|
|
23625
|
+
this._imageQueueHandle = ImageRequest.addThrottleControl(() => this.isMoving());
|
|
23276
23626
|
this.on("move", () => this._update(false));
|
|
23277
23627
|
this.on("moveend", () => this._update(false));
|
|
23278
23628
|
this.on("zoom", () => this._update(true));
|
|
@@ -24247,14 +24597,17 @@ var Map$1 = class extends Evented {
|
|
|
24247
24597
|
/**
|
|
24248
24598
|
* Returns the map's minimum allowable zoom level.
|
|
24249
24599
|
*
|
|
24600
|
+
* @param constrained - If `true`, returns the effective minimum zoom after applying the map's viewport constraints.
|
|
24601
|
+
* If `false` or omitted, returns the configured minimum zoom.
|
|
24250
24602
|
* @returns minZoom
|
|
24251
24603
|
* @example
|
|
24252
24604
|
* ```ts
|
|
24253
24605
|
* let minZoom = map.getMinZoom();
|
|
24254
24606
|
* ```
|
|
24255
24607
|
*/
|
|
24256
|
-
getMinZoom() {
|
|
24257
|
-
|
|
24608
|
+
getMinZoom(constrained = false) {
|
|
24609
|
+
const transform = this._camera.transform;
|
|
24610
|
+
return constrained ? transform.applyConstrain(transform.center, transform.minZoom).zoom : transform.minZoom;
|
|
24258
24611
|
}
|
|
24259
24612
|
/**
|
|
24260
24613
|
* Sets or clears the map's maximum zoom level.
|
|
@@ -25049,10 +25402,11 @@ var Map$1 = class extends Evented {
|
|
|
25049
25402
|
this.painter.renderToTexture = new RenderToTexture(this.painter, this.terrain);
|
|
25050
25403
|
this._camera.terrain = this.terrain;
|
|
25051
25404
|
this._camera.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this._camera.transform.center, this._camera.transform.tileZoom));
|
|
25052
|
-
this._camera.transform.setElevation(this.terrain.
|
|
25405
|
+
this._camera.transform.setElevation(this.terrain.getElevationForLngLat(this._camera.transform.center, this._camera.transform));
|
|
25053
25406
|
this._terrainDataCallback = (e) => this._handleTerrainDataEvent(e, options.source);
|
|
25054
25407
|
this.style.on("data", this._terrainDataCallback);
|
|
25055
25408
|
}
|
|
25409
|
+
this.style.triggerSymbolPlacement();
|
|
25056
25410
|
this.fire(new MapTerrainEvent({ terrain: options }));
|
|
25057
25411
|
return this;
|
|
25058
25412
|
}
|
|
@@ -25062,10 +25416,13 @@ var Map$1 = class extends Evented {
|
|
|
25062
25416
|
return;
|
|
25063
25417
|
}
|
|
25064
25418
|
const isTerrainSourceEvent = event.sourceId === terrainSourceId;
|
|
25065
|
-
if (isTerrainSourceEvent)
|
|
25419
|
+
if (isTerrainSourceEvent) {
|
|
25420
|
+
this.terrain.resetElevationCache();
|
|
25421
|
+
this.style.triggerSymbolPlacement();
|
|
25422
|
+
}
|
|
25066
25423
|
if (isTerrainSourceEvent && event.tile && !this._camera.elevationFreeze) {
|
|
25067
25424
|
this._camera.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this._camera.transform.center, this._camera.transform.tileZoom));
|
|
25068
|
-
if (this.getCenterClampedToGround()) this._camera.transform.setElevation(this.terrain.
|
|
25425
|
+
if (this.getCenterClampedToGround()) this._camera.transform.setElevation(this.terrain.getElevationForLngLat(this._camera.transform.center, this._camera.transform));
|
|
25069
25426
|
}
|
|
25070
25427
|
if (!event.tile) return;
|
|
25071
25428
|
if (event.source?.type === "image") {
|
|
@@ -25720,6 +26077,42 @@ var Map$1 = class extends Evented {
|
|
|
25720
26077
|
return this.style.getGlyphsUrl();
|
|
25721
26078
|
}
|
|
25722
26079
|
/**
|
|
26080
|
+
* Sets the value of the style's `font-faces` property, which points at the font files used to
|
|
26081
|
+
* draw text that the style's `glyphs` URL does not cover. Pass a falsy value (null or undefined)
|
|
26082
|
+
* to unset it.
|
|
26083
|
+
*
|
|
26084
|
+
* The files are handed to the browser's CSS Font Loading API, so any format the browser can
|
|
26085
|
+
* render text with may be used, and requests for them go through `transformRequest` as glyph
|
|
26086
|
+
* requests do. Text is drawn a grapheme cluster at a time, so a letter and the marks written on
|
|
26087
|
+
* it are handed to the browser's text engine together and come back as the one shape they are
|
|
26088
|
+
* written as -- which is what a `glyphs` URL, serving one codepoint at a time, cannot do.
|
|
26089
|
+
*
|
|
26090
|
+
* @param fontFaces - The font faces to set. Must conform to the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/root/#font-faces).
|
|
26091
|
+
* A declaration this cannot make sense of is skipped with a warning, as is a font file that
|
|
26092
|
+
* fails to load, so the text it would have drawn falls back to the `glyphs` URL.
|
|
26093
|
+
* @example
|
|
26094
|
+
* ```ts
|
|
26095
|
+
* map.setFontFaces({
|
|
26096
|
+
* 'Noto Sans Regular': [
|
|
26097
|
+
* {url: 'https://example.com/NotoSansKhmer-Regular.ttf', 'unicode-range': ['U+1780-17FF']}
|
|
26098
|
+
* ]
|
|
26099
|
+
* });
|
|
26100
|
+
* ```
|
|
26101
|
+
*/
|
|
26102
|
+
setFontFaces(fontFaces) {
|
|
26103
|
+
this._lazyInitEmptyStyle();
|
|
26104
|
+
this.style.setFontFaces(fontFaces);
|
|
26105
|
+
return this._update(true);
|
|
26106
|
+
}
|
|
26107
|
+
/**
|
|
26108
|
+
* Returns the value of the style's `font-faces` property.
|
|
26109
|
+
*
|
|
26110
|
+
* @returns The style's font faces, or `null` if it declares none.
|
|
26111
|
+
*/
|
|
26112
|
+
getFontFaces() {
|
|
26113
|
+
return this.style.getFontFaces();
|
|
26114
|
+
}
|
|
26115
|
+
/**
|
|
25723
26116
|
* Adds a sprite to the map's style. Fires the `style` event.
|
|
25724
26117
|
*
|
|
25725
26118
|
* @param id - The ID of the sprite to add. Must not conflict with existing sprites.
|
|
@@ -26055,12 +26448,28 @@ var Map$1 = class extends Evented {
|
|
|
26055
26448
|
]) positions[positionName] = DOM.create("div", `maplibregl-ctrl-${positionName} `, controlContainer);
|
|
26056
26449
|
this._container.addEventListener("scroll", this._onMapScroll, false);
|
|
26057
26450
|
}
|
|
26451
|
+
/**
|
|
26452
|
+
* @internal
|
|
26453
|
+
* Reverses the DOM mutations of {@link Map._setupContainer}, returning the container element to its pre-construction state.
|
|
26454
|
+
*/
|
|
26455
|
+
_cleanupContainer() {
|
|
26456
|
+
this._canvas.removeEventListener("webglcontextrestored", this._contextRestored, false);
|
|
26457
|
+
this._canvas.removeEventListener("webglcontextlost", this._contextLost, false);
|
|
26458
|
+
this._canvasContainer.remove();
|
|
26459
|
+
this._controlContainer.remove();
|
|
26460
|
+
this._container.removeEventListener("scroll", this._onMapScroll, false);
|
|
26461
|
+
this._container.classList.remove("maplibregl-map");
|
|
26462
|
+
}
|
|
26058
26463
|
_resizeCanvas(width, height, pixelRatio) {
|
|
26059
26464
|
this._canvas.width = Math.floor(pixelRatio * width);
|
|
26060
26465
|
this._canvas.height = Math.floor(pixelRatio * height);
|
|
26061
26466
|
this._canvas.style.width = `${width}px`;
|
|
26062
26467
|
this._canvas.style.height = `${height}px`;
|
|
26063
26468
|
}
|
|
26469
|
+
/**
|
|
26470
|
+
* @internal
|
|
26471
|
+
* Creates the WebGL2 context and the painter. Throws {@link GPUInitializationError} when the context cannot be created.
|
|
26472
|
+
*/
|
|
26064
26473
|
_setupPainter() {
|
|
26065
26474
|
const attributes = {
|
|
26066
26475
|
...this._canvasContextAttributes,
|
|
@@ -26074,10 +26483,7 @@ var Map$1 = class extends Evented {
|
|
|
26074
26483
|
creationEvent = event;
|
|
26075
26484
|
}, { once: true });
|
|
26076
26485
|
const gl = this._canvas.getContext("webgl2", attributes);
|
|
26077
|
-
if (!gl)
|
|
26078
|
-
this.fire(new ErrorEvent(new GPUInitializationError(attributes, creationEvent)));
|
|
26079
|
-
return;
|
|
26080
|
-
}
|
|
26486
|
+
if (!gl) throw new GPUInitializationError(attributes, creationEvent);
|
|
26081
26487
|
this.painter = new Painter(gl, this._camera.transform);
|
|
26082
26488
|
}
|
|
26083
26489
|
/**
|
|
@@ -26175,10 +26581,9 @@ var Map$1 = class extends Evented {
|
|
|
26175
26581
|
this.style._updateSources(this._camera.transform);
|
|
26176
26582
|
}
|
|
26177
26583
|
if (this.terrain) {
|
|
26178
|
-
this.terrain.tileManager.update(this._camera.transform, this.terrain);
|
|
26179
|
-
this.terrain.resetElevationCache();
|
|
26584
|
+
if (this.terrain.tileManager.update(this._camera.transform, this.terrain)) this.terrain.resetElevationCache();
|
|
26180
26585
|
this._camera.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this._camera.transform.center, this._camera.transform.tileZoom));
|
|
26181
|
-
if (!this._camera.elevationFreeze && this.getCenterClampedToGround()) this._camera.transform.setElevation(this.terrain.
|
|
26586
|
+
if (!this._camera.elevationFreeze && this.getCenterClampedToGround()) this._camera.transform.setElevation(this.terrain.getElevationForLngLat(this._camera.transform.center, this._camera.transform));
|
|
26182
26587
|
} else {
|
|
26183
26588
|
this._camera.transform.setMinElevationForCurrentTile(0);
|
|
26184
26589
|
if (this.getCenterClampedToGround()) this._camera.transform.setElevation(0);
|
|
@@ -26251,12 +26656,7 @@ var Map$1 = class extends Evented {
|
|
|
26251
26656
|
this._resizeObserver?.disconnect();
|
|
26252
26657
|
const extension = this.painter.context.gl.getExtension("WEBGL_lose_context");
|
|
26253
26658
|
if (extension?.loseContext) extension.loseContext();
|
|
26254
|
-
this.
|
|
26255
|
-
this._canvas.removeEventListener("webglcontextlost", this._contextLost, false);
|
|
26256
|
-
this._canvasContainer.remove();
|
|
26257
|
-
this._controlContainer.remove();
|
|
26258
|
-
this._container.removeEventListener("scroll", this._onMapScroll, false);
|
|
26259
|
-
this._container.classList.remove("maplibregl-map");
|
|
26659
|
+
this._cleanupContainer();
|
|
26260
26660
|
this._removed = true;
|
|
26261
26661
|
this.fire(new MapLibreEvent("remove"));
|
|
26262
26662
|
}
|
|
@@ -26433,7 +26833,7 @@ var NavigationControl = class {
|
|
|
26433
26833
|
this._updateZoomButtons = () => {
|
|
26434
26834
|
const zoom = this._map.getZoom();
|
|
26435
26835
|
const isMax = zoom === this._map.getMaxZoom();
|
|
26436
|
-
const isMin = zoom === this._map.getMinZoom();
|
|
26836
|
+
const isMin = zoom === this._map.getMinZoom(true);
|
|
26437
26837
|
this._zoomInButton.disabled = isMax;
|
|
26438
26838
|
this._zoomOutButton.disabled = isMin;
|
|
26439
26839
|
this._zoomInButton.setAttribute("aria-disabled", isMax.toString());
|
|
@@ -26487,7 +26887,7 @@ var NavigationControl = class {
|
|
|
26487
26887
|
if (this.options.showZoom) {
|
|
26488
26888
|
this._setButtonTitle(this._zoomInButton, "ZoomIn");
|
|
26489
26889
|
this._setButtonTitle(this._zoomOutButton, "ZoomOut");
|
|
26490
|
-
this._map.on("
|
|
26890
|
+
this._map.on("move", this._updateZoomButtons);
|
|
26491
26891
|
this._updateZoomButtons();
|
|
26492
26892
|
}
|
|
26493
26893
|
if (this.options.showCompass) {
|
|
@@ -26503,7 +26903,7 @@ var NavigationControl = class {
|
|
|
26503
26903
|
/** {@inheritDoc IControl.onRemove} */
|
|
26504
26904
|
onRemove() {
|
|
26505
26905
|
this._container.remove();
|
|
26506
|
-
if (this.options.showZoom) this._map.off("
|
|
26906
|
+
if (this.options.showZoom) this._map.off("move", this._updateZoomButtons);
|
|
26507
26907
|
if (this.options.showCompass) {
|
|
26508
26908
|
if (this.options.visualizePitch) this._map.off("pitch", this._rotateCompassArrow);
|
|
26509
26909
|
if (this.options.visualizeRoll) this._map.off("roll", this._rotateCompassArrow);
|