maplibre-gl 6.5.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 +2062 -1531
- package/dist/maplibre-gl-dev.mjs.map +1 -1
- package/dist/maplibre-gl-shared-dev.mjs +1906 -1181
- 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 +409 -153
- package/dist/maplibre-gl.mjs +42 -42
- package/dist/maplibre-gl.mjs.map +1 -1
- package/package.json +26 -21
- package/src/data/array_types.g.ts +43 -32
- package/src/data/bucket/symbol_attributes.ts +6 -3
- package/src/data/bucket/symbol_bucket.test.ts +45 -12
- package/src/data/bucket/symbol_bucket.ts +51 -26
- package/src/data/bucket.ts +11 -3
- package/src/data/feature_index.ts +38 -9
- package/src/geo/projection/covering_tiles.test.ts +36 -0
- package/src/geo/projection/covering_tiles.ts +2 -3
- package/src/geo/projection/globe_transform.test.ts +4 -5
- package/src/geo/projection/globe_transform.ts +7 -2
- package/src/geo/projection/globe_utils.ts +57 -0
- package/src/geo/projection/mercator_transform.bench.ts +55 -0
- package/src/geo/projection/mercator_transform.test.ts +227 -17
- package/src/geo/projection/mercator_transform.ts +119 -28
- package/src/geo/projection/mercator_utils.test.ts +50 -1
- package/src/geo/projection/mercator_utils.ts +20 -1
- package/src/geo/projection/vertical_perspective_transform.test.ts +97 -0
- package/src/geo/projection/vertical_perspective_transform.ts +72 -68
- package/src/geo/transform_helper.ts +14 -5
- package/src/geo/transform_interface.ts +22 -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/painter.test.ts +1 -3
- package/src/render/painter.ts +7 -20
- package/src/render/terrain.test.ts +109 -84
- package/src/render/terrain.ts +138 -117
- package/src/shaders/glsl/symbol_icon.vertex.glsl +4 -1
- package/src/shaders/glsl/symbol_icon.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/symbol_sdf.vertex.glsl +4 -1
- package/src/shaders/glsl/symbol_sdf.vertex.glsl.g.ts +1 -1
- package/src/shaders/glsl/symbol_text_and_icon.vertex.glsl +4 -1
- package/src/shaders/glsl/symbol_text_and_icon.vertex.glsl.g.ts +1 -1
- package/src/shaders/shaders.ts +0 -4
- 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/raster_dem_tile_source.ts +1 -0
- 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/query_utils.ts +8 -8
- 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 +9 -3
- package/src/style/style_layer.ts +4 -6
- package/src/symbol/collision_index.ts +25 -19
- package/src/symbol/placement.test.ts +1 -1
- package/src/symbol/placement.ts +28 -13
- package/src/symbol/projection.test.ts +79 -15
- package/src/symbol/projection.ts +38 -20
- package/src/symbol/shaping.test.ts +436 -2
- package/src/symbol/shaping.ts +298 -113
- package/src/symbol/symbol_elevation.ts +28 -0
- package/src/symbol/symbol_layout.ts +15 -7
- package/src/symbol/symbol_size.test.ts +100 -0
- package/src/symbol/symbol_size.ts +78 -50
- 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 +8 -3
- package/src/tile/tile.ts +3 -2
- package/src/tile/tile_manager_raster_dem.ts +1 -0
- 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/handler_manager.test.ts +11 -23
- package/src/ui/handler_manager.ts +1 -1
- package/src/ui/map.ts +87 -21
- package/src/ui/map_tests/map_pitch.test.ts +12 -0
- package/src/ui/map_tests/map_render.test.ts +139 -1
- package/src/ui/map_tests/map_style.test.ts +66 -27
- 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 +23 -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 +80 -4
- package/src/util/unicode_properties.g.ts +28 -0
- package/src/util/util.ts +6 -2
- package/src/webgl/draw/draw_color_relief.ts +7 -10
- package/src/webgl/draw/draw_symbol.ts +17 -13
- package/src/webgl/draw/draw_terrain.ts +4 -39
- package/src/webgl/draw/index.ts +2 -4
- package/src/webgl/program/program_uniforms.ts +1 -3
- package/src/webgl/program/symbol_program.ts +15 -5
- package/src/webgl/program/terrain_program.ts +1 -22
- package/src/webgl/render_to_texture.test.ts +0 -1
- package/src/shaders/glsl/terrain_coords.fragment.glsl +0 -11
- package/src/shaders/glsl/terrain_coords.fragment.glsl.g.ts +0 -2
- package/src/shaders/glsl/terrain_coords.vertex.glsl +0 -12
- package/src/shaders/glsl/terrain_coords.vertex.glsl.g.ts +0 -2
- 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
|
|
@@ -3013,6 +3274,7 @@ var RasterDEMTileSource = class extends RasterTileSource {
|
|
|
3013
3274
|
});
|
|
3014
3275
|
tile.needsHillshadePrepare = true;
|
|
3015
3276
|
tile.needsTerrainPrepare = true;
|
|
3277
|
+
tile.needsColorReliefPrepare = true;
|
|
3016
3278
|
tile.state = "loaded";
|
|
3017
3279
|
}
|
|
3018
3280
|
} catch (err) {
|
|
@@ -4062,6 +4324,18 @@ const BILINEAR_FORESHORTENING = 512;
|
|
|
4062
4324
|
* const bitmap = await createImageBitmap(myCanvas);
|
|
4063
4325
|
* mySource.updateImage({image: bitmap});
|
|
4064
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
|
+
*
|
|
4065
4339
|
* map.removeSource('some id'); // remove
|
|
4066
4340
|
* ```
|
|
4067
4341
|
*/
|
|
@@ -4088,6 +4362,11 @@ var ImageSource = class extends Evented {
|
|
|
4088
4362
|
this.options = options;
|
|
4089
4363
|
}
|
|
4090
4364
|
async load(newCoordinates) {
|
|
4365
|
+
if (this.options.url === void 0) {
|
|
4366
|
+
this._loaded = true;
|
|
4367
|
+
this._finishLoading();
|
|
4368
|
+
return;
|
|
4369
|
+
}
|
|
4091
4370
|
this._loaded = false;
|
|
4092
4371
|
this.fire(new MapSourceDataEvent("dataloading"));
|
|
4093
4372
|
this.url = this.options.url;
|
|
@@ -4270,11 +4549,12 @@ var ImageSource = class extends Evented {
|
|
|
4270
4549
|
} else tile.state = "errored";
|
|
4271
4550
|
}
|
|
4272
4551
|
serialize() {
|
|
4273
|
-
|
|
4552
|
+
const spec = {
|
|
4274
4553
|
type: "image",
|
|
4275
|
-
url: this.options.url,
|
|
4276
4554
|
coordinates: this.coordinates
|
|
4277
4555
|
};
|
|
4556
|
+
if (this.options.url !== void 0) spec.url = this.options.url;
|
|
4557
|
+
return spec;
|
|
4278
4558
|
}
|
|
4279
4559
|
hasTransition() {
|
|
4280
4560
|
return false;
|
|
@@ -5322,9 +5602,8 @@ function getElevationForTileCulling(transform) {
|
|
|
5322
5602
|
function coveringTiles(transform, options) {
|
|
5323
5603
|
const frustum = transform.getCameraFrustum();
|
|
5324
5604
|
const plane = transform.getClippingPlane();
|
|
5325
|
-
const cameraCoord =
|
|
5605
|
+
const cameraCoord = cameraMercatorCoordinate(transform);
|
|
5326
5606
|
const centerCoord = MercatorCoordinate.fromLngLat(transform.center, transform.elevation);
|
|
5327
|
-
cameraCoord.z = centerCoord.z + Math.cos(transform.pitchInRadians) * transform.cameraToCenterDistance / transform.worldSize;
|
|
5328
5607
|
const elevationForTileCulling = getElevationForTileCulling(transform);
|
|
5329
5608
|
const detailsProvider = transform.getCoveringTilesDetailsProvider();
|
|
5330
5609
|
const allowVariableZoom = detailsProvider.allowVariableZoom(transform, options);
|
|
@@ -5565,6 +5844,7 @@ function backfillDEM(tile, inViewTiles) {
|
|
|
5565
5844
|
function fillBorder(tile, borderTile) {
|
|
5566
5845
|
tile.needsHillshadePrepare = true;
|
|
5567
5846
|
tile.needsTerrainPrepare = true;
|
|
5847
|
+
tile.needsColorReliefPrepare = true;
|
|
5568
5848
|
let dx = borderTile.tileID.canonical.x - tile.tileID.canonical.x;
|
|
5569
5849
|
const dy = borderTile.tileID.canonical.y - tile.tileID.canonical.y;
|
|
5570
5850
|
const dim = Math.pow(2, tile.tileID.canonical.z);
|
|
@@ -6285,6 +6565,30 @@ var PathInterpolator = class {
|
|
|
6285
6565
|
}
|
|
6286
6566
|
};
|
|
6287
6567
|
//#endregion
|
|
6568
|
+
//#region src/symbol/symbol_elevation.ts
|
|
6569
|
+
/**
|
|
6570
|
+
* Resolves the elevation of a symbol at a tile coordinate, combining the terrain elevation with the
|
|
6571
|
+
* symbol's `symbol-height-offset`. `symbol-height-anchor` selects the datum the offset is measured
|
|
6572
|
+
* from: the terrain surface below the symbol (`ground`) or the zero elevation datum (`absolute`),
|
|
6573
|
+
* in which case the terrain below the symbol is ignored.
|
|
6574
|
+
*
|
|
6575
|
+
* Call this where the elevation is needed, so that `getElevation` keeps returning the plain terrain
|
|
6576
|
+
* elevation and never has to be substituted with a wrapper.
|
|
6577
|
+
*
|
|
6578
|
+
* @param getElevation - the terrain elevation function, absent when the map has no terrain
|
|
6579
|
+
* @param x - the tile x coordinate to evaluate the elevation at
|
|
6580
|
+
* @param y - the tile y coordinate to evaluate the elevation at
|
|
6581
|
+
* @param heightOffset - the evaluated `symbol-height-offset` of the symbol, in meters
|
|
6582
|
+
* @param heightAnchorGround - whether `symbol-height-anchor` is `ground`
|
|
6583
|
+
* @returns the elevation in meters, or undefined when the symbol sits at the zero elevation datum,
|
|
6584
|
+
* which lets the projection take its fast path that ignores the z coordinate
|
|
6585
|
+
*/
|
|
6586
|
+
function getSymbolElevation(getElevation, x, y, heightOffset, heightAnchorGround) {
|
|
6587
|
+
if (!heightAnchorGround) return heightOffset;
|
|
6588
|
+
if (!getElevation) return heightOffset !== 0 ? heightOffset : void 0;
|
|
6589
|
+
return getElevation(x, y) + heightOffset;
|
|
6590
|
+
}
|
|
6591
|
+
//#endregion
|
|
6288
6592
|
//#region src/symbol/grid_index.ts
|
|
6289
6593
|
function overlapAllowed(overlapA, overlapB) {
|
|
6290
6594
|
let allowed = true;
|
|
@@ -6678,16 +6982,22 @@ function getTileSkewVectors(transform) {
|
|
|
6678
6982
|
};
|
|
6679
6983
|
}
|
|
6680
6984
|
/**
|
|
6985
|
+
* Resolves the elevation of the symbol described by `projectionContext` at a tile coordinate.
|
|
6986
|
+
*/
|
|
6987
|
+
function elevationAt(projectionContext, x, y) {
|
|
6988
|
+
return getSymbolElevation(projectionContext.getElevation, x, y, projectionContext.heightOffset ?? 0, projectionContext.heightAnchorGround ?? true);
|
|
6989
|
+
}
|
|
6990
|
+
/**
|
|
6681
6991
|
* Projects a point using a specified matrix, including the perspective divide.
|
|
6682
|
-
* Uses a fast path if `
|
|
6992
|
+
* Uses a fast path if `elevation` is undefined.
|
|
6683
6993
|
*/
|
|
6684
|
-
function projectWithMatrix(x, y, matrix,
|
|
6994
|
+
function projectWithMatrix(x, y, matrix, elevation) {
|
|
6685
6995
|
let pos;
|
|
6686
|
-
if (
|
|
6996
|
+
if (elevation != null) {
|
|
6687
6997
|
pos = [
|
|
6688
6998
|
x,
|
|
6689
6999
|
y,
|
|
6690
|
-
|
|
7000
|
+
elevation,
|
|
6691
7001
|
1
|
|
6692
7002
|
];
|
|
6693
7003
|
transformMat4(pos, pos, matrix);
|
|
@@ -6901,10 +7211,10 @@ function projectTileCoordinatesToLabelPlane(x, y, projectionContext) {
|
|
|
6901
7211
|
const translatedY = y + projectionContext.translation[1];
|
|
6902
7212
|
let projection;
|
|
6903
7213
|
if (projectionContext.pitchWithMap) {
|
|
6904
|
-
projection = projectWithMatrix(translatedX, translatedY, projectionContext.pitchedLabelPlaneMatrix, projectionContext
|
|
7214
|
+
projection = projectWithMatrix(translatedX, translatedY, projectionContext.pitchedLabelPlaneMatrix, elevationAt(projectionContext, translatedX, translatedY));
|
|
6905
7215
|
projection.isOccluded = false;
|
|
6906
7216
|
} else {
|
|
6907
|
-
projection = projectionContext.transform.projectTileCoordinates(translatedX, translatedY, projectionContext.unwrappedTileID, projectionContext
|
|
7217
|
+
projection = projectionContext.transform.projectTileCoordinates(translatedX, translatedY, projectionContext.unwrappedTileID, elevationAt(projectionContext, translatedX, translatedY));
|
|
6908
7218
|
projection.point.x = (projection.point.x * .5 + .5) * projectionContext.width;
|
|
6909
7219
|
projection.point.y = (-projection.point.y * .5 + .5) * projectionContext.height;
|
|
6910
7220
|
}
|
|
@@ -6919,7 +7229,9 @@ function projectFromLabelPlaneToClipSpace(x, y, projectionContext, pitchedLabelP
|
|
|
6919
7229
|
1
|
|
6920
7230
|
];
|
|
6921
7231
|
transformMat4(pos, pos, pitchedLabelPlaneMatrixInverse);
|
|
6922
|
-
|
|
7232
|
+
const tileX = pos[0] / pos[3];
|
|
7233
|
+
const tileY = pos[1] / pos[3];
|
|
7234
|
+
return projectionContext.transform.projectTileCoordinates(tileX, tileY, projectionContext.unwrappedTileID, elevationAt(projectionContext, tileX, tileY)).point;
|
|
6923
7235
|
} else return {
|
|
6924
7236
|
x: x / projectionContext.width * 2 - 1,
|
|
6925
7237
|
y: 1 - y / projectionContext.height * 2
|
|
@@ -6929,7 +7241,7 @@ function projectFromLabelPlaneToClipSpace(x, y, projectionContext, pitchedLabelP
|
|
|
6929
7241
|
* Projects the given point in tile coordinates to the GL clip space (-1..1).
|
|
6930
7242
|
*/
|
|
6931
7243
|
function projectTileCoordinatesToClipSpace(x, y, projectionContext) {
|
|
6932
|
-
return projectionContext.transform.projectTileCoordinates(x, y, projectionContext.unwrappedTileID, projectionContext
|
|
7244
|
+
return projectionContext.transform.projectTileCoordinates(x, y, projectionContext.unwrappedTileID, elevationAt(projectionContext, x, y));
|
|
6933
7245
|
}
|
|
6934
7246
|
/**
|
|
6935
7247
|
* Calculate the normal vector for a line segment
|
|
@@ -7070,8 +7382,8 @@ function projectPathSpecialProjection(projectedPath, projectionContext) {
|
|
|
7070
7382
|
const inverseLabelPlaneMatrix = tmpMat4;
|
|
7071
7383
|
fastInvertSkewMat4(inverseLabelPlaneMatrix, projectionContext.pitchedLabelPlaneMatrix);
|
|
7072
7384
|
return projectedPath.map((p) => {
|
|
7073
|
-
const backProjected = projectWithMatrix(p.x, p.y, inverseLabelPlaneMatrix, projectionContext.
|
|
7074
|
-
const projected = projectionContext.transform.projectTileCoordinates(backProjected.point.x, backProjected.point.y, projectionContext.unwrappedTileID, projectionContext.
|
|
7385
|
+
const backProjected = projectWithMatrix(p.x, p.y, inverseLabelPlaneMatrix, elevationAt(projectionContext, p.x, p.y));
|
|
7386
|
+
const projected = projectionContext.transform.projectTileCoordinates(backProjected.point.x, backProjected.point.y, projectionContext.unwrappedTileID, elevationAt(projectionContext, backProjected.point.x, backProjected.point.y));
|
|
7075
7387
|
projected.point.x = (projected.point.x * .5 + .5) * projectionContext.width;
|
|
7076
7388
|
projected.point.y = (-projected.point.y * .5 + .5) * projectionContext.height;
|
|
7077
7389
|
return projected;
|
|
@@ -7122,10 +7434,10 @@ var CollisionIndex = class {
|
|
|
7122
7434
|
this.gridBottomBoundary = transform.height + 200;
|
|
7123
7435
|
this.perspectiveRatioCutoff = .6;
|
|
7124
7436
|
}
|
|
7125
|
-
placeCollisionBox(collisionBox, overlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translation, collisionGroupPredicate, getElevation, shift, simpleProjectionMatrix) {
|
|
7437
|
+
placeCollisionBox(collisionBox, overlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translation, collisionGroupPredicate, getElevation, shift, simpleProjectionMatrix, heightOffset = 0, heightAnchorGround = true) {
|
|
7126
7438
|
const x = collisionBox.anchorPointX + translation[0];
|
|
7127
7439
|
const y = collisionBox.anchorPointY + translation[1];
|
|
7128
|
-
const projectedPoint = this.projectAndGetPerspectiveRatio(x, y, unwrappedTileID, getElevation, simpleProjectionMatrix);
|
|
7440
|
+
const projectedPoint = this.projectAndGetPerspectiveRatio(x, y, unwrappedTileID, getSymbolElevation(getElevation, x, y, heightOffset, heightAnchorGround), simpleProjectionMatrix);
|
|
7129
7441
|
const tileToViewport = textPixelRatio * projectedPoint.perspectiveRatio;
|
|
7130
7442
|
let projectedBox;
|
|
7131
7443
|
if (!pitchWithMap && !rotateWithMap) {
|
|
@@ -7140,7 +7452,7 @@ var CollisionIndex = class {
|
|
|
7140
7452
|
pointY + collisionBox.y2 * tileToViewport
|
|
7141
7453
|
]
|
|
7142
7454
|
};
|
|
7143
|
-
} else projectedBox = this._projectCollisionBox(collisionBox, tileToViewport, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translation, projectedPoint, getElevation, shift, simpleProjectionMatrix);
|
|
7455
|
+
} else projectedBox = this._projectCollisionBox(collisionBox, tileToViewport, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translation, projectedPoint, getElevation, shift, simpleProjectionMatrix, heightOffset, heightAnchorGround);
|
|
7144
7456
|
const [tlX, tlY, brX, brY] = projectedBox.box;
|
|
7145
7457
|
const occluded = pitchWithMap ? projectedBox.allPointsOccluded : projectedPoint.isOccluded;
|
|
7146
7458
|
let unplaceable = occluded;
|
|
@@ -7172,7 +7484,7 @@ var CollisionIndex = class {
|
|
|
7172
7484
|
placeCollisionCircles(overlapMode, symbol, lineVertexArray, glyphOffsetArray, fontSize, unwrappedTileID, pitchedLabelPlaneMatrix, showCollisionCircles, pitchWithMap, collisionGroupPredicate, circlePixelDiameter, textPixelPadding, translation, getElevation) {
|
|
7173
7485
|
const placedCollisionCircles = [];
|
|
7174
7486
|
const tileUnitAnchorPoint = new Point(symbol.anchorX, symbol.anchorY);
|
|
7175
|
-
const perspectiveRatio = this.getPerspectiveRatio(tileUnitAnchorPoint.x, tileUnitAnchorPoint.y, unwrappedTileID, getElevation);
|
|
7487
|
+
const perspectiveRatio = this.getPerspectiveRatio(tileUnitAnchorPoint.x, tileUnitAnchorPoint.y, unwrappedTileID, getElevation?.(tileUnitAnchorPoint.x, tileUnitAnchorPoint.y));
|
|
7176
7488
|
const labelPlaneFontScale = (pitchWithMap ? fontSize * this.transform.getPitchedTextCorrection(symbol.anchorX, symbol.anchorY, unwrappedTileID) / perspectiveRatio : fontSize * perspectiveRatio) / 24;
|
|
7177
7489
|
const projectionCache = {
|
|
7178
7490
|
projections: {},
|
|
@@ -7321,14 +7633,14 @@ var CollisionIndex = class {
|
|
|
7321
7633
|
};
|
|
7322
7634
|
for (let k = 0; k < collisionCircles.length; k += 4) grid.insertCircle(key, collisionCircles[k], collisionCircles[k + 1], collisionCircles[k + 2]);
|
|
7323
7635
|
}
|
|
7324
|
-
projectAndGetPerspectiveRatio(x, y, unwrappedTileID,
|
|
7636
|
+
projectAndGetPerspectiveRatio(x, y, unwrappedTileID, elevation, simpleProjectionMatrix) {
|
|
7325
7637
|
if (simpleProjectionMatrix) {
|
|
7326
7638
|
let pos;
|
|
7327
|
-
if (
|
|
7639
|
+
if (elevation != null) {
|
|
7328
7640
|
pos = [
|
|
7329
7641
|
x,
|
|
7330
7642
|
y,
|
|
7331
|
-
|
|
7643
|
+
elevation,
|
|
7332
7644
|
1
|
|
7333
7645
|
];
|
|
7334
7646
|
transformMat4(pos, pos, simpleProjectionMatrix);
|
|
@@ -7350,7 +7662,7 @@ var CollisionIndex = class {
|
|
|
7350
7662
|
signedDistanceFromCamera: w
|
|
7351
7663
|
};
|
|
7352
7664
|
} else {
|
|
7353
|
-
const projected = this.transform.projectTileCoordinates(x, y, unwrappedTileID,
|
|
7665
|
+
const projected = this.transform.projectTileCoordinates(x, y, unwrappedTileID, elevation);
|
|
7354
7666
|
return {
|
|
7355
7667
|
x: (projected.point.x + 1) / 2 * this.transform.width + 100,
|
|
7356
7668
|
y: (-projected.point.y + 1) / 2 * this.transform.height + 100,
|
|
@@ -7360,8 +7672,8 @@ var CollisionIndex = class {
|
|
|
7360
7672
|
};
|
|
7361
7673
|
}
|
|
7362
7674
|
}
|
|
7363
|
-
getPerspectiveRatio(x, y, unwrappedTileID,
|
|
7364
|
-
const projected = this.transform.projectTileCoordinates(x, y, unwrappedTileID,
|
|
7675
|
+
getPerspectiveRatio(x, y, unwrappedTileID, elevation) {
|
|
7676
|
+
const projected = this.transform.projectTileCoordinates(x, y, unwrappedTileID, elevation);
|
|
7365
7677
|
return .5 + .5 * (this.transform.cameraToCenterDistance / projected.signedDistanceFromCamera);
|
|
7366
7678
|
}
|
|
7367
7679
|
isOffscreen(x1, y1, x2, y2) {
|
|
@@ -7382,7 +7694,7 @@ var CollisionIndex = class {
|
|
|
7382
7694
|
/**
|
|
7383
7695
|
* Applies all layout+paint properties of the given box in order to find as good approximation of its screen-space bounding box as possible.
|
|
7384
7696
|
*/
|
|
7385
|
-
_projectCollisionBox(collisionBox, tileToViewport, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translation, projectedPoint, getElevation, shift, simpleProjectionMatrix) {
|
|
7697
|
+
_projectCollisionBox(collisionBox, tileToViewport, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translation, projectedPoint, getElevation, shift, simpleProjectionMatrix, heightOffset = 0, heightAnchorGround = true) {
|
|
7386
7698
|
let vecEastX = 1;
|
|
7387
7699
|
let vecEastY = 0;
|
|
7388
7700
|
let vecSouthX = 0;
|
|
@@ -7390,7 +7702,7 @@ var CollisionIndex = class {
|
|
|
7390
7702
|
const translatedAnchorX = collisionBox.anchorPointX + translation[0];
|
|
7391
7703
|
const translatedAnchorY = collisionBox.anchorPointY + translation[1];
|
|
7392
7704
|
if (rotateWithMap && !pitchWithMap) {
|
|
7393
|
-
const projectedEast = this.projectAndGetPerspectiveRatio(translatedAnchorX + 1, translatedAnchorY, unwrappedTileID, getElevation, simpleProjectionMatrix);
|
|
7705
|
+
const projectedEast = this.projectAndGetPerspectiveRatio(translatedAnchorX + 1, translatedAnchorY, unwrappedTileID, getSymbolElevation(getElevation, translatedAnchorX + 1, translatedAnchorY, heightOffset, heightAnchorGround), simpleProjectionMatrix);
|
|
7394
7706
|
const toEastX = projectedEast.x - projectedPoint.x;
|
|
7395
7707
|
const toEastY = projectedEast.y - projectedPoint.y;
|
|
7396
7708
|
const angle = Math.atan(toEastY / toEastX) + (toEastX < 0 ? Math.PI : 0);
|
|
@@ -7470,7 +7782,7 @@ var CollisionIndex = class {
|
|
|
7470
7782
|
for (const { offsetX, offsetY } of offsetsArray) points.push(new Point(basePointX + vecEastX * offsetX + vecSouthX * offsetY, basePointY + vecEastY * offsetX + vecSouthY * offsetY));
|
|
7471
7783
|
let anyPointVisible = false;
|
|
7472
7784
|
if (pitchWithMap) {
|
|
7473
|
-
const projected = points.map((p) => this.projectAndGetPerspectiveRatio(p.x, p.y, unwrappedTileID, getElevation, simpleProjectionMatrix));
|
|
7785
|
+
const projected = points.map((p) => this.projectAndGetPerspectiveRatio(p.x, p.y, unwrappedTileID, getSymbolElevation(getElevation, p.x, p.y, heightOffset, heightAnchorGround), simpleProjectionMatrix));
|
|
7474
7786
|
anyPointVisible = projected.some((p) => !p.isOccluded);
|
|
7475
7787
|
points = projected.map((p) => new Point(p.x, p.y));
|
|
7476
7788
|
} else anyPointVisible = true;
|
|
@@ -7568,7 +7880,7 @@ var Placement = class {
|
|
|
7568
7880
|
}
|
|
7569
7881
|
_getTerrainElevationFunc(tileID) {
|
|
7570
7882
|
const terrain = this.terrain;
|
|
7571
|
-
if (!terrain) return
|
|
7883
|
+
if (!terrain) return void 0;
|
|
7572
7884
|
return (x, y) => terrain.getElevation(tileID, x, y);
|
|
7573
7885
|
}
|
|
7574
7886
|
getBucketParts(results, styleLayer, tile, sortAcrossTiles) {
|
|
@@ -7616,13 +7928,13 @@ var Placement = class {
|
|
|
7616
7928
|
parameters
|
|
7617
7929
|
});
|
|
7618
7930
|
}
|
|
7619
|
-
attemptAnchorPlacement(textAnchorOffset, textBox, width, height, textBoxScale, rotateWithMap, pitchWithMap, textPixelRatio, tileID, unwrappedTileID, collisionGroup, textOverlapMode, symbolInstance, bucket, orientation, translationText, translationIcon, iconBox, getElevation, simpleProjectionMatrix) {
|
|
7931
|
+
attemptAnchorPlacement(textAnchorOffset, textBox, width, height, textBoxScale, rotateWithMap, pitchWithMap, textPixelRatio, tileID, unwrappedTileID, collisionGroup, textOverlapMode, symbolInstance, bucket, orientation, translationText, translationIcon, iconBox, getElevation, simpleProjectionMatrix, heightOffset, heightAnchorGround) {
|
|
7620
7932
|
const anchor = TextAnchorEnum[textAnchorOffset.textAnchor];
|
|
7621
7933
|
const textOffset = [textAnchorOffset.textOffset0, textAnchorOffset.textOffset1];
|
|
7622
7934
|
const shift = calculateVariableLayoutShift(anchor, width, height, textOffset, textBoxScale);
|
|
7623
|
-
const placedGlyphBoxes = this.collisionIndex.placeCollisionBox(textBox, textOverlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationText, collisionGroup.predicate, getElevation, shift, simpleProjectionMatrix);
|
|
7935
|
+
const placedGlyphBoxes = this.collisionIndex.placeCollisionBox(textBox, textOverlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationText, collisionGroup.predicate, getElevation, shift, simpleProjectionMatrix, heightOffset, heightAnchorGround);
|
|
7624
7936
|
if (iconBox) {
|
|
7625
|
-
if (!this.collisionIndex.placeCollisionBox(iconBox, textOverlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationIcon, collisionGroup.predicate, getElevation, shift, simpleProjectionMatrix).placeable) return;
|
|
7937
|
+
if (!this.collisionIndex.placeCollisionBox(iconBox, textOverlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationIcon, collisionGroup.predicate, getElevation, shift, simpleProjectionMatrix, heightOffset, heightAnchorGround).placeable) return;
|
|
7626
7938
|
}
|
|
7627
7939
|
if (placedGlyphBoxes.placeable) {
|
|
7628
7940
|
let prevAnchor;
|
|
@@ -7659,6 +7971,7 @@ var Placement = class {
|
|
|
7659
7971
|
const pitchWithMap = layout.get("text-pitch-alignment") === "map";
|
|
7660
7972
|
const hasIconTextFit = layout.get("icon-text-fit") !== "none";
|
|
7661
7973
|
const zOrderByViewportY = layout.get("symbol-z-order") === "viewport-y";
|
|
7974
|
+
const heightAnchorGround = layout.get("symbol-height-anchor") === "ground";
|
|
7662
7975
|
const alwaysShowText = textAlwaysOverlap && (iconAlwaysOverlap || !bucket.hasIconData() || iconOptional);
|
|
7663
7976
|
const alwaysShowIcon = iconAlwaysOverlap && (textAlwaysOverlap || !bucket.hasTextData() || textOptional);
|
|
7664
7977
|
if (!bucket.collisionArrays && collisionBoxArray) bucket.deserializeCollisionBoxes(collisionBoxArray);
|
|
@@ -7671,6 +7984,7 @@ var Placement = class {
|
|
|
7671
7984
|
this.placements[symbolInstance.crossTileID] = new JointPlacement(false, false, false);
|
|
7672
7985
|
return;
|
|
7673
7986
|
}
|
|
7987
|
+
const symbolHeightOffset = symbolInstance.heightOffset;
|
|
7674
7988
|
let placeText = false;
|
|
7675
7989
|
let placeIcon = false;
|
|
7676
7990
|
let offscreen = true;
|
|
@@ -7723,7 +8037,7 @@ var Placement = class {
|
|
|
7723
8037
|
const textAnchorOffsetEnd = symbolInstance.textAnchorOffsetEndIndex;
|
|
7724
8038
|
if (textAnchorOffsetEnd === textAnchorOffsetStart) {
|
|
7725
8039
|
const placeBox = (collisionTextBox, orientation) => {
|
|
7726
|
-
const placedFeature = this.collisionIndex.placeCollisionBox(collisionTextBox, textOverlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationText, collisionGroup.predicate, getElevation, void 0, simpleProjectionMatrix);
|
|
8040
|
+
const placedFeature = this.collisionIndex.placeCollisionBox(collisionTextBox, textOverlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationText, collisionGroup.predicate, getElevation, void 0, simpleProjectionMatrix, symbolHeightOffset, heightAnchorGround);
|
|
7727
8041
|
if (placedFeature?.placeable) {
|
|
7728
8042
|
this.markUsedOrientation(bucket, orientation, symbolInstance);
|
|
7729
8043
|
this.placedOrientations[symbolInstance.crossTileID] = orientation;
|
|
@@ -7758,7 +8072,7 @@ var Placement = class {
|
|
|
7758
8072
|
for (let i = textAnchorOffsetStart; i < textAnchorOffsetEnd; i++) {
|
|
7759
8073
|
const textAnchorOffset = bucket.textAnchorOffsets.get(i);
|
|
7760
8074
|
if (prevAnchor && textAnchorOffset.textAnchor !== prevAnchor) continue;
|
|
7761
|
-
const result = this.attemptAnchorPlacement(textAnchorOffset, collisionTextBox, width, height, textBoxScale, rotateWithMap, pitchWithMap, textPixelRatio, tileID, unwrappedTileID, collisionGroup, overlapMode, symbolInstance, bucket, orientation, translationText, translationIcon, variableIconBox, getElevation);
|
|
8075
|
+
const result = this.attemptAnchorPlacement(textAnchorOffset, collisionTextBox, width, height, textBoxScale, rotateWithMap, pitchWithMap, textPixelRatio, tileID, unwrappedTileID, collisionGroup, overlapMode, symbolInstance, bucket, orientation, translationText, translationIcon, variableIconBox, getElevation, simpleProjectionMatrix, symbolHeightOffset, heightAnchorGround);
|
|
7762
8076
|
if (result) {
|
|
7763
8077
|
placedBox = result.placedGlyphBoxes;
|
|
7764
8078
|
if (placedBox?.placeable) {
|
|
@@ -7772,7 +8086,7 @@ var Placement = class {
|
|
|
7772
8086
|
else overlapMode = textOverlapMode;
|
|
7773
8087
|
}
|
|
7774
8088
|
if (showCollisionBoxes && !placedBox) placedBox = {
|
|
7775
|
-
box: this.collisionIndex.placeCollisionBox(textBox, "always", textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationText, collisionGroup.predicate, getElevation, void 0, simpleProjectionMatrix).box,
|
|
8089
|
+
box: this.collisionIndex.placeCollisionBox(textBox, "always", textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationText, collisionGroup.predicate, getElevation, void 0, simpleProjectionMatrix, symbolHeightOffset, heightAnchorGround).box,
|
|
7776
8090
|
offscreen: false,
|
|
7777
8091
|
placeable: false,
|
|
7778
8092
|
occluded: false
|
|
@@ -7823,7 +8137,7 @@ var Placement = class {
|
|
|
7823
8137
|
if (collisionArrays.iconFeatureIndex) iconFeatureIndex = collisionArrays.iconFeatureIndex;
|
|
7824
8138
|
if (collisionArrays.iconBox) {
|
|
7825
8139
|
const placeIconFeature = (iconBox) => {
|
|
7826
|
-
return this.collisionIndex.placeCollisionBox(iconBox, iconOverlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationIcon, collisionGroup.predicate, getElevation, hasIconTextFit && shift ? shift : void 0, simpleProjectionMatrix);
|
|
8140
|
+
return this.collisionIndex.placeCollisionBox(iconBox, iconOverlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationIcon, collisionGroup.predicate, getElevation, hasIconTextFit && shift ? shift : void 0, simpleProjectionMatrix, symbolHeightOffset, heightAnchorGround);
|
|
7827
8141
|
};
|
|
7828
8142
|
if (placedVerticalText && placedVerticalText.placeable && collisionArrays.verticalIconBox) {
|
|
7829
8143
|
placedIconBoxes = placeIconFeature(collisionArrays.verticalIconBox);
|
|
@@ -8778,12 +9092,11 @@ const shaders = {
|
|
|
8778
9092
|
lineGradientSDF: prepare("uniform lowp float u_device_pixel_ratio;uniform sampler2D u_image;uniform sampler2D u_image_dash;uniform float u_mix;uniform lowp float u_lineatlas_width;in vec2 v_normal;flat in vec2 v_width2;in vec2 v_tex_a;in vec2 v_tex_b;in float v_gamma_scale;in highp vec2 v_uv;\n#ifdef GLOBE\nin float v_depth;\n#endif\n#pragma maplibre: define lowp float blur\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define mediump float width\n#pragma maplibre: define lowp float floorwidth\n#pragma maplibre: define mediump vec4 dasharray_from\n#pragma maplibre: define mediump vec4 dasharray_to\nvoid main() {\n#pragma maplibre: initialize lowp float blur\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize mediump float width\n#pragma maplibre: initialize lowp float floorwidth\n#pragma maplibre: initialize mediump vec4 dasharray_from\n#pragma maplibre: initialize mediump vec4 dasharray_to\nclipAntimeridian();float dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);vec4 color=texture(u_image,v_uv);float sdfdist_a=texture(u_image_dash,v_tex_a).a;float sdfdist_b=texture(u_image_dash,v_tex_b).a;float sdfdist=mix(sdfdist_a,sdfdist_b,u_mix);float sdfgamma=(u_lineatlas_width/256.0)/min(dasharray_from.w,dasharray_to.w);float dash_alpha=smoothstep(0.5-sdfgamma/floorwidth,0.5+sdfgamma/floorwidth,sdfdist);fragColor=color*(alpha*dash_alpha*opacity);\n#ifdef GLOBE\nif (v_depth > 1.0) {discard;}\n#endif\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "\n#define scale 0.015873016\n#define LINE_DISTANCE_SCALE 2.0\nlayout(location=0) in ivec2 a_pos_normal;layout(location=1) in uvec4 a_data;layout(location=2) in float a_uv_x;layout(location=3) in float a_split_index;uniform vec2 u_translation;uniform mediump float u_ratio;uniform lowp float u_device_pixel_ratio;uniform vec2 u_units_to_pixels;uniform float u_image_height;uniform float u_tileratio;uniform float u_crossfade_from;uniform float u_crossfade_to;uniform float u_lineatlas_height;out vec2 v_normal;flat out vec2 v_width2;out float v_gamma_scale;out highp vec2 v_uv;out vec2 v_tex_a;out vec2 v_tex_b;\n#ifdef GLOBE\nout float v_depth;\n#endif\n#pragma maplibre: define lowp float blur\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define mediump float gapwidth\n#pragma maplibre: define lowp float offset\n#pragma maplibre: define mediump float width\n#pragma maplibre: define lowp float floorwidth\n#pragma maplibre: define mediump vec4 dasharray_from\n#pragma maplibre: define mediump vec4 dasharray_to\nvoid main() {\n#pragma maplibre: initialize lowp float blur\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize mediump float gapwidth\n#pragma maplibre: initialize lowp float offset\n#pragma maplibre: initialize mediump float width\n#pragma maplibre: initialize lowp float floorwidth\n#pragma maplibre: initialize mediump vec4 dasharray_from\n#pragma maplibre: initialize mediump vec4 dasharray_to\nif (opacity < 0.01) {gl_Position=vec4(-2.0,-2.0,-2.0,1.0);return;}float ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=vec2(ivec2(a_data.xy)-128);float a_direction=float(int(a_data.z & 3u)-1);float a_linesofar=float((a_data.z >> 2u)+a_data.w*64u)*LINE_DISTANCE_SCALE;float texel_height=1.0/u_image_height;float half_texel_height=0.5*texel_height;v_uv=vec2(a_uv_x,a_split_index*texel_height-half_texel_height);vec2 pos=vec2(a_pos_normal >> 1);mediump vec2 normal=vec2(a_pos_normal & 1);normal.y=normal.y*2.0-1.0;v_normal=normal;gapwidth=gapwidth/2.0;float halfwidth=width/2.0;offset=-1.0*offset;float inset=gapwidth+(gapwidth > 0.0 ? ANTIALIASING : 0.0);float outset=gapwidth+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*scale;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*scale*normal.y*mat2(t,-u,u,t);float adjustedThickness=projectLineThickness(pos.y);vec4 projected_no_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation);vec4 projected_with_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation+dist/u_ratio*adjustedThickness);gl_Position=projected_with_extrude;\n#ifdef GLOBE\nv_depth=gl_Position.z/gl_Position.w;\n#endif\n#ifdef TERRAIN3D\nv_gamma_scale=1.0;\n#else\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length((projected_with_extrude.xy-projected_no_extrude.xy)/projected_with_extrude.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\n#endif\nfloat u_patternscale_a_x=u_tileratio/dasharray_from.w/u_crossfade_from;float u_patternscale_a_y=-dasharray_from.z/2.0/u_lineatlas_height;float u_patternscale_b_x=u_tileratio/dasharray_to.w/u_crossfade_to;float u_patternscale_b_y=-dasharray_to.z/2.0/u_lineatlas_height;v_tex_a=vec2(a_linesofar*u_patternscale_a_x/floorwidth,normal.y*u_patternscale_a_y+(float(dasharray_from.y)+0.5)/u_lineatlas_height);v_tex_b=vec2(a_linesofar*u_patternscale_b_x/floorwidth,normal.y*u_patternscale_b_y+(float(dasharray_to.y)+0.5)/u_lineatlas_height);v_width2=vec2(outset,inset);}"),
|
|
8779
9093
|
layerOpacity: prepare("uniform sampler2D u_image;uniform float u_opacity;in vec2 v_pos;void main() {fragColor=texture(u_image,v_pos)*u_opacity;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(0.0);\n#endif\n}", "layout(location=0) in vec2 a_pos;out vec2 v_pos;void main() {gl_Position=vec4(a_pos.x*2.0-1.0,1.0-a_pos.y*2.0,0.0,1.0);v_pos.x=a_pos.x;v_pos.y=1.0-a_pos.y;}"),
|
|
8780
9094
|
raster: prepare("uniform float u_fade_t;uniform float u_opacity;uniform sampler2D u_image0;uniform sampler2D u_image1;in vec3 v_pos0;in vec3 v_pos1;uniform float u_brightness_low;uniform float u_brightness_high;uniform float u_saturation_factor;uniform float u_contrast_factor;uniform vec3 u_spin_weights;void main() {vec4 color0=texture(u_image0,v_pos0.xy/v_pos0.z);vec4 color1=texture(u_image1,v_pos1.xy/v_pos1.z);if (color0.a > 0.0) {color0.rgb=color0.rgb/color0.a;}if (color1.a > 0.0) {color1.rgb=color1.rgb/color1.a;}vec4 color=mix(color0,color1,u_fade_t);color.a*=u_opacity;vec3 rgb=color.rgb;rgb=vec3(dot(rgb,u_spin_weights.xyz),dot(rgb,u_spin_weights.zxy),dot(rgb,u_spin_weights.yzx));float average=(color.r+color.g+color.b)/3.0;rgb+=(average-rgb)*u_saturation_factor;rgb=(rgb-0.5)*u_contrast_factor+0.5;vec3 u_high_vec=vec3(u_brightness_low,u_brightness_low,u_brightness_low);vec3 u_low_vec=vec3(u_brightness_high,u_brightness_high,u_brightness_high);fragColor=vec4(mix(u_high_vec,u_low_vec,rgb)*color.a,color.a);\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "uniform vec2 u_tl_parent;uniform float u_scale_parent;uniform float u_buffer_scale;uniform vec3 u_image_warp;uniform vec4 u_coords_top;uniform vec4 u_coords_bottom;layout(location=0) in vec2 a_pos;out vec3 v_pos0;out vec3 v_pos1;void main() {vec2 fractionalPos=a_pos/8192.0;vec2 topLeft=u_coords_top.xy;vec2 topRight=u_coords_top.zw;vec2 bottomLeft=u_coords_bottom.xy;vec2 bottomRight=u_coords_bottom.zw;vec2 bilinearPos=mix(mix(topLeft,topRight,fractionalPos.x),mix(bottomLeft,bottomRight,fractionalPos.x),fractionalPos.y);float denominator=dot(u_image_warp.xy,fractionalPos)+1.0;vec2 acrossTop=topRight-topLeft+u_image_warp.x*topRight;vec2 downLeft=bottomLeft-topLeft+u_image_warp.y*bottomLeft;vec2 projectivePos=(acrossTop*fractionalPos.x+downLeft*fractionalPos.y+topLeft)/denominator;vec2 position=mix(projectivePos,bilinearPos,u_image_warp.z);gl_Position=projectTile(position,position);vec2 texturePos=((fractionalPos-0.5)/u_buffer_scale)+0.5;\n#ifdef GLOBE\nif (a_pos.y <-32767.5) {texturePos.y=0.0;}if (a_pos.y > 32766.5) {texturePos.y=1.0;}\n#endif\nfloat perspectiveRatio=mix(1.0/denominator,1.0,u_image_warp.z);v_pos0=vec3(texturePos*perspectiveRatio,perspectiveRatio);vec2 parentPos=(texturePos*u_scale_parent)+u_tl_parent;v_pos1=vec3(parentPos*perspectiveRatio,perspectiveRatio);}"),
|
|
8781
|
-
symbolIcon: prepare("uniform sampler2D u_texture;in vec2 v_tex;flat in float v_total_opacity;void main() {fragColor=texture(u_texture,v_tex)*v_total_opacity;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "layout(location=0) in vec4 a_pos_offset;layout(location=1) in uvec4 a_data;layout(location=2) in vec4 a_pixeloffset;layout(location=3) in vec3 a_projected_pos;layout(location=4) in uint a_fade_opacity;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform highp float u_camera_to_center_distance;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform float u_fade_change;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform vec2 u_texsize;uniform bool u_is_along_line;uniform bool u_is_variable_anchor;uniform vec2 u_translation;uniform float u_pitched_scale;uniform bool u_is_offset;out vec2 v_tex;flat out float v_total_opacity;\n#pragma maplibre: define lowp float opacity\nvoid main() {\n#pragma maplibre: initialize lowp float opacity\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=vec2(a_data.xy);vec2 a_size=vec2(a_data.zw);float a_size_min=float(a_data.z >> 1u);vec2 a_pxoffset=a_pixeloffset.xy;vec2 a_minFontScale=a_pixeloffset.zw/256.0;float ele=get_elevation(a_pos);highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec2 translated_a_pos=a_pos+u_translation;vec4 projectedPoint=projectTileWithElevation(translated_a_pos,ele);vec2 fade_opacity=unpack_opacity(a_fade_opacity);float fade_change=fade_opacity[1] > 0.5 ? u_fade_change :-u_fade_change;float visibility=calculate_visibility(projectedPoint);v_total_opacity=opacity*max(0.0,min(visibility,fade_opacity[0]+fade_change));if (v_total_opacity < 0.1){gl_Position=vec4(-2.,-2.,-2.,1.);return;}highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\ncamera_to_anchor_distance/u_camera_to_center_distance :\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(0.5+0.5*distance_ratio,0.0,4.0);if (!u_is_offset) {size*=perspective_ratio;}float fontScale=u_is_text ? size/24.0 : size;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=projectTileWithElevation(translated_a_pos+vec2(1,0),ele);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos;if (u_is_along_line || u_is_variable_anchor) {projected_pos=vec4(a_projected_pos.xy,ele,1.0);} else if (u_pitch_with_map) {projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy+u_translation,ele,1.0);} else {projected_pos=u_label_plane_matrix*projectTileWithElevation(a_projected_pos.xy+u_translation,ele);}float z=float(u_pitch_with_map)*projected_pos.z/projected_pos.w;float projectionScaling=1.0;\n#ifdef GLOBE\nif(u_pitch_with_map) {float anchor_pos_tile_y=(u_coord_matrix*vec4(projected_pos.xy/projected_pos.w,z,1.0)).y;projectionScaling=mix(projectionScaling,1.0/circumferenceRatioAtTileY(anchor_pos_tile_y)*u_pitched_scale,u_projection_transition);}\n#endif\nvec4 finalPos=u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+rotation_matrix*(a_offset/32.0*max(a_minFontScale,fontScale)+a_pxoffset/16.0)*projectionScaling,z,1.0);if(u_pitch_with_map) {finalPos=projectTileWithElevation(finalPos.xy,finalPos.z);}gl_Position=finalPos;v_tex=a_tex/u_texsize;}"),
|
|
8782
|
-
symbolSDF: prepare("#define SDF_PX 8.0\nuniform bool u_is_halo;uniform bool u_is_plain;uniform sampler2D u_texture;uniform highp float u_gamma_scale;uniform lowp float u_device_pixel_ratio;uniform bool u_is_text;in vec2 v_data0;in vec3 v_data1;\n#pragma maplibre: define highp vec4 fill_color\n#pragma maplibre: define highp vec4 halo_color\n#pragma maplibre: define lowp float halo_width\n#pragma maplibre: define lowp float halo_blur\nvoid main() {\n#pragma maplibre: initialize highp vec4 fill_color\n#pragma maplibre: initialize highp vec4 halo_color\n#pragma maplibre: initialize lowp float halo_width\n#pragma maplibre: initialize lowp float halo_blur\nfloat EDGE_GAMMA=0.105/u_device_pixel_ratio;vec2 tex=v_data0.xy;float gamma_scale=v_data1.x;float size=v_data1.y;float total_opacity=v_data1[2];float fontScale=u_is_text ? size/24.0 : size;highp float gamma=EDGE_GAMMA/(fontScale*u_gamma_scale);lowp float inner_edge=(256.0-64.0)/256.0;lowp float dist=texture(u_texture,tex).a;lowp vec4 color_alpha_out_text,color_alpha_out_halo;if (u_is_plain){highp float gamma_scaled=gamma*gamma_scale;highp float alpha=smoothstep(inner_edge-gamma_scaled,inner_edge+gamma_scaled,dist);color_alpha_out_text=total_opacity*alpha*fill_color;}if (u_is_halo) {float gamma_halo=(halo_blur*1.19/SDF_PX+EDGE_GAMMA)/(fontScale*u_gamma_scale);float inner_edge_halo=inner_edge+gamma_halo*gamma_scale;highp float gamma_scaled_halo=gamma_halo*gamma_scale;highp float alpha_halo=smoothstep(inner_edge_halo-gamma_scaled_halo,inner_edge_halo+gamma_scaled_halo,dist);highp float halo_edge=(6.0-halo_width/fontScale)/SDF_PX;alpha_halo= min(smoothstep(halo_edge-gamma_scaled_halo,halo_edge+gamma_scaled_halo,dist),1.0-alpha_halo);color_alpha_out_halo=total_opacity*alpha_halo*halo_color;}if (u_is_plain && u_is_halo) {fragColor=color_alpha_out_text+(1.-color_alpha_out_text.a)*color_alpha_out_halo;} else if (u_is_halo){fragColor=color_alpha_out_halo;} else {fragColor=color_alpha_out_text;}\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "layout(location=0) in vec4 a_pos_offset;layout(location=1) in uvec4 a_data;layout(location=2) in vec4 a_pixeloffset;layout(location=3) in vec3 a_projected_pos;layout(location=4) in uint a_fade_opacity;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform bool u_is_along_line;uniform bool u_is_variable_anchor;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform highp float u_camera_to_center_distance;uniform float u_fade_change;uniform vec2 u_texsize;uniform vec2 u_translation;uniform float u_pitched_scale;uniform bool u_is_offset;out vec2 v_data0;out vec3 v_data1;\n#pragma maplibre: define highp vec4 fill_color\n#pragma maplibre: define highp vec4 halo_color\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define lowp float halo_width\n#pragma maplibre: define lowp float halo_blur\nvoid main() {\n#pragma maplibre: initialize highp vec4 fill_color\n#pragma maplibre: initialize highp vec4 halo_color\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize lowp float halo_width\n#pragma maplibre: initialize lowp float halo_blur\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=vec2(a_data.xy);vec2 a_size=vec2(a_data.zw);float a_size_min=float(a_data.z >> 1u);vec2 a_pxoffset=a_pixeloffset.xy/16.0;vec2 a_minFontScale=a_pixeloffset.zw/256.0;float ele=get_elevation(a_pos);highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec2 translated_a_pos=a_pos+u_translation;vec4 projectedPoint=projectTileWithElevation(translated_a_pos,ele);vec2 fade_opacity=unpack_opacity(a_fade_opacity);float visibility=calculate_visibility(projectedPoint);float fade_change=fade_opacity[1] > 0.5 ? u_fade_change :-u_fade_change;float interpolated_fade_opacity=max(0.0,min(visibility,fade_opacity[0]+fade_change));float total_opacity=opacity*interpolated_fade_opacity;if (total_opacity < 0.1){gl_Position=vec4(-2.,-2.,-2.,1.);return;}highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\ncamera_to_anchor_distance/u_camera_to_center_distance :\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(0.5+0.5*distance_ratio,0.0,4.0);if (!u_is_offset) {size*=perspective_ratio;}float fontScale=u_is_text ? size/24.0 : size;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=projectTileWithElevation(translated_a_pos+vec2(1,0),ele);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos;if (u_is_along_line || u_is_variable_anchor) {projected_pos=vec4(a_projected_pos.xy,ele,1.0);} else if (u_pitch_with_map) {projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy+u_translation,ele,1.0);} else {projected_pos=u_label_plane_matrix*projectTileWithElevation(a_projected_pos.xy+u_translation,ele);}float z=float(u_pitch_with_map)*projected_pos.z/projected_pos.w;float projectionScaling=1.0;\n#ifdef GLOBE\nif(u_pitch_with_map) {float anchor_pos_tile_y=(u_coord_matrix*vec4(projected_pos.xy/projected_pos.w,z,1.0)).y;projectionScaling=mix(projectionScaling,1.0/circumferenceRatioAtTileY(anchor_pos_tile_y)*u_pitched_scale,u_projection_transition);}\n#endif\nvec4 finalPos=u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+rotation_matrix*(a_offset/32.0*max(a_minFontScale,fontScale)+a_pxoffset)*projectionScaling,z,1.0);if(u_pitch_with_map) {finalPos=projectTileWithElevation(finalPos.xy,finalPos.z);}float gamma_scale=finalPos.w;gl_Position=finalPos;v_data0=a_tex/u_texsize;v_data1=vec3(gamma_scale,size,total_opacity);}"),
|
|
8783
|
-
symbolTextAndIcon: prepare("#define SDF_PX 8.0\n#define SDF 1.0\n#define ICON 0.0\nuniform bool u_is_halo;uniform bool u_is_text;uniform sampler2D u_texture;uniform sampler2D u_texture_icon;uniform highp float u_gamma_scale;uniform lowp float u_device_pixel_ratio;in vec4 v_data0;in vec3 v_data1;flat in float v_is_sdf;\n#pragma maplibre: define highp vec4 fill_color\n#pragma maplibre: define highp vec4 halo_color\n#pragma maplibre: define lowp float halo_width\n#pragma maplibre: define lowp float halo_blur\nvoid main() {\n#pragma maplibre: initialize highp vec4 fill_color\n#pragma maplibre: initialize highp vec4 halo_color\n#pragma maplibre: initialize lowp float halo_width\n#pragma maplibre: initialize lowp float halo_blur\nfloat total_opacity=v_data1[2];if (v_is_sdf==ICON) {vec2 tex_icon=v_data0.zw;fragColor=texture(u_texture_icon,tex_icon)*total_opacity;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\nreturn;}vec2 tex=v_data0.xy;float EDGE_GAMMA=0.105/u_device_pixel_ratio;float gamma_scale=v_data1.x;float size=v_data1.y;float fontScale=size/24.0;highp float gamma=EDGE_GAMMA/(fontScale*u_gamma_scale);lowp float buff=(256.0-64.0)/256.0;lowp float dist=texture(u_texture,tex).a;lowp vec4 color_alpha_out,color_alpha_out_halo;if (u_is_text) {highp float gamma_scaled=gamma*gamma_scale;highp float alpha=smoothstep(buff-gamma_scaled,buff+gamma_scaled,dist);color_alpha_out=fill_color*(alpha*total_opacity);}if (u_is_halo) {highp float gamma_halo=(halo_blur*1.19/SDF_PX+EDGE_GAMMA)/(fontScale*u_gamma_scale);lowp float buff_halo=(6.0-halo_width/fontScale)/SDF_PX;highp float gamma_scaled_halo=gamma_halo*gamma_scale;highp float alpha_halo=smoothstep(buff_halo-gamma_scaled_halo,buff_halo+gamma_scaled_halo,dist);color_alpha_out_halo=halo_color*(alpha_halo*total_opacity);}if (u_is_text && u_is_halo) {fragColor=color_alpha_out+(1.-color_alpha_out.a)*color_alpha_out_halo;} else if (u_is_halo) {fragColor=color_alpha_out_halo;} else {fragColor=color_alpha_out;}\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "layout(location=0) in vec4 a_pos_offset;layout(location=1) in uvec4 a_data;layout(location=2) in vec3 a_projected_pos;layout(location=3) in uint a_fade_opacity;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform highp float u_camera_to_center_distance;uniform float u_fade_change;uniform vec2 u_texsize;uniform vec2 u_texsize_icon;uniform bool u_is_along_line;uniform bool u_is_variable_anchor;uniform vec2 u_translation;uniform float u_pitched_scale;uniform bool u_is_offset;out vec4 v_data0;out vec3 v_data1;flat out float v_is_sdf;\n#pragma maplibre: define highp vec4 fill_color\n#pragma maplibre: define highp vec4 halo_color\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define lowp float halo_width\n#pragma maplibre: define lowp float halo_blur\nvoid main() {\n#pragma maplibre: initialize highp vec4 fill_color\n#pragma maplibre: initialize highp vec4 halo_color\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize lowp float halo_width\n#pragma maplibre: initialize lowp float halo_blur\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=vec2(a_data.xy);vec2 a_size=vec2(a_data.zw);float a_size_min=float(a_data.z >> 1u);float is_sdf=float(a_data.z & 1u);float ele=get_elevation(a_pos);highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec2 translated_a_pos=a_pos+u_translation;vec4 projectedPoint=projectTileWithElevation(translated_a_pos,ele);vec2 fade_opacity=unpack_opacity(a_fade_opacity);float visibility=calculate_visibility(projectedPoint);float fade_change=fade_opacity[1] > 0.5 ? u_fade_change :-u_fade_change;float interpolated_fade_opacity=max(0.0,min(visibility,fade_opacity[0]+fade_change));float total_opacity=opacity*interpolated_fade_opacity;if (total_opacity < 0.1){gl_Position=vec4(-2.,-2.,-2.,1.);return;}highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\ncamera_to_anchor_distance/u_camera_to_center_distance :\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(0.5+0.5*distance_ratio,0.0,4.0);if (!u_is_offset) {size*=perspective_ratio;}float fontScale=size/24.0;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=projectTileWithElevation(translated_a_pos+vec2(1,0),ele);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos;if (u_is_along_line || u_is_variable_anchor) {projected_pos=vec4(a_projected_pos.xy,ele,1.0);} else if (u_pitch_with_map) {projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy+u_translation,ele,1.0);} else {projected_pos=u_label_plane_matrix*projectTileWithElevation(a_projected_pos.xy+u_translation,ele);}float z=float(u_pitch_with_map)*projected_pos.z/projected_pos.w;float projectionScaling=1.0;\n#ifdef GLOBE\nif(u_pitch_with_map && !u_is_along_line) {float anchor_pos_tile_y=(u_coord_matrix*vec4(projected_pos.xy/projected_pos.w,z,1.0)).y;projectionScaling=mix(projectionScaling,1.0/circumferenceRatioAtTileY(anchor_pos_tile_y)*u_pitched_scale,u_projection_transition);}\n#endif\nvec4 finalPos=u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+rotation_matrix*(a_offset/32.0*fontScale)*projectionScaling,z,1.0);if(u_pitch_with_map) {finalPos=projectTileWithElevation(finalPos.xy,finalPos.z);}float gamma_scale=finalPos.w;gl_Position=finalPos;v_data0.xy=a_tex/u_texsize;v_data0.zw=a_tex/u_texsize_icon;v_data1=vec3(gamma_scale,size,total_opacity);v_is_sdf=is_sdf;}"),
|
|
9095
|
+
symbolIcon: prepare("uniform sampler2D u_texture;in vec2 v_tex;flat in float v_total_opacity;void main() {fragColor=texture(u_texture,v_tex)*v_total_opacity;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "layout(location=0) in vec4 a_pos_offset;layout(location=1) in uvec4 a_data;layout(location=2) in vec4 a_pixeloffset;layout(location=3) in vec3 a_projected_pos;layout(location=4) in uint a_fade_opacity;layout(location=5) in float a_height_offset;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform highp float u_camera_to_center_distance;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform float u_fade_change;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform vec2 u_texsize;uniform bool u_is_along_line;uniform bool u_is_variable_anchor;uniform vec2 u_translation;uniform float u_pitched_scale;uniform bool u_is_offset;uniform bool u_height_anchor_ground;out vec2 v_tex;flat out float v_total_opacity;\n#pragma maplibre: define lowp float opacity\nvoid main() {\n#pragma maplibre: initialize lowp float opacity\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=vec2(a_data.xy);vec2 a_size=vec2(a_data.zw);float a_size_min=float(a_data.z >> 1u);vec2 a_pxoffset=a_pixeloffset.xy;vec2 a_minFontScale=a_pixeloffset.zw/256.0;float ele=a_height_offset+(u_height_anchor_ground ? get_elevation(a_pos) : 0.0);highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec2 translated_a_pos=a_pos+u_translation;vec4 projectedPoint=projectTileWithElevation(translated_a_pos,ele);vec2 fade_opacity=unpack_opacity(a_fade_opacity);float fade_change=fade_opacity[1] > 0.5 ? u_fade_change :-u_fade_change;float visibility=calculate_visibility(projectedPoint);v_total_opacity=opacity*max(0.0,min(visibility,fade_opacity[0]+fade_change));if (v_total_opacity < 0.1){gl_Position=vec4(-2.,-2.,-2.,1.);return;}highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\ncamera_to_anchor_distance/u_camera_to_center_distance :\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(0.5+0.5*distance_ratio,0.0,4.0);if (!u_is_offset) {size*=perspective_ratio;}float fontScale=u_is_text ? size/24.0 : size;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=projectTileWithElevation(translated_a_pos+vec2(1,0),ele);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos;if (u_is_along_line || u_is_variable_anchor) {projected_pos=vec4(a_projected_pos.xy,ele,1.0);} else if (u_pitch_with_map) {projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy+u_translation,ele,1.0);} else {projected_pos=u_label_plane_matrix*projectTileWithElevation(a_projected_pos.xy+u_translation,ele);}float z=float(u_pitch_with_map)*projected_pos.z/projected_pos.w;float projectionScaling=1.0;\n#ifdef GLOBE\nif(u_pitch_with_map) {float anchor_pos_tile_y=(u_coord_matrix*vec4(projected_pos.xy/projected_pos.w,z,1.0)).y;projectionScaling=mix(projectionScaling,1.0/circumferenceRatioAtTileY(anchor_pos_tile_y)*u_pitched_scale,u_projection_transition);}\n#endif\nvec4 finalPos=u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+rotation_matrix*(a_offset/32.0*max(a_minFontScale,fontScale)+a_pxoffset/16.0)*projectionScaling,z,1.0);if(u_pitch_with_map) {finalPos=projectTileWithElevation(finalPos.xy,finalPos.z);}gl_Position=finalPos;v_tex=a_tex/u_texsize;}"),
|
|
9096
|
+
symbolSDF: prepare("#define SDF_PX 8.0\nuniform bool u_is_halo;uniform bool u_is_plain;uniform sampler2D u_texture;uniform highp float u_gamma_scale;uniform lowp float u_device_pixel_ratio;uniform bool u_is_text;in vec2 v_data0;in vec3 v_data1;\n#pragma maplibre: define highp vec4 fill_color\n#pragma maplibre: define highp vec4 halo_color\n#pragma maplibre: define lowp float halo_width\n#pragma maplibre: define lowp float halo_blur\nvoid main() {\n#pragma maplibre: initialize highp vec4 fill_color\n#pragma maplibre: initialize highp vec4 halo_color\n#pragma maplibre: initialize lowp float halo_width\n#pragma maplibre: initialize lowp float halo_blur\nfloat EDGE_GAMMA=0.105/u_device_pixel_ratio;vec2 tex=v_data0.xy;float gamma_scale=v_data1.x;float size=v_data1.y;float total_opacity=v_data1[2];float fontScale=u_is_text ? size/24.0 : size;highp float gamma=EDGE_GAMMA/(fontScale*u_gamma_scale);lowp float inner_edge=(256.0-64.0)/256.0;lowp float dist=texture(u_texture,tex).a;lowp vec4 color_alpha_out_text,color_alpha_out_halo;if (u_is_plain){highp float gamma_scaled=gamma*gamma_scale;highp float alpha=smoothstep(inner_edge-gamma_scaled,inner_edge+gamma_scaled,dist);color_alpha_out_text=total_opacity*alpha*fill_color;}if (u_is_halo) {float gamma_halo=(halo_blur*1.19/SDF_PX+EDGE_GAMMA)/(fontScale*u_gamma_scale);float inner_edge_halo=inner_edge+gamma_halo*gamma_scale;highp float gamma_scaled_halo=gamma_halo*gamma_scale;highp float alpha_halo=smoothstep(inner_edge_halo-gamma_scaled_halo,inner_edge_halo+gamma_scaled_halo,dist);highp float halo_edge=(6.0-halo_width/fontScale)/SDF_PX;alpha_halo= min(smoothstep(halo_edge-gamma_scaled_halo,halo_edge+gamma_scaled_halo,dist),1.0-alpha_halo);color_alpha_out_halo=total_opacity*alpha_halo*halo_color;}if (u_is_plain && u_is_halo) {fragColor=color_alpha_out_text+(1.-color_alpha_out_text.a)*color_alpha_out_halo;} else if (u_is_halo){fragColor=color_alpha_out_halo;} else {fragColor=color_alpha_out_text;}\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "layout(location=0) in vec4 a_pos_offset;layout(location=1) in uvec4 a_data;layout(location=2) in vec4 a_pixeloffset;layout(location=3) in vec3 a_projected_pos;layout(location=4) in uint a_fade_opacity;layout(location=5) in float a_height_offset;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform bool u_is_along_line;uniform bool u_is_variable_anchor;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform highp float u_camera_to_center_distance;uniform float u_fade_change;uniform vec2 u_texsize;uniform vec2 u_translation;uniform float u_pitched_scale;uniform bool u_is_offset;uniform bool u_height_anchor_ground;out vec2 v_data0;out vec3 v_data1;\n#pragma maplibre: define highp vec4 fill_color\n#pragma maplibre: define highp vec4 halo_color\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define lowp float halo_width\n#pragma maplibre: define lowp float halo_blur\nvoid main() {\n#pragma maplibre: initialize highp vec4 fill_color\n#pragma maplibre: initialize highp vec4 halo_color\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize lowp float halo_width\n#pragma maplibre: initialize lowp float halo_blur\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=vec2(a_data.xy);vec2 a_size=vec2(a_data.zw);float a_size_min=float(a_data.z >> 1u);vec2 a_pxoffset=a_pixeloffset.xy/16.0;vec2 a_minFontScale=a_pixeloffset.zw/256.0;float ele=a_height_offset+(u_height_anchor_ground ? get_elevation(a_pos) : 0.0);highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec2 translated_a_pos=a_pos+u_translation;vec4 projectedPoint=projectTileWithElevation(translated_a_pos,ele);vec2 fade_opacity=unpack_opacity(a_fade_opacity);float visibility=calculate_visibility(projectedPoint);float fade_change=fade_opacity[1] > 0.5 ? u_fade_change :-u_fade_change;float interpolated_fade_opacity=max(0.0,min(visibility,fade_opacity[0]+fade_change));float total_opacity=opacity*interpolated_fade_opacity;if (total_opacity < 0.1){gl_Position=vec4(-2.,-2.,-2.,1.);return;}highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\ncamera_to_anchor_distance/u_camera_to_center_distance :\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(0.5+0.5*distance_ratio,0.0,4.0);if (!u_is_offset) {size*=perspective_ratio;}float fontScale=u_is_text ? size/24.0 : size;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=projectTileWithElevation(translated_a_pos+vec2(1,0),ele);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos;if (u_is_along_line || u_is_variable_anchor) {projected_pos=vec4(a_projected_pos.xy,ele,1.0);} else if (u_pitch_with_map) {projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy+u_translation,ele,1.0);} else {projected_pos=u_label_plane_matrix*projectTileWithElevation(a_projected_pos.xy+u_translation,ele);}float z=float(u_pitch_with_map)*projected_pos.z/projected_pos.w;float projectionScaling=1.0;\n#ifdef GLOBE\nif(u_pitch_with_map) {float anchor_pos_tile_y=(u_coord_matrix*vec4(projected_pos.xy/projected_pos.w,z,1.0)).y;projectionScaling=mix(projectionScaling,1.0/circumferenceRatioAtTileY(anchor_pos_tile_y)*u_pitched_scale,u_projection_transition);}\n#endif\nvec4 finalPos=u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+rotation_matrix*(a_offset/32.0*max(a_minFontScale,fontScale)+a_pxoffset)*projectionScaling,z,1.0);if(u_pitch_with_map) {finalPos=projectTileWithElevation(finalPos.xy,finalPos.z);}float gamma_scale=finalPos.w;gl_Position=finalPos;v_data0=a_tex/u_texsize;v_data1=vec3(gamma_scale,size,total_opacity);}"),
|
|
9097
|
+
symbolTextAndIcon: prepare("#define SDF_PX 8.0\n#define SDF 1.0\n#define ICON 0.0\nuniform bool u_is_halo;uniform bool u_is_text;uniform sampler2D u_texture;uniform sampler2D u_texture_icon;uniform highp float u_gamma_scale;uniform lowp float u_device_pixel_ratio;in vec4 v_data0;in vec3 v_data1;flat in float v_is_sdf;\n#pragma maplibre: define highp vec4 fill_color\n#pragma maplibre: define highp vec4 halo_color\n#pragma maplibre: define lowp float halo_width\n#pragma maplibre: define lowp float halo_blur\nvoid main() {\n#pragma maplibre: initialize highp vec4 fill_color\n#pragma maplibre: initialize highp vec4 halo_color\n#pragma maplibre: initialize lowp float halo_width\n#pragma maplibre: initialize lowp float halo_blur\nfloat total_opacity=v_data1[2];if (v_is_sdf==ICON) {vec2 tex_icon=v_data0.zw;fragColor=texture(u_texture_icon,tex_icon)*total_opacity;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\nreturn;}vec2 tex=v_data0.xy;float EDGE_GAMMA=0.105/u_device_pixel_ratio;float gamma_scale=v_data1.x;float size=v_data1.y;float fontScale=size/24.0;highp float gamma=EDGE_GAMMA/(fontScale*u_gamma_scale);lowp float buff=(256.0-64.0)/256.0;lowp float dist=texture(u_texture,tex).a;lowp vec4 color_alpha_out,color_alpha_out_halo;if (u_is_text) {highp float gamma_scaled=gamma*gamma_scale;highp float alpha=smoothstep(buff-gamma_scaled,buff+gamma_scaled,dist);color_alpha_out=fill_color*(alpha*total_opacity);}if (u_is_halo) {highp float gamma_halo=(halo_blur*1.19/SDF_PX+EDGE_GAMMA)/(fontScale*u_gamma_scale);lowp float buff_halo=(6.0-halo_width/fontScale)/SDF_PX;highp float gamma_scaled_halo=gamma_halo*gamma_scale;highp float alpha_halo=smoothstep(buff_halo-gamma_scaled_halo,buff_halo+gamma_scaled_halo,dist);color_alpha_out_halo=halo_color*(alpha_halo*total_opacity);}if (u_is_text && u_is_halo) {fragColor=color_alpha_out+(1.-color_alpha_out.a)*color_alpha_out_halo;} else if (u_is_halo) {fragColor=color_alpha_out_halo;} else {fragColor=color_alpha_out;}\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "layout(location=0) in vec4 a_pos_offset;layout(location=1) in uvec4 a_data;layout(location=2) in vec3 a_projected_pos;layout(location=3) in uint a_fade_opacity;layout(location=4) in float a_height_offset;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform highp float u_camera_to_center_distance;uniform float u_fade_change;uniform vec2 u_texsize;uniform vec2 u_texsize_icon;uniform bool u_is_along_line;uniform bool u_is_variable_anchor;uniform vec2 u_translation;uniform float u_pitched_scale;uniform bool u_is_offset;uniform bool u_height_anchor_ground;out vec4 v_data0;out vec3 v_data1;flat out float v_is_sdf;\n#pragma maplibre: define highp vec4 fill_color\n#pragma maplibre: define highp vec4 halo_color\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define lowp float halo_width\n#pragma maplibre: define lowp float halo_blur\nvoid main() {\n#pragma maplibre: initialize highp vec4 fill_color\n#pragma maplibre: initialize highp vec4 halo_color\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize lowp float halo_width\n#pragma maplibre: initialize lowp float halo_blur\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=vec2(a_data.xy);vec2 a_size=vec2(a_data.zw);float a_size_min=float(a_data.z >> 1u);float is_sdf=float(a_data.z & 1u);float ele=a_height_offset+(u_height_anchor_ground ? get_elevation(a_pos) : 0.0);highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec2 translated_a_pos=a_pos+u_translation;vec4 projectedPoint=projectTileWithElevation(translated_a_pos,ele);vec2 fade_opacity=unpack_opacity(a_fade_opacity);float visibility=calculate_visibility(projectedPoint);float fade_change=fade_opacity[1] > 0.5 ? u_fade_change :-u_fade_change;float interpolated_fade_opacity=max(0.0,min(visibility,fade_opacity[0]+fade_change));float total_opacity=opacity*interpolated_fade_opacity;if (total_opacity < 0.1){gl_Position=vec4(-2.,-2.,-2.,1.);return;}highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\ncamera_to_anchor_distance/u_camera_to_center_distance :\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(0.5+0.5*distance_ratio,0.0,4.0);if (!u_is_offset) {size*=perspective_ratio;}float fontScale=size/24.0;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=projectTileWithElevation(translated_a_pos+vec2(1,0),ele);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos;if (u_is_along_line || u_is_variable_anchor) {projected_pos=vec4(a_projected_pos.xy,ele,1.0);} else if (u_pitch_with_map) {projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy+u_translation,ele,1.0);} else {projected_pos=u_label_plane_matrix*projectTileWithElevation(a_projected_pos.xy+u_translation,ele);}float z=float(u_pitch_with_map)*projected_pos.z/projected_pos.w;float projectionScaling=1.0;\n#ifdef GLOBE\nif(u_pitch_with_map && !u_is_along_line) {float anchor_pos_tile_y=(u_coord_matrix*vec4(projected_pos.xy/projected_pos.w,z,1.0)).y;projectionScaling=mix(projectionScaling,1.0/circumferenceRatioAtTileY(anchor_pos_tile_y)*u_pitched_scale,u_projection_transition);}\n#endif\nvec4 finalPos=u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+rotation_matrix*(a_offset/32.0*fontScale)*projectionScaling,z,1.0);if(u_pitch_with_map) {finalPos=projectTileWithElevation(finalPos.xy,finalPos.z);}float gamma_scale=finalPos.w;gl_Position=finalPos;v_data0.xy=a_tex/u_texsize;v_data0.zw=a_tex/u_texsize_icon;v_data1=vec3(gamma_scale,size,total_opacity);v_is_sdf=is_sdf;}"),
|
|
8784
9098
|
terrain: prepare("uniform sampler2D u_texture;uniform vec4 u_fog_color;uniform vec4 u_horizon_color;uniform float u_fog_ground_blend;uniform float u_fog_ground_blend_opacity;uniform float u_horizon_fog_blend;uniform bool u_is_globe_mode;in vec2 v_texture_pos;in float v_fog_depth;const float gamma=2.2;vec4 gammaToLinear(vec4 color) {return pow(color,vec4(gamma));}vec4 linearToGamma(vec4 color) {return pow(color,vec4(1.0/gamma));}void main() {vec4 surface_color=texture(u_texture,vec2(v_texture_pos.x,1.0-v_texture_pos.y));if (!u_is_globe_mode && u_fog_ground_blend_opacity > 0.0 && v_fog_depth > u_fog_ground_blend) {vec4 surface_color_linear=gammaToLinear(surface_color);float blend_color=smoothstep(0.0,1.0,max((v_fog_depth-u_horizon_fog_blend)/(1.0-u_horizon_fog_blend),0.0));vec4 fog_horizon_color_linear=mix(gammaToLinear(u_fog_color),gammaToLinear(u_horizon_color),blend_color);float factor_fog=max(v_fog_depth-u_fog_ground_blend,0.0)/(1.0-u_fog_ground_blend);fragColor=linearToGamma(mix(surface_color_linear,fog_horizon_color_linear,pow(factor_fog,2.0)*u_fog_ground_blend_opacity));} else {fragColor=surface_color;}}", "layout(location=0) in vec3 a_pos3d;uniform mat4 u_fog_matrix;uniform float u_ele_delta;out vec2 v_texture_pos;out float v_fog_depth;void main() {float ele=get_elevation(a_pos3d.xy);float ele_delta=a_pos3d.z==1.0 ? u_ele_delta : 0.0;v_texture_pos=a_pos3d.xy/8192.0;gl_Position=projectTileFor3D(a_pos3d.xy,ele-ele_delta);vec4 pos=u_fog_matrix*vec4(a_pos3d.xy,ele,1.0);v_fog_depth=pos.z/pos.w*0.5+0.5;}"),
|
|
8785
9099
|
terrainDepth: prepare("in float v_depth;const highp vec4 bitSh=vec4(256.*256.*256.,256.*256.,256.,1.);const highp vec4 bitMsk=vec4(0.,vec3(1./256.0));highp vec4 pack(highp float value) {highp vec4 comp=fract(value*bitSh);comp-=comp.xxyz*bitMsk;return comp;}void main() {fragColor=pack(v_depth);}", "layout(location=0) in vec3 a_pos3d;uniform float u_ele_delta;out float v_depth;void main() {float ele=get_elevation(a_pos3d.xy);float ele_delta=a_pos3d.z==1.0 ? u_ele_delta : 0.0;gl_Position=projectTileFor3D(a_pos3d.xy,ele-ele_delta);v_depth=gl_Position.z/gl_Position.w;}"),
|
|
8786
|
-
terrainCoords: prepare("precision mediump float;uniform sampler2D u_texture;uniform float u_terrain_coords_id;in vec2 v_texture_pos;void main() {vec4 rgba=texture(u_texture,v_texture_pos);fragColor=vec4(rgba.r,rgba.g,rgba.b,u_terrain_coords_id);}", "layout(location=0) in vec3 a_pos3d;uniform float u_ele_delta;out vec2 v_texture_pos;void main() {float ele=get_elevation(a_pos3d.xy);float ele_delta=a_pos3d.z==1.0 ? u_ele_delta : 0.0;v_texture_pos=a_pos3d.xy/8192.0;gl_Position=projectTileFor3D(a_pos3d.xy,ele-ele_delta);}"),
|
|
8787
9100
|
atmosphere: prepare("#ifdef GL_ES\nprecision highp float;\n#endif\nin vec3 view_direction;uniform vec3 u_sun_pos;uniform vec3 u_globe_position;uniform float u_globe_radius;uniform float u_atmosphere_blend;/**Shader use from https:*Made some change to adapt to MapLibre Globe geometry*/const float PI=3.141592653589793;const int iSteps=5;const int jSteps=3;/*radius of the planet*/const float EARTH_RADIUS=6371e3;/*radius of the atmosphere*/const float ATMOS_RADIUS=6471e3;vec2 rsi(vec3 r0,vec3 rd,float sr) {float a=dot(rd,rd);float b=2.0*dot(rd,r0);float c=dot(r0,r0)-(sr*sr);float d=(b*b)-4.0*a*c;if (d < 0.0) return vec2(1e5,-1e5);return vec2((-b-sqrt(d))/(2.0*a),(-b+sqrt(d))/(2.0*a));}vec4 atmosphere(vec3 r,vec3 r0,vec3 pSun,float iSun,float rPlanet,float rAtmos,vec3 kRlh,float kMie,float shRlh,float shMie,float g) {pSun=normalize(pSun);r=normalize(r);vec2 p=rsi(r0,r,rAtmos);if (p.x > p.y) {return vec4(0.0,0.0,0.0,1.0);}if (p.x < 0.0) {p.x=0.0;}vec3 pos=r0+r*p.x;vec2 p2=rsi(r0,r,rPlanet);if (p2.x <=p2.y && p2.x > 0.0) {p.y=min(p.y,p2.x);}float iStepSize=(p.y-p.x)/float(iSteps);float iTime=p.x+iStepSize*0.5;vec3 totalRlh=vec3(0,0,0);vec3 totalMie=vec3(0,0,0);float iOdRlh=0.0;float iOdMie=0.0;float mu=dot(r,pSun);float mumu=mu*mu;float gg=g*g;float pRlh=3.0/(16.0*PI)*(1.0+mumu);float pMie=3.0/(8.0*PI)*((1.0-gg)*(mumu+1.0))/(pow(1.0+gg-2.0*mu*g,1.5)*(2.0+gg));for (int i=0; i < iSteps; i++) {vec3 iPos=r0+r*iTime;float iHeight=length(iPos)-rPlanet;float odStepRlh=exp(-iHeight/shRlh)*iStepSize;float odStepMie=exp(-iHeight/shMie)*iStepSize;iOdRlh+=odStepRlh;iOdMie+=odStepMie;float jStepSize=rsi(iPos,pSun,rAtmos).y/float(jSteps);float jTime=jStepSize*0.5;float jOdRlh=0.0;float jOdMie=0.0;for (int j=0; j < jSteps; j++) {vec3 jPos=iPos+pSun*jTime;float jHeight=length(jPos)-rPlanet;jOdRlh+=exp(-jHeight/shRlh)*jStepSize;jOdMie+=exp(-jHeight/shMie)*jStepSize;jTime+=jStepSize;}vec3 attn=exp(-(kMie*(iOdMie+jOdMie)+kRlh*(iOdRlh+jOdRlh)));totalRlh+=odStepRlh*attn;totalMie+=odStepMie*attn;iTime+=iStepSize;}float opacity=exp(-(length(kRlh)*length(totalRlh)+kMie*length(totalMie)));vec3 color=iSun*(pRlh*kRlh*totalRlh+pMie*kMie*totalMie);return vec4(color,opacity);}void main() {vec3 scale_camera_pos=-u_globe_position*EARTH_RADIUS/u_globe_radius;vec4 color=atmosphere(normalize(view_direction),scale_camera_pos,u_sun_pos,22.0,EARTH_RADIUS,ATMOS_RADIUS,vec3(5.5e-6,13.0e-6,22.4e-6),21e-6,8e3,1.2e3,0.758\n);color.rgb=1.0-exp(-1.0*color.rgb);color=pow(color,vec4(1.0/2.2));fragColor=vec4(color.rgb,1.0-color.a)*u_atmosphere_blend;}", "layout(location=0) in vec2 a_pos;uniform mat4 u_inv_proj_matrix;out vec3 view_direction;void main() {view_direction=(u_inv_proj_matrix*vec4(a_pos,0.0,1.0)).xyz;gl_Position=vec4(a_pos,0.0,1.0);}"),
|
|
8788
9101
|
sky: prepare("uniform vec4 u_sky_color;uniform vec4 u_horizon_color;uniform vec2 u_horizon;uniform vec2 u_horizon_normal;uniform float u_sky_horizon_blend;uniform float u_sky_blend;void main() {float x=gl_FragCoord.x;float y=gl_FragCoord.y;float blend=(y-u_horizon.y)*u_horizon_normal.y+(x-u_horizon.x)*u_horizon_normal.x;if (blend > 0.0) {if (blend < u_sky_horizon_blend) {fragColor=mix(u_sky_color,u_horizon_color,pow(1.0-blend/u_sky_horizon_blend,2.0));} else {fragColor=u_sky_color;}}fragColor=mix(fragColor,vec4(vec3(0.0),0.0),u_sky_blend);}", "layout(location=0) in vec2 a_pos;void main() {gl_Position=vec4(a_pos,1.0,1.0);}")
|
|
8789
9102
|
};
|
|
@@ -9127,7 +9440,9 @@ var TransformHelper = class {
|
|
|
9127
9440
|
setMinZoom(zoom) {
|
|
9128
9441
|
if (this._minZoom === zoom) return;
|
|
9129
9442
|
this._minZoom = zoom;
|
|
9443
|
+
const unmodified = this._unmodified;
|
|
9130
9444
|
this.setZoom(this.applyConstrain(this._center, this.zoom).zoom);
|
|
9445
|
+
this._unmodified = unmodified;
|
|
9131
9446
|
}
|
|
9132
9447
|
get maxZoom() {
|
|
9133
9448
|
return this._maxZoom;
|
|
@@ -9135,7 +9450,9 @@ var TransformHelper = class {
|
|
|
9135
9450
|
setMaxZoom(zoom) {
|
|
9136
9451
|
if (this._maxZoom === zoom) return;
|
|
9137
9452
|
this._maxZoom = zoom;
|
|
9453
|
+
const unmodified = this._unmodified;
|
|
9138
9454
|
this.setZoom(this.applyConstrain(this._center, this.zoom).zoom);
|
|
9455
|
+
this._unmodified = unmodified;
|
|
9139
9456
|
}
|
|
9140
9457
|
get minPitch() {
|
|
9141
9458
|
return this._minPitch;
|
|
@@ -9143,7 +9460,9 @@ var TransformHelper = class {
|
|
|
9143
9460
|
setMinPitch(pitch) {
|
|
9144
9461
|
if (this._minPitch === pitch) return;
|
|
9145
9462
|
this._minPitch = pitch;
|
|
9463
|
+
const unmodified = this._unmodified;
|
|
9146
9464
|
this.setPitch(Math.max(this.pitch, pitch));
|
|
9465
|
+
this._unmodified = unmodified;
|
|
9147
9466
|
}
|
|
9148
9467
|
get maxPitch() {
|
|
9149
9468
|
return this._maxPitch;
|
|
@@ -9151,7 +9470,9 @@ var TransformHelper = class {
|
|
|
9151
9470
|
setMaxPitch(pitch) {
|
|
9152
9471
|
if (this._maxPitch === pitch) return;
|
|
9153
9472
|
this._maxPitch = pitch;
|
|
9473
|
+
const unmodified = this._unmodified;
|
|
9154
9474
|
this.setPitch(Math.min(this.pitch, pitch));
|
|
9475
|
+
this._unmodified = unmodified;
|
|
9155
9476
|
}
|
|
9156
9477
|
get renderWorldCopies() {
|
|
9157
9478
|
return this._renderWorldCopies;
|
|
@@ -9523,9 +9844,7 @@ var TransformHelper = class {
|
|
|
9523
9844
|
return Math.cos(this.pitchInRadians) * this._cameraToCenterDistance / this._pixelPerMeter + this.elevation;
|
|
9524
9845
|
}
|
|
9525
9846
|
getCameraLngLat() {
|
|
9526
|
-
|
|
9527
|
-
const cameraToCenterDistanceMeters = this.cameraToCenterDistance / pixelPerMeter;
|
|
9528
|
-
return cameraMercatorCoordinateFromCenterAndRotation(this.center, this.elevation, this.pitch, this.bearing, cameraToCenterDistanceMeters).toLngLat();
|
|
9847
|
+
return cameraMercatorCoordinate(this).toLngLat();
|
|
9529
9848
|
}
|
|
9530
9849
|
getMercatorTileCoordinates(overscaledTileID) {
|
|
9531
9850
|
if (!overscaledTileID) return [
|
|
@@ -9883,118 +10202,857 @@ function getIdealNearFarPlaneDistance(horizonPlane, nearPlaneNormalized) {
|
|
|
9883
10202
|
} else return null;
|
|
9884
10203
|
}
|
|
9885
10204
|
//#endregion
|
|
9886
|
-
//#region src/
|
|
9887
|
-
|
|
9888
|
-
|
|
9889
|
-
|
|
9890
|
-
|
|
9891
|
-
|
|
9892
|
-
|
|
9893
|
-
|
|
9894
|
-
|
|
9895
|
-
|
|
10205
|
+
//#region src/data/pos3d_attributes.ts
|
|
10206
|
+
const pos3dAttributes = createLayout([{
|
|
10207
|
+
name: "a_pos3d",
|
|
10208
|
+
type: "Int16",
|
|
10209
|
+
components: 3
|
|
10210
|
+
}]);
|
|
10211
|
+
//#endregion
|
|
10212
|
+
//#region src/tile/terrain_tile_manager.ts
|
|
10213
|
+
/**
|
|
10214
|
+
* @internal
|
|
10215
|
+
* This class is a helper for the Terrain-class, it:
|
|
10216
|
+
*
|
|
10217
|
+
* - loads raster-dem tiles
|
|
10218
|
+
* - manages all renderToTexture tiles.
|
|
10219
|
+
* - caches previous rendered tiles.
|
|
10220
|
+
* - finds all necessary renderToTexture tiles for a OverscaledTileID area
|
|
10221
|
+
* - finds the corresponding raster-dem tile for OverscaledTileID
|
|
10222
|
+
*/
|
|
10223
|
+
var TerrainTileManager = class extends Evented {
|
|
10224
|
+
constructor(tileManager) {
|
|
10225
|
+
super();
|
|
10226
|
+
this._lastTilesetChange = now();
|
|
10227
|
+
this.tileManager = tileManager;
|
|
10228
|
+
this._tiles = {};
|
|
10229
|
+
this._renderableTilesKeys = [];
|
|
10230
|
+
this._sourceTileCache = {};
|
|
10231
|
+
this.minzoom = 0;
|
|
10232
|
+
this.maxzoom = 22;
|
|
10233
|
+
this.deltaZoom = 1;
|
|
10234
|
+
this.tileSize = tileManager._source.tileSize * 2 ** this.deltaZoom;
|
|
10235
|
+
tileManager.usedForTerrain = true;
|
|
10236
|
+
tileManager.tileSize = this.tileSize;
|
|
9896
10237
|
}
|
|
9897
|
-
|
|
9898
|
-
|
|
10238
|
+
destruct() {
|
|
10239
|
+
this.tileManager.usedForTerrain = false;
|
|
10240
|
+
this.tileManager.tileSize = null;
|
|
10241
|
+
this.releaseAllRTT();
|
|
9899
10242
|
}
|
|
9900
|
-
|
|
9901
|
-
return this.
|
|
10243
|
+
getSource() {
|
|
10244
|
+
return this.tileManager._source;
|
|
9902
10245
|
}
|
|
9903
|
-
|
|
9904
|
-
|
|
10246
|
+
/**
|
|
10247
|
+
* Load Terrain Tiles, create internal render-to-texture tiles, free GPU memory.
|
|
10248
|
+
* @param transform - the operation to do
|
|
10249
|
+
* @param terrain - the terrain
|
|
10250
|
+
* @returns true when the set of renderable tiles changed
|
|
10251
|
+
*/
|
|
10252
|
+
update(transform, terrain) {
|
|
10253
|
+
this.tileManager.update(transform, terrain);
|
|
10254
|
+
this._renderableTilesKeys = [];
|
|
10255
|
+
const keys = {};
|
|
10256
|
+
let changed = false;
|
|
10257
|
+
for (const tileID of coveringTiles(transform, {
|
|
10258
|
+
tileSize: this.tileSize,
|
|
10259
|
+
minzoom: this.minzoom,
|
|
10260
|
+
maxzoom: this.maxzoom,
|
|
10261
|
+
reparseOverscaled: false,
|
|
10262
|
+
terrain,
|
|
10263
|
+
calculateTileZoom: this.tileManager._source.calculateTileZoom
|
|
10264
|
+
})) {
|
|
10265
|
+
keys[tileID.key] = true;
|
|
10266
|
+
this._renderableTilesKeys.push(tileID.key);
|
|
10267
|
+
if (!this._tiles[tileID.key]) {
|
|
10268
|
+
tileID.terrainRttPosMatrix32f = /* @__PURE__ */ new Float32Array(16);
|
|
10269
|
+
ortho(tileID.terrainRttPosMatrix32f, 0, EXTENT, EXTENT, 0, 0, 1);
|
|
10270
|
+
this._tiles[tileID.key] = new Tile(tileID, this.tileSize);
|
|
10271
|
+
this._lastTilesetChange = now();
|
|
10272
|
+
changed = true;
|
|
10273
|
+
}
|
|
10274
|
+
}
|
|
10275
|
+
for (const key in this._tiles) if (!keys[key]) {
|
|
10276
|
+
this._tiles[key].releaseRTT(this.tileManager.map.painter);
|
|
10277
|
+
delete this._tiles[key];
|
|
10278
|
+
changed = true;
|
|
10279
|
+
}
|
|
10280
|
+
return changed;
|
|
9905
10281
|
}
|
|
9906
|
-
|
|
9907
|
-
|
|
10282
|
+
/**
|
|
10283
|
+
* Release the RTT objects for `tileID` (and its ancestors/descendants),
|
|
10284
|
+
*/
|
|
10285
|
+
releaseRTT(tileID) {
|
|
10286
|
+
for (const key in this._tiles) {
|
|
10287
|
+
const tile = this._tiles[key];
|
|
10288
|
+
if (tile.tileID.equals(tileID) || tile.tileID.isChildOf(tileID) || tileID.isChildOf(tile.tileID)) tile.releaseRTT(this.tileManager.map.painter);
|
|
10289
|
+
}
|
|
9908
10290
|
}
|
|
9909
|
-
|
|
9910
|
-
|
|
10291
|
+
/**
|
|
10292
|
+
* Release the A RTT objects for all tiles.
|
|
10293
|
+
*/
|
|
10294
|
+
releaseAllRTT() {
|
|
10295
|
+
for (const key in this._tiles) this._tiles[key].releaseRTT(this.tileManager.map.painter);
|
|
9911
10296
|
}
|
|
9912
|
-
|
|
9913
|
-
|
|
10297
|
+
/**
|
|
10298
|
+
* get a list of tiles, which are loaded and should be rendered in the current scene
|
|
10299
|
+
* @returns the renderable tiles
|
|
10300
|
+
*/
|
|
10301
|
+
getRenderableTiles() {
|
|
10302
|
+
return this._renderableTilesKeys.map((key) => this.getTileByID(key));
|
|
9914
10303
|
}
|
|
9915
|
-
|
|
9916
|
-
|
|
9917
|
-
|
|
9918
|
-
|
|
9919
|
-
|
|
9920
|
-
|
|
9921
|
-
|
|
9922
|
-
this._helper.setMaxPitch(pitch);
|
|
9923
|
-
}
|
|
9924
|
-
setRenderWorldCopies(renderWorldCopies) {
|
|
9925
|
-
this._helper.setRenderWorldCopies(renderWorldCopies);
|
|
10304
|
+
/**
|
|
10305
|
+
* get terrain tile by the TileID key
|
|
10306
|
+
* @param id - the tile id
|
|
10307
|
+
* @returns the tile
|
|
10308
|
+
*/
|
|
10309
|
+
getTileByID(id) {
|
|
10310
|
+
return this._tiles[id];
|
|
9926
10311
|
}
|
|
9927
|
-
|
|
9928
|
-
|
|
10312
|
+
/**
|
|
10313
|
+
* Searches for the corresponding current renderable terrain-tiles
|
|
10314
|
+
* @param tileID - the tile to look for
|
|
10315
|
+
* @returns the tiles that were found
|
|
10316
|
+
*/
|
|
10317
|
+
getTerrainCoords(tileID, terrainTileRanges) {
|
|
10318
|
+
if (terrainTileRanges) return this._getTerrainCoordsForTileRanges(tileID, terrainTileRanges);
|
|
10319
|
+
else return this._getTerrainCoordsForRegularTile(tileID);
|
|
9929
10320
|
}
|
|
9930
|
-
|
|
9931
|
-
|
|
10321
|
+
/**
|
|
10322
|
+
* Searches for the corresponding current renderable terrain-tiles.
|
|
10323
|
+
* Includes terrain tiles that are either:
|
|
10324
|
+
* - the same as the tileID
|
|
10325
|
+
* - a parent of the tileID
|
|
10326
|
+
* - a child of the tileID
|
|
10327
|
+
* @param tileID - the tile to look for
|
|
10328
|
+
* @returns the tiles that were found
|
|
10329
|
+
*/
|
|
10330
|
+
_getTerrainCoordsForRegularTile(tileID) {
|
|
10331
|
+
const coords = {};
|
|
10332
|
+
for (const key of this._renderableTilesKeys) {
|
|
10333
|
+
const terrainTileID = this._tiles[key].tileID;
|
|
10334
|
+
const coord = tileID.clone();
|
|
10335
|
+
const mat = createMat4f64();
|
|
10336
|
+
if (terrainTileID.canonical.equals(tileID.canonical)) ortho(mat, 0, EXTENT, EXTENT, 0, 0, 1);
|
|
10337
|
+
else if (terrainTileID.canonical.isChildOf(tileID.canonical)) {
|
|
10338
|
+
const dz = terrainTileID.canonical.z - tileID.canonical.z;
|
|
10339
|
+
const dx = terrainTileID.canonical.x - (terrainTileID.canonical.x >> dz << dz);
|
|
10340
|
+
const dy = terrainTileID.canonical.y - (terrainTileID.canonical.y >> dz << dz);
|
|
10341
|
+
const size = EXTENT >> dz;
|
|
10342
|
+
ortho(mat, 0, size, size, 0, 0, 1);
|
|
10343
|
+
translate(mat, mat, [
|
|
10344
|
+
-dx * size,
|
|
10345
|
+
-dy * size,
|
|
10346
|
+
0
|
|
10347
|
+
]);
|
|
10348
|
+
} else if (tileID.canonical.isChildOf(terrainTileID.canonical)) {
|
|
10349
|
+
const dz = tileID.canonical.z - terrainTileID.canonical.z;
|
|
10350
|
+
const dx = tileID.canonical.x - (tileID.canonical.x >> dz << dz);
|
|
10351
|
+
const dy = tileID.canonical.y - (tileID.canonical.y >> dz << dz);
|
|
10352
|
+
const size = EXTENT >> dz;
|
|
10353
|
+
ortho(mat, 0, EXTENT, EXTENT, 0, 0, 1);
|
|
10354
|
+
translate(mat, mat, [
|
|
10355
|
+
dx * size,
|
|
10356
|
+
dy * size,
|
|
10357
|
+
0
|
|
10358
|
+
]);
|
|
10359
|
+
scale(mat, mat, [
|
|
10360
|
+
1 / 2 ** dz,
|
|
10361
|
+
1 / 2 ** dz,
|
|
10362
|
+
0
|
|
10363
|
+
]);
|
|
10364
|
+
} else continue;
|
|
10365
|
+
coord.terrainRttPosMatrix32f = new Float32Array(mat);
|
|
10366
|
+
coords[key] = coord;
|
|
10367
|
+
}
|
|
10368
|
+
return coords;
|
|
9932
10369
|
}
|
|
9933
|
-
|
|
9934
|
-
|
|
10370
|
+
/**
|
|
10371
|
+
* Searches for the corresponding current renderable terrain-tiles.
|
|
10372
|
+
* Includes terrain tiles that are within terrain tile ranges.
|
|
10373
|
+
* @param tileID - the tile to look for
|
|
10374
|
+
* @returns the tiles that were found
|
|
10375
|
+
*/
|
|
10376
|
+
_getTerrainCoordsForTileRanges(tileID, terrainTileRanges) {
|
|
10377
|
+
const coords = {};
|
|
10378
|
+
for (const key of this._renderableTilesKeys) {
|
|
10379
|
+
const terrainTileID = this._tiles[key].tileID;
|
|
10380
|
+
if (!this._isWithinTileRanges(terrainTileID, terrainTileRanges)) continue;
|
|
10381
|
+
const coord = tileID.clone();
|
|
10382
|
+
const mat = createMat4f64();
|
|
10383
|
+
if (terrainTileID.canonical.z === tileID.canonical.z) {
|
|
10384
|
+
const dx = tileID.canonical.x - terrainTileID.canonical.x + tileID.wrap * (1 << tileID.canonical.z);
|
|
10385
|
+
const dy = tileID.canonical.y - terrainTileID.canonical.y;
|
|
10386
|
+
ortho(mat, 0, EXTENT, EXTENT, 0, 0, 1);
|
|
10387
|
+
translate(mat, mat, [
|
|
10388
|
+
dx * EXTENT,
|
|
10389
|
+
dy * EXTENT,
|
|
10390
|
+
0
|
|
10391
|
+
]);
|
|
10392
|
+
} else if (terrainTileID.canonical.z > tileID.canonical.z) {
|
|
10393
|
+
const dz = terrainTileID.canonical.z - tileID.canonical.z;
|
|
10394
|
+
const dx = terrainTileID.canonical.x - (terrainTileID.canonical.x >> dz << dz) + tileID.wrap * (1 << terrainTileID.canonical.z);
|
|
10395
|
+
const dy = terrainTileID.canonical.y - (terrainTileID.canonical.y >> dz << dz);
|
|
10396
|
+
const dx2 = tileID.canonical.x - (terrainTileID.canonical.x >> dz);
|
|
10397
|
+
const dy2 = tileID.canonical.y - (terrainTileID.canonical.y >> dz);
|
|
10398
|
+
const size = EXTENT >> dz;
|
|
10399
|
+
ortho(mat, 0, size, size, 0, 0, 1);
|
|
10400
|
+
translate(mat, mat, [
|
|
10401
|
+
-dx * size + dx2 * EXTENT,
|
|
10402
|
+
-dy * size + dy2 * EXTENT,
|
|
10403
|
+
0
|
|
10404
|
+
]);
|
|
10405
|
+
} else {
|
|
10406
|
+
const dz = tileID.canonical.z - terrainTileID.canonical.z;
|
|
10407
|
+
const dx = tileID.canonical.x - (tileID.canonical.x >> dz << dz) + tileID.wrap * (1 << tileID.canonical.z);
|
|
10408
|
+
const dy = tileID.canonical.y - (tileID.canonical.y >> dz << dz);
|
|
10409
|
+
const dx2 = (tileID.canonical.x >> dz) - terrainTileID.canonical.x;
|
|
10410
|
+
const dy2 = (tileID.canonical.y >> dz) - terrainTileID.canonical.y;
|
|
10411
|
+
const size = EXTENT << dz;
|
|
10412
|
+
ortho(mat, 0, size, size, 0, 0, 1);
|
|
10413
|
+
translate(mat, mat, [
|
|
10414
|
+
dx * EXTENT + dx2 * size,
|
|
10415
|
+
dy * EXTENT + dy2 * size,
|
|
10416
|
+
0
|
|
10417
|
+
]);
|
|
10418
|
+
}
|
|
10419
|
+
coord.terrainRttPosMatrix32f = new Float32Array(mat);
|
|
10420
|
+
coords[key] = coord;
|
|
10421
|
+
}
|
|
10422
|
+
return coords;
|
|
9935
10423
|
}
|
|
9936
|
-
|
|
9937
|
-
|
|
10424
|
+
/**
|
|
10425
|
+
* find the covering raster-dem tile
|
|
10426
|
+
* @param tileID - the tile to look for
|
|
10427
|
+
* @param searchForDEM - Optional parameter to search for (parent) source tiles with loaded dem.
|
|
10428
|
+
* @returns the tile
|
|
10429
|
+
*/
|
|
10430
|
+
getSourceTile(tileID, searchForDEM) {
|
|
10431
|
+
const source = this.tileManager._source;
|
|
10432
|
+
let z = tileID.overscaledZ - this.deltaZoom;
|
|
10433
|
+
if (z > source.maxzoom) z = source.maxzoom;
|
|
10434
|
+
if (z < source.minzoom) return void 0;
|
|
10435
|
+
this._sourceTileCache[tileID.key] ||= tileID.scaledTo(z).key;
|
|
10436
|
+
let tile = this.findTileInCaches(this._sourceTileCache[tileID.key]);
|
|
10437
|
+
if (!tile?.dem && searchForDEM) while (z >= source.minzoom && !tile?.dem) tile = this.findTileInCaches(tileID.scaledTo(z--).key);
|
|
10438
|
+
return tile;
|
|
9938
10439
|
}
|
|
9939
|
-
|
|
9940
|
-
this.
|
|
10440
|
+
findTileInCaches(key) {
|
|
10441
|
+
let tile = this.tileManager.getTileByID(key);
|
|
10442
|
+
if (tile) return tile;
|
|
10443
|
+
tile = this.tileManager._outOfViewCache.getByKey(key);
|
|
10444
|
+
return tile;
|
|
9941
10445
|
}
|
|
9942
|
-
|
|
9943
|
-
|
|
10446
|
+
/**
|
|
10447
|
+
* gets whether any tiles were loaded after a specific time. This is used to update the depth framebuffer.
|
|
10448
|
+
* @param time - the time
|
|
10449
|
+
* @returns true if any tiles came into view at or after the specified time
|
|
10450
|
+
*/
|
|
10451
|
+
anyTilesAfterTime(time = now()) {
|
|
10452
|
+
return this._lastTilesetChange >= time;
|
|
9944
10453
|
}
|
|
9945
|
-
|
|
9946
|
-
|
|
10454
|
+
/**
|
|
10455
|
+
* Checks whether a tile is within the canonical tile ranges.
|
|
10456
|
+
* @param tileID - Tile to check
|
|
10457
|
+
* @param canonicalTileRanges - Canonical tile ranges
|
|
10458
|
+
* @returns
|
|
10459
|
+
*/
|
|
10460
|
+
_isWithinTileRanges(tileID, canonicalTileRanges) {
|
|
10461
|
+
const range = canonicalTileRanges[tileID.canonical.z];
|
|
10462
|
+
return !!range && (tileID.wrap > range.minWrap || tileID.wrap < range.maxWrap || tileID.canonical.x >= range.minTileXWrapped && tileID.canonical.x <= range.maxTileXWrapped && tileID.canonical.y >= range.minTileY && tileID.canonical.y <= range.maxTileY);
|
|
9947
10463
|
}
|
|
9948
|
-
|
|
9949
|
-
|
|
10464
|
+
};
|
|
10465
|
+
//#endregion
|
|
10466
|
+
//#region src/render/terrain.ts
|
|
10467
|
+
const MAX_BISECTIONS = 40;
|
|
10468
|
+
const HIT_EPSILON_M = 1e-6;
|
|
10469
|
+
/** Keeps the elevation bracket non-degenerate when the terrain is entirely flat, such as unloaded DEMs. */
|
|
10470
|
+
const BRACKET_PADDING_M = 10;
|
|
10471
|
+
/** `DEMData.sampleBilinear` throws on the far tile edge, so samples stop just short of it. */
|
|
10472
|
+
const MAX_TILE_COORD = EXTENT * (1 - 1e-12);
|
|
10473
|
+
/**
|
|
10474
|
+
* @internal
|
|
10475
|
+
* This is the main class which handles most of the 3D Terrain logic. It has the following topics:
|
|
10476
|
+
*
|
|
10477
|
+
* 1. loads raster-dem tiles via the internal tileManager this.tileManager
|
|
10478
|
+
* 2. creates a depth-framebuffer, which is used to calculate the visibility of coordinates
|
|
10479
|
+
* 3. stores all render-to-texture tiles in the this.tileManager._tiles
|
|
10480
|
+
* 4. calculates the elevation for a specific tile-coordinate
|
|
10481
|
+
* 5. creates a terrain-mesh
|
|
10482
|
+
*
|
|
10483
|
+
* A note about the GPU resource-usage:
|
|
10484
|
+
*
|
|
10485
|
+
* Framebuffers:
|
|
10486
|
+
*
|
|
10487
|
+
* - one for the depth framebuffer with the size of the map-div.
|
|
10488
|
+
* - one for rendering a tile to texture with the size of tileSize (= 512x512).
|
|
10489
|
+
*
|
|
10490
|
+
* Textures:
|
|
10491
|
+
*
|
|
10492
|
+
* - one texture for an empty raster-dem tile with size 1x1
|
|
10493
|
+
* - one texture for an empty depth-buffer, when terrain is disabled with size 1x1
|
|
10494
|
+
* - one texture for an each loaded raster-dem with size of the source.tileSize
|
|
10495
|
+
* - one texture for the depth-framebuffer with the size of the map-div.
|
|
10496
|
+
* - finally for each render-to-texture tile (= this._tiles) a set of textures
|
|
10497
|
+
* for each render stack (The stack-concept is documented in painter.ts).
|
|
10498
|
+
*
|
|
10499
|
+
* Normally there exists 1-3 Textures per tile, depending on the stylesheet.
|
|
10500
|
+
* Each Textures has the size 2*tileSize (= 1024x1024). Also there exists a
|
|
10501
|
+
* cache of the last 150 newest rendered tiles.
|
|
10502
|
+
*
|
|
10503
|
+
*/
|
|
10504
|
+
var Terrain = class {
|
|
10505
|
+
constructor(painter, tileManager, options, terrainSkirtLength = "auto") {
|
|
10506
|
+
this._meshCache = {};
|
|
10507
|
+
this.painter = painter;
|
|
10508
|
+
this.tileManager = new TerrainTileManager(tileManager);
|
|
10509
|
+
this.options = options;
|
|
10510
|
+
this.exaggeration = typeof options.exaggeration === "number" ? options.exaggeration : 1;
|
|
10511
|
+
this._terrainSkirtLength = terrainSkirtLength;
|
|
10512
|
+
this.qualityFactor = 2;
|
|
10513
|
+
this.meshSize = 128;
|
|
10514
|
+
this._demMatrixCache = /* @__PURE__ */ new Map();
|
|
10515
|
+
this._elevationSamplerCache = /* @__PURE__ */ new Map();
|
|
9950
10516
|
}
|
|
9951
|
-
|
|
9952
|
-
this.
|
|
10517
|
+
destroy() {
|
|
10518
|
+
if (this._fbo) {
|
|
10519
|
+
this._fbo.destroy();
|
|
10520
|
+
this._fbo = null;
|
|
10521
|
+
}
|
|
10522
|
+
if (this._fboDepthTexture) {
|
|
10523
|
+
this._fboDepthTexture.destroy();
|
|
10524
|
+
this._fboDepthTexture = null;
|
|
10525
|
+
}
|
|
10526
|
+
if (this._emptyDemTexture) {
|
|
10527
|
+
this._emptyDemTexture.destroy();
|
|
10528
|
+
this._emptyDemTexture = null;
|
|
10529
|
+
}
|
|
10530
|
+
if (this._emptyDepthTexture) {
|
|
10531
|
+
this._emptyDepthTexture.destroy();
|
|
10532
|
+
this._emptyDepthTexture = null;
|
|
10533
|
+
}
|
|
10534
|
+
for (const key in this._meshCache) this._meshCache[key].destroy();
|
|
10535
|
+
this._meshCache = {};
|
|
10536
|
+
this.tileManager.destruct();
|
|
9953
10537
|
}
|
|
9954
|
-
|
|
9955
|
-
|
|
10538
|
+
/**
|
|
10539
|
+
* Get the elevation-value from original dem-data for a given tile-coordinate.
|
|
10540
|
+
* Coordinates that fall outside `[0, extent)` are normalized to the
|
|
10541
|
+
* appropriate neighbor tile before lookup.
|
|
10542
|
+
* @param tileID - the tile to get the elevation for
|
|
10543
|
+
* @param x - x coordinate relative to the tile, may be outside `[0, extent)`
|
|
10544
|
+
* @param y - y coordinate relative to the tile, may be outside `[0, extent)`
|
|
10545
|
+
* @param extent - optional, default 8192
|
|
10546
|
+
* @returns the elevation
|
|
10547
|
+
*/
|
|
10548
|
+
getDEMElevation(tileID, x, y, extent = EXTENT) {
|
|
10549
|
+
const normalized = tileID.normalizeCoordinates(x, y, extent);
|
|
10550
|
+
if (!normalized) return 0;
|
|
10551
|
+
const sampler = this.getElevationSampler(normalized.tileID);
|
|
10552
|
+
return sampler ? sampler(normalized.x, normalized.y, extent) : 0;
|
|
9956
10553
|
}
|
|
9957
|
-
|
|
9958
|
-
|
|
9959
|
-
|
|
9960
|
-
|
|
9961
|
-
|
|
10554
|
+
/**
|
|
10555
|
+
* Get the elevation for given {@link LngLat} in respect of exaggeration.
|
|
10556
|
+
* @param lnglat - the location
|
|
10557
|
+
* @param zoom - the zoom, use {@link getElevationForLngLat} if you don't want a specific zoom level, but more accurate results.
|
|
10558
|
+
* @returns the elevation
|
|
10559
|
+
*/
|
|
10560
|
+
getElevationForLngLatZoom(lnglat, zoom) {
|
|
10561
|
+
if (!isInBoundsForZoomLngLat(zoom, lnglat.wrap())) return 0;
|
|
10562
|
+
const { tileID, mercatorX, mercatorY } = this._getOverscaledTileIDFromLngLatZoom(lnglat, zoom);
|
|
10563
|
+
return this.getElevation(tileID, mercatorX % EXTENT, mercatorY % EXTENT, EXTENT);
|
|
9962
10564
|
}
|
|
9963
|
-
|
|
9964
|
-
|
|
10565
|
+
/**
|
|
10566
|
+
* Get the elevation for given {@link LngLat} in respect of exaggeration.
|
|
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.
|
|
10570
|
+
* @param lnglat - the location
|
|
10571
|
+
* @returns the elevation
|
|
10572
|
+
*/
|
|
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
|
+
}
|
|
10580
|
+
const terrainCoveringTiles = coveringTiles(transform, {
|
|
10581
|
+
maxzoom: this.tileManager.maxzoom,
|
|
10582
|
+
minzoom: this.tileManager.minzoom,
|
|
10583
|
+
tileSize: 512,
|
|
10584
|
+
terrain: this
|
|
10585
|
+
});
|
|
10586
|
+
let zoom = 0;
|
|
10587
|
+
for (const tile of terrainCoveringTiles) if (tile.canonical.z > zoom) zoom = Math.min(tile.canonical.z, this.tileManager.maxzoom);
|
|
10588
|
+
return this.getElevationForLngLatZoom(lnglat, zoom);
|
|
9965
10589
|
}
|
|
9966
|
-
|
|
9967
|
-
|
|
10590
|
+
/**
|
|
10591
|
+
* Get the elevation for given coordinate in respect of exaggeration.
|
|
10592
|
+
* @param tileID - the tile id
|
|
10593
|
+
* @param x - x coordinate relative to the tile, may be outside `[0, extent)`
|
|
10594
|
+
* @param y - y coordinate relative to the tile, may be outside `[0, extent)`
|
|
10595
|
+
* @param extent - optional, default 8192
|
|
10596
|
+
* @returns the elevation
|
|
10597
|
+
*/
|
|
10598
|
+
getElevation(tileID, x, y, extent = EXTENT) {
|
|
10599
|
+
return this.getDEMElevation(tileID, x, y, extent) * this.exaggeration;
|
|
9968
10600
|
}
|
|
9969
|
-
|
|
9970
|
-
|
|
10601
|
+
/**
|
|
10602
|
+
* Clear CPU elevation samplers that may retain a previously selected DEM tile.
|
|
10603
|
+
* @internal
|
|
10604
|
+
*/
|
|
10605
|
+
resetElevationCache() {
|
|
10606
|
+
this._elevationSamplerCache.clear();
|
|
10607
|
+
this._coverageIndex = void 0;
|
|
10608
|
+
}
|
|
10609
|
+
/**
|
|
10610
|
+
* Index of the tiles the terrain currently renders, for sampling the terrain surface on the CPU.
|
|
10611
|
+
* Built on first use and kept until {@link resetElevationCache}.
|
|
10612
|
+
* @returns the index, or null when no terrain tile is renderable
|
|
10613
|
+
*/
|
|
10614
|
+
getCoverageIndex() {
|
|
10615
|
+
if (this._coverageIndex === void 0) this._coverageIndex = this._buildCoverageIndex();
|
|
10616
|
+
return this._coverageIndex;
|
|
10617
|
+
}
|
|
10618
|
+
_buildCoverageIndex() {
|
|
10619
|
+
const zooms = [];
|
|
10620
|
+
const samplerPerTile = /* @__PURE__ */ new Map();
|
|
10621
|
+
let minElevation = 0;
|
|
10622
|
+
let maxElevation = 0;
|
|
10623
|
+
for (const tile of this.tileManager.getRenderableTiles()) {
|
|
10624
|
+
if (!tile) continue;
|
|
10625
|
+
const { canonical, wrap } = tile.tileID;
|
|
10626
|
+
if (!zooms.includes(canonical.z)) zooms.push(canonical.z);
|
|
10627
|
+
const sampler = this.getElevationSampler(tile.tileID);
|
|
10628
|
+
samplerPerTile.set(`${wrap}/${canonical.z}/${canonical.x}/${canonical.y}`, sampler);
|
|
10629
|
+
const { minElevation: tileMin, maxElevation: tileMax } = this.getMinMaxElevation(tile.tileID);
|
|
10630
|
+
minElevation = Math.min(minElevation, tileMin ?? 0);
|
|
10631
|
+
maxElevation = Math.max(maxElevation, tileMax ?? 0);
|
|
10632
|
+
}
|
|
10633
|
+
if (samplerPerTile.size === 0) return null;
|
|
10634
|
+
zooms.sort((a, b) => b - a);
|
|
10635
|
+
return {
|
|
10636
|
+
zooms,
|
|
10637
|
+
samplerPerTile,
|
|
10638
|
+
minElevation: minElevation - BRACKET_PADDING_M,
|
|
10639
|
+
maxElevation: maxElevation + BRACKET_PADDING_M
|
|
10640
|
+
};
|
|
9971
10641
|
}
|
|
9972
|
-
|
|
9973
|
-
|
|
10642
|
+
/**
|
|
10643
|
+
* Get a function that samples the raw DEM elevation of a tile, without exaggeration.
|
|
10644
|
+
* @param tileID - the tile id
|
|
10645
|
+
* @returns the sampler, or null when the tile's DEM data is not loaded
|
|
10646
|
+
*/
|
|
10647
|
+
getElevationSampler(tileID) {
|
|
10648
|
+
const key = tileID.key;
|
|
10649
|
+
const cachedSampler = this._elevationSamplerCache.get(key);
|
|
10650
|
+
if (cachedSampler) return cachedSampler;
|
|
10651
|
+
const sourceTile = this.tileManager.getSourceTile(tileID, true);
|
|
10652
|
+
const dem = sourceTile?.dem;
|
|
10653
|
+
if (!sourceTile || !dem) return null;
|
|
10654
|
+
const matrix = this._getDEMTileMatrix(tileID, sourceTile);
|
|
10655
|
+
const demPixelScaleX = matrix[0] * dem.dim;
|
|
10656
|
+
const demPixelScaleY = matrix[5] * dem.dim;
|
|
10657
|
+
const demPixelOffsetX = matrix[12] * dem.dim;
|
|
10658
|
+
const demPixelOffsetY = matrix[13] * dem.dim;
|
|
10659
|
+
const sampler = (x, y, extent) => {
|
|
10660
|
+
const extentScale = extent === 8192 ? 1 : EXTENT / extent;
|
|
10661
|
+
return dem.sampleBilinear(x * extentScale * demPixelScaleX + demPixelOffsetX, y * extentScale * demPixelScaleY + demPixelOffsetY);
|
|
10662
|
+
};
|
|
10663
|
+
this._elevationSamplerCache.set(key, sampler);
|
|
10664
|
+
return sampler;
|
|
9974
10665
|
}
|
|
9975
|
-
|
|
9976
|
-
|
|
10666
|
+
_getDEMTileMatrix(tileID, sourceTile) {
|
|
10667
|
+
const matrixKey = `${sourceTile.tileID.key}/${tileID.key}`;
|
|
10668
|
+
const cachedMatrix = this._demMatrixCache.get(matrixKey);
|
|
10669
|
+
if (cachedMatrix) return cachedMatrix;
|
|
10670
|
+
const maxzoom = this.tileManager.getSource().maxzoom;
|
|
10671
|
+
let dz = tileID.canonical.z - sourceTile.tileID.canonical.z;
|
|
10672
|
+
if (tileID.overscaledZ > tileID.canonical.z) {
|
|
10673
|
+
if (tileID.canonical.z >= maxzoom) dz = tileID.canonical.z - maxzoom;
|
|
10674
|
+
else warnOnce("cannot calculate elevation if elevation maxzoom > source.maxzoom");
|
|
10675
|
+
}
|
|
10676
|
+
const dx = tileID.canonical.x - (tileID.canonical.x >> dz << dz);
|
|
10677
|
+
const dy = tileID.canonical.y - (tileID.canonical.y >> dz << dz);
|
|
10678
|
+
const demMatrix = fromScaling(/* @__PURE__ */ new Float64Array(16), [
|
|
10679
|
+
1 / (EXTENT << dz),
|
|
10680
|
+
1 / (EXTENT << dz),
|
|
10681
|
+
0
|
|
10682
|
+
]);
|
|
10683
|
+
translate(demMatrix, demMatrix, [
|
|
10684
|
+
dx * EXTENT,
|
|
10685
|
+
dy * EXTENT,
|
|
10686
|
+
0
|
|
10687
|
+
]);
|
|
10688
|
+
this._demMatrixCache.set(matrixKey, demMatrix);
|
|
10689
|
+
return demMatrix;
|
|
9977
10690
|
}
|
|
9978
|
-
|
|
9979
|
-
|
|
10691
|
+
/**
|
|
10692
|
+
* returns a Terrain Object for a tile. Unless the tile corresponds to data (e.g. tile is loading), return a flat dem object
|
|
10693
|
+
* @param tileID - the tile to get the terrain for
|
|
10694
|
+
* @returns the terrain data to use in the program
|
|
10695
|
+
*/
|
|
10696
|
+
getTerrainData(tileID) {
|
|
10697
|
+
if (!this._emptyDemTexture) {
|
|
10698
|
+
const context = this.painter.context;
|
|
10699
|
+
const image = new RGBAImage({
|
|
10700
|
+
width: 1,
|
|
10701
|
+
height: 1
|
|
10702
|
+
}, /* @__PURE__ */ new Uint8Array(4));
|
|
10703
|
+
this._emptyDepthTexture = new Texture(context, image, context.gl.RGBA, { premultiply: false });
|
|
10704
|
+
this._emptyDemUnpack = [
|
|
10705
|
+
0,
|
|
10706
|
+
0,
|
|
10707
|
+
0,
|
|
10708
|
+
0
|
|
10709
|
+
];
|
|
10710
|
+
this._emptyDemTexture = new Texture(context, new RGBAImage({
|
|
10711
|
+
width: 1,
|
|
10712
|
+
height: 1
|
|
10713
|
+
}), context.gl.RGBA, { premultiply: false });
|
|
10714
|
+
this._emptyDemTexture.bind(context.gl.NEAREST, context.gl.CLAMP_TO_EDGE);
|
|
10715
|
+
this._emptyDemMatrix = identity([]);
|
|
10716
|
+
}
|
|
10717
|
+
const sourceTile = this.tileManager.getSourceTile(tileID, true);
|
|
10718
|
+
if (sourceTile?.dem && (!sourceTile.demTexture || sourceTile.needsTerrainPrepare)) {
|
|
10719
|
+
const context = this.painter.context;
|
|
10720
|
+
sourceTile.demTexture ||= this.painter.getTileTexture(sourceTile.dem.stride);
|
|
10721
|
+
if (sourceTile.demTexture) sourceTile.demTexture.update(sourceTile.dem.getPixels(), { premultiply: false });
|
|
10722
|
+
else sourceTile.demTexture = new Texture(context, sourceTile.dem.getPixels(), context.gl.RGBA, { premultiply: false });
|
|
10723
|
+
sourceTile.demTexture.bind(context.gl.NEAREST, context.gl.CLAMP_TO_EDGE);
|
|
10724
|
+
sourceTile.needsTerrainPrepare = false;
|
|
10725
|
+
}
|
|
10726
|
+
const terrainMatrix = sourceTile ? this._getDEMTileMatrix(tileID, sourceTile) : this._emptyDemMatrix;
|
|
10727
|
+
return {
|
|
10728
|
+
"u_depth": 2,
|
|
10729
|
+
"u_terrain": 3,
|
|
10730
|
+
"u_terrain_dim": sourceTile?.dem?.dim || 1,
|
|
10731
|
+
"u_terrain_matrix": terrainMatrix,
|
|
10732
|
+
"u_terrain_unpack": sourceTile?.dem?.getUnpackVector() || this._emptyDemUnpack,
|
|
10733
|
+
"u_terrain_exaggeration": this.exaggeration,
|
|
10734
|
+
texture: (sourceTile?.demTexture || this._emptyDemTexture).texture,
|
|
10735
|
+
depthTexture: (this._fboDepthTexture || this._emptyDepthTexture).texture,
|
|
10736
|
+
tile: sourceTile
|
|
10737
|
+
};
|
|
9980
10738
|
}
|
|
9981
|
-
|
|
9982
|
-
|
|
10739
|
+
/**
|
|
10740
|
+
* get a framebuffer as big as the map-div, which will be used to render depth into a texture
|
|
10741
|
+
* @returns the frame buffer
|
|
10742
|
+
*/
|
|
10743
|
+
getFramebuffer() {
|
|
10744
|
+
const painter = this.painter;
|
|
10745
|
+
const width = painter.width / devicePixelRatio;
|
|
10746
|
+
const height = painter.height / devicePixelRatio;
|
|
10747
|
+
if (this._fbo && (this._fbo.width !== width || this._fbo.height !== height)) {
|
|
10748
|
+
this._fbo.destroy();
|
|
10749
|
+
this._fboDepthTexture.destroy();
|
|
10750
|
+
delete this._fbo;
|
|
10751
|
+
delete this._fboDepthTexture;
|
|
10752
|
+
}
|
|
10753
|
+
if (!this._fboDepthTexture) {
|
|
10754
|
+
this._fboDepthTexture = new Texture(painter.context, {
|
|
10755
|
+
width,
|
|
10756
|
+
height,
|
|
10757
|
+
data: null
|
|
10758
|
+
}, painter.context.gl.RGBA, { premultiply: false });
|
|
10759
|
+
this._fboDepthTexture.bind(painter.context.gl.NEAREST, painter.context.gl.CLAMP_TO_EDGE);
|
|
10760
|
+
}
|
|
10761
|
+
if (!this._fbo) {
|
|
10762
|
+
this._fbo = painter.context.createFramebuffer(width, height, true, false);
|
|
10763
|
+
this._fbo.depthAttachment.set(painter.context.createRenderbuffer(painter.context.gl.DEPTH_COMPONENT16, width, height));
|
|
10764
|
+
}
|
|
10765
|
+
this._fbo.colorAttachment.set(this._fboDepthTexture.texture);
|
|
10766
|
+
return this._fbo;
|
|
9983
10767
|
}
|
|
9984
|
-
|
|
9985
|
-
|
|
10768
|
+
/**
|
|
10769
|
+
* Reads the depth value from the depth-framebuffer at a given screen pixel
|
|
10770
|
+
* @param p - Screen coordinate
|
|
10771
|
+
* @returns depth value in clip space (between 0 and 1)
|
|
10772
|
+
*/
|
|
10773
|
+
depthAtPoint(p) {
|
|
10774
|
+
const rgba = /* @__PURE__ */ new Uint8Array(4);
|
|
10775
|
+
const context = this.painter.context, gl = context.gl;
|
|
10776
|
+
context.bindFramebuffer.set(this.getFramebuffer().framebuffer);
|
|
10777
|
+
gl.readPixels(p.x, this.painter.height / devicePixelRatio - p.y - 1, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, rgba);
|
|
10778
|
+
context.bindFramebuffer.set(null);
|
|
10779
|
+
return (rgba[0] / 16777216 + rgba[1] / 65536 + rgba[2] / 256 + rgba[3]) / 256;
|
|
9986
10780
|
}
|
|
9987
|
-
|
|
9988
|
-
|
|
10781
|
+
/**
|
|
10782
|
+
* create a regular mesh which will be used by all terrain-tiles
|
|
10783
|
+
* @returns the created regular mesh
|
|
10784
|
+
*/
|
|
10785
|
+
getTerrainMesh(tileId) {
|
|
10786
|
+
const globeEnabled = this.painter.style.projection?.transitionState > 0;
|
|
10787
|
+
const northPole = globeEnabled && tileId.canonical.y === 0;
|
|
10788
|
+
const southPole = globeEnabled && tileId.canonical.y === (1 << tileId.canonical.z) - 1;
|
|
10789
|
+
const key = `m_${northPole ? "n" : ""}_${southPole ? "s" : ""}`;
|
|
10790
|
+
if (this._meshCache[key]) return this._meshCache[key];
|
|
10791
|
+
const context = this.painter.context;
|
|
10792
|
+
const vertexArray = new Pos3dArray();
|
|
10793
|
+
const indexArray = new TriangleIndexArray();
|
|
10794
|
+
const meshSize = this.meshSize;
|
|
10795
|
+
const delta = EXTENT / meshSize;
|
|
10796
|
+
const meshSize2 = meshSize * meshSize;
|
|
10797
|
+
for (let y = 0; y <= meshSize; y++) for (let x = 0; x <= meshSize; x++) vertexArray.emplaceBack(x * delta, y * delta, 0);
|
|
10798
|
+
for (let y = 0; y < meshSize2; y += meshSize + 1) for (let x = 0; x < meshSize; x++) {
|
|
10799
|
+
indexArray.emplaceBack(x + y, meshSize + x + y + 1, meshSize + x + y + 2);
|
|
10800
|
+
indexArray.emplaceBack(x + y, meshSize + x + y + 2, x + y + 1);
|
|
10801
|
+
}
|
|
10802
|
+
if (this._terrainSkirtLength !== "none") this._buildSkirts(vertexArray, indexArray, meshSize, delta, northPole, southPole);
|
|
10803
|
+
const mesh = new Mesh(context.createVertexBuffer(vertexArray, pos3dAttributes.members), context.createIndexBuffer(indexArray), SegmentVector.simpleSegment(0, 0, vertexArray.length, indexArray.length));
|
|
10804
|
+
this._meshCache[key] = mesh;
|
|
10805
|
+
return mesh;
|
|
9989
10806
|
}
|
|
9990
|
-
|
|
9991
|
-
|
|
10807
|
+
/**
|
|
10808
|
+
* Calculates the height of the tile skirts for the "auto" strategy.
|
|
10809
|
+
* @see {@link MapOptions.terrainSkirtLength}
|
|
10810
|
+
* @param zoom - current zoomlevel
|
|
10811
|
+
* @returns the elevation delta in meters
|
|
10812
|
+
*/
|
|
10813
|
+
getSkirtLength(zoom) {
|
|
10814
|
+
return 2 * Math.PI * earthRadius / Math.pow(2, Math.max(zoom, 0)) / 5;
|
|
9992
10815
|
}
|
|
9993
|
-
|
|
9994
|
-
return
|
|
10816
|
+
getMinTileElevationForLngLatZoom(lnglat, zoom) {
|
|
10817
|
+
if (!isInBoundsForZoomLngLat(zoom, lnglat.wrap())) return 0;
|
|
10818
|
+
const { tileID } = this._getOverscaledTileIDFromLngLatZoom(lnglat, zoom);
|
|
10819
|
+
return this.getMinMaxElevation(tileID).minElevation ?? 0;
|
|
9995
10820
|
}
|
|
9996
|
-
|
|
9997
|
-
|
|
10821
|
+
/**
|
|
10822
|
+
* Get the minimum and maximum elevation contained in a tile. This includes any
|
|
10823
|
+
* exaggeration included in the terrain.
|
|
10824
|
+
*
|
|
10825
|
+
* @param tileID - ID of the tile to be used as a source for the min/max elevation
|
|
10826
|
+
* @returns the minimum and maximum elevation found in the tile, including the terrain's
|
|
10827
|
+
* exaggeration
|
|
10828
|
+
*/
|
|
10829
|
+
getMinMaxElevation(tileID) {
|
|
10830
|
+
const tile = this.tileManager.getSourceTile(tileID, true);
|
|
10831
|
+
const minMax = {
|
|
10832
|
+
minElevation: null,
|
|
10833
|
+
maxElevation: null
|
|
10834
|
+
};
|
|
10835
|
+
if (tile?.dem) {
|
|
10836
|
+
minMax.minElevation = tile.dem.min * this.exaggeration;
|
|
10837
|
+
minMax.maxElevation = tile.dem.max * this.exaggeration;
|
|
10838
|
+
}
|
|
10839
|
+
return minMax;
|
|
10840
|
+
}
|
|
10841
|
+
_getOverscaledTileIDFromLngLatZoom(lnglat, zoom) {
|
|
10842
|
+
const mercatorCoordinate = MercatorCoordinate.fromLngLat(lnglat.wrap());
|
|
10843
|
+
const worldSize = (1 << zoom) * EXTENT;
|
|
10844
|
+
const mercatorX = mercatorCoordinate.x * worldSize;
|
|
10845
|
+
const mercatorY = mercatorCoordinate.y * worldSize;
|
|
10846
|
+
const tileX = Math.floor(mercatorX / EXTENT), tileY = Math.floor(mercatorY / EXTENT);
|
|
10847
|
+
return {
|
|
10848
|
+
tileID: new OverscaledTileID(zoom, 0, zoom, tileX, tileY),
|
|
10849
|
+
mercatorX,
|
|
10850
|
+
mercatorY
|
|
10851
|
+
};
|
|
10852
|
+
}
|
|
10853
|
+
/** Add an extra frame around the mesh to avoid hairline gaps (stitching) on tile boundaries with different zoomlevels.
|
|
10854
|
+
* @see {@link MapOptions.terrainSkirtLength}
|
|
10855
|
+
*/
|
|
10856
|
+
_buildSkirts(vertexArray, indexArray, meshSize, delta, northPole, southPole) {
|
|
10857
|
+
const offsetTop = vertexArray.length;
|
|
10858
|
+
const offsetTopEdge = 0;
|
|
10859
|
+
const offsetBottom = offsetTop + (meshSize + 1);
|
|
10860
|
+
const offsetBottomEdge = (meshSize + 1) * meshSize;
|
|
10861
|
+
const northY = northPole ? NORTH_POLE_Y : 0;
|
|
10862
|
+
const northZ = northPole ? 0 : 1;
|
|
10863
|
+
const southY = southPole ? SOUTH_POLE_Y : EXTENT;
|
|
10864
|
+
const southZ = southPole ? 0 : 1;
|
|
10865
|
+
for (let x = 0; x <= meshSize; x++) vertexArray.emplaceBack(x * delta, northY, northZ);
|
|
10866
|
+
for (let x = 0; x <= meshSize; x++) vertexArray.emplaceBack(x * delta, southY, southZ);
|
|
10867
|
+
for (let x = 0; x < meshSize; x++) {
|
|
10868
|
+
indexArray.emplaceBack(offsetBottomEdge + x, offsetBottom + x, offsetBottom + x + 1);
|
|
10869
|
+
indexArray.emplaceBack(offsetBottomEdge + x, offsetBottom + x + 1, offsetBottomEdge + x + 1);
|
|
10870
|
+
indexArray.emplaceBack(offsetTopEdge + x, offsetTop + x + 1, offsetTop + x);
|
|
10871
|
+
indexArray.emplaceBack(offsetTopEdge + x, offsetTopEdge + x + 1, offsetTop + x + 1);
|
|
10872
|
+
}
|
|
10873
|
+
const offsetLeft = vertexArray.length;
|
|
10874
|
+
const offsetRight = offsetLeft + (meshSize + 1) * 2;
|
|
10875
|
+
for (const x of [0, 1]) for (let y = 0; y <= meshSize; y++) for (const z of [0, 1]) vertexArray.emplaceBack(x * EXTENT, y * delta, z);
|
|
10876
|
+
for (let y = 0; y < meshSize * 2; y += 2) {
|
|
10877
|
+
indexArray.emplaceBack(offsetLeft + y, offsetLeft + y + 1, offsetLeft + y + 3);
|
|
10878
|
+
indexArray.emplaceBack(offsetLeft + y, offsetLeft + y + 3, offsetLeft + y + 2);
|
|
10879
|
+
indexArray.emplaceBack(offsetRight + y, offsetRight + y + 3, offsetRight + y + 1);
|
|
10880
|
+
indexArray.emplaceBack(offsetRight + y, offsetRight + y + 2, offsetRight + y + 3);
|
|
10881
|
+
}
|
|
10882
|
+
}
|
|
10883
|
+
};
|
|
10884
|
+
const NOT_COVERED = {
|
|
10885
|
+
covered: false,
|
|
10886
|
+
demLoaded: false,
|
|
10887
|
+
elevation: 0
|
|
10888
|
+
};
|
|
10889
|
+
/**
|
|
10890
|
+
* Elevation of the rendered terrain surface at a mercator position, and whether it is covered at all.
|
|
10891
|
+
* A covered tile whose DEM has not loaded yet is flat at zero, which is what the terrain mesh renders.
|
|
10892
|
+
*/
|
|
10893
|
+
function sampleAt(index, exaggeration, mercatorX, mercatorY) {
|
|
10894
|
+
if (mercatorY < 0 || mercatorY >= 1) return NOT_COVERED;
|
|
10895
|
+
const wrap = Math.floor(mercatorX);
|
|
10896
|
+
const wrappedX = mercatorX - wrap;
|
|
10897
|
+
for (const z of index.zooms) {
|
|
10898
|
+
const scale = 1 << z;
|
|
10899
|
+
const scaledX = wrappedX * scale;
|
|
10900
|
+
const scaledY = mercatorY * scale;
|
|
10901
|
+
const tileX = Math.floor(scaledX);
|
|
10902
|
+
const tileY = Math.floor(scaledY);
|
|
10903
|
+
const key = `${wrap}/${z}/${tileX}/${tileY}`;
|
|
10904
|
+
if (!index.samplerPerTile.has(key)) continue;
|
|
10905
|
+
const sampler = index.samplerPerTile.get(key);
|
|
10906
|
+
if (!sampler) return {
|
|
10907
|
+
covered: true,
|
|
10908
|
+
demLoaded: false,
|
|
10909
|
+
elevation: 0
|
|
10910
|
+
};
|
|
10911
|
+
return {
|
|
10912
|
+
covered: true,
|
|
10913
|
+
demLoaded: true,
|
|
10914
|
+
elevation: sampler(Math.min((scaledX - tileX) * EXTENT, MAX_TILE_COORD), Math.min((scaledY - tileY) * EXTENT, MAX_TILE_COORD), EXTENT) * exaggeration
|
|
10915
|
+
};
|
|
10916
|
+
}
|
|
10917
|
+
return NOT_COVERED;
|
|
10918
|
+
}
|
|
10919
|
+
/**
|
|
10920
|
+
* Whether a height in meters is at or below the sampled terrain surface.
|
|
10921
|
+
* The epsilon absorbs rounding when a bracket endpoint lands exactly on the surface.
|
|
10922
|
+
*/
|
|
10923
|
+
function isBelowTerrainSample(sample, height) {
|
|
10924
|
+
return sample.covered && height <= sample.elevation + HIT_EPSILON_M;
|
|
10925
|
+
}
|
|
10926
|
+
/**
|
|
10927
|
+
* Narrows the bracket `[lo, hi]` around the surface crossing until it is shorter than `tolerance` in ray parameter units.
|
|
10928
|
+
*/
|
|
10929
|
+
function bisect(ray, isBelowTerrain, lo, hi, tolerance) {
|
|
10930
|
+
for (let j = 0; j < MAX_BISECTIONS && hi - lo > tolerance; j++) {
|
|
10931
|
+
const mid = (lo + hi) / 2;
|
|
10932
|
+
if (isBelowTerrain(ray, mid)) hi = mid;
|
|
10933
|
+
else lo = mid;
|
|
10934
|
+
}
|
|
10935
|
+
return {
|
|
10936
|
+
lo,
|
|
10937
|
+
hi
|
|
10938
|
+
};
|
|
10939
|
+
}
|
|
10940
|
+
//#endregion
|
|
10941
|
+
//#region src/geo/projection/mercator_transform.ts
|
|
10942
|
+
const TARGET_WORLD_STEP_PX = 4;
|
|
10943
|
+
const MAX_SAMPLES = 512;
|
|
10944
|
+
const MERCATOR_BISECT_EPSILON_WORLD_PX = .001;
|
|
10945
|
+
var MercatorTransform = class MercatorTransform {
|
|
10946
|
+
get pixelsToClipSpaceMatrix() {
|
|
10947
|
+
return this._helper.pixelsToClipSpaceMatrix;
|
|
10948
|
+
}
|
|
10949
|
+
get clipSpaceToPixelsMatrix() {
|
|
10950
|
+
return this._helper.clipSpaceToPixelsMatrix;
|
|
10951
|
+
}
|
|
10952
|
+
get pixelsToGLUnits() {
|
|
10953
|
+
return this._helper.pixelsToGLUnits;
|
|
10954
|
+
}
|
|
10955
|
+
get centerOffset() {
|
|
10956
|
+
return this._helper.centerOffset;
|
|
10957
|
+
}
|
|
10958
|
+
get size() {
|
|
10959
|
+
return this._helper.size;
|
|
10960
|
+
}
|
|
10961
|
+
get rotationMatrix() {
|
|
10962
|
+
return this._helper.rotationMatrix;
|
|
10963
|
+
}
|
|
10964
|
+
get centerPoint() {
|
|
10965
|
+
return this._helper.centerPoint;
|
|
10966
|
+
}
|
|
10967
|
+
get pixelsPerMeter() {
|
|
10968
|
+
return this._helper.pixelsPerMeter;
|
|
10969
|
+
}
|
|
10970
|
+
setMinZoom(zoom) {
|
|
10971
|
+
this._helper.setMinZoom(zoom);
|
|
10972
|
+
}
|
|
10973
|
+
setMaxZoom(zoom) {
|
|
10974
|
+
this._helper.setMaxZoom(zoom);
|
|
10975
|
+
}
|
|
10976
|
+
setMinPitch(pitch) {
|
|
10977
|
+
this._helper.setMinPitch(pitch);
|
|
10978
|
+
}
|
|
10979
|
+
setMaxPitch(pitch) {
|
|
10980
|
+
this._helper.setMaxPitch(pitch);
|
|
10981
|
+
}
|
|
10982
|
+
setRenderWorldCopies(renderWorldCopies) {
|
|
10983
|
+
this._helper.setRenderWorldCopies(renderWorldCopies);
|
|
10984
|
+
}
|
|
10985
|
+
setBearing(bearing) {
|
|
10986
|
+
this._helper.setBearing(bearing);
|
|
10987
|
+
}
|
|
10988
|
+
setPitch(pitch) {
|
|
10989
|
+
this._helper.setPitch(pitch);
|
|
10990
|
+
}
|
|
10991
|
+
setRoll(roll) {
|
|
10992
|
+
this._helper.setRoll(roll);
|
|
10993
|
+
}
|
|
10994
|
+
setFov(fov) {
|
|
10995
|
+
this._helper.setFov(fov);
|
|
10996
|
+
}
|
|
10997
|
+
setZoom(zoom) {
|
|
10998
|
+
this._helper.setZoom(zoom);
|
|
10999
|
+
}
|
|
11000
|
+
setCenter(center) {
|
|
11001
|
+
this._helper.setCenter(center);
|
|
11002
|
+
}
|
|
11003
|
+
setElevation(elevation) {
|
|
11004
|
+
this._helper.setElevation(elevation);
|
|
11005
|
+
}
|
|
11006
|
+
setMinElevationForCurrentTile(elevation) {
|
|
11007
|
+
this._helper.setMinElevationForCurrentTile(elevation);
|
|
11008
|
+
}
|
|
11009
|
+
setPadding(padding) {
|
|
11010
|
+
this._helper.setPadding(padding);
|
|
11011
|
+
}
|
|
11012
|
+
interpolatePadding(start, target, t) {
|
|
11013
|
+
this._helper.interpolatePadding(start, target, t);
|
|
11014
|
+
}
|
|
11015
|
+
isPaddingEqual(padding) {
|
|
11016
|
+
return this._helper.isPaddingEqual(padding);
|
|
11017
|
+
}
|
|
11018
|
+
resize(width, height, constrain = true) {
|
|
11019
|
+
this._helper.resize(width, height, constrain);
|
|
11020
|
+
}
|
|
11021
|
+
getMaxBounds() {
|
|
11022
|
+
return this._helper.getMaxBounds();
|
|
11023
|
+
}
|
|
11024
|
+
setMaxBounds(bounds) {
|
|
11025
|
+
this._helper.setMaxBounds(bounds);
|
|
11026
|
+
}
|
|
11027
|
+
setConstrainOverride(constrain) {
|
|
11028
|
+
this._helper.setConstrainOverride(constrain);
|
|
11029
|
+
}
|
|
11030
|
+
overrideNearFarZ(nearZ, farZ) {
|
|
11031
|
+
this._helper.overrideNearFarZ(nearZ, farZ);
|
|
11032
|
+
}
|
|
11033
|
+
clearNearFarZOverride() {
|
|
11034
|
+
this._helper.clearNearFarZOverride();
|
|
11035
|
+
}
|
|
11036
|
+
getCameraQueryGeometry(queryGeometry) {
|
|
11037
|
+
return this._helper.getCameraQueryGeometry(this.getCameraPoint(), queryGeometry);
|
|
11038
|
+
}
|
|
11039
|
+
get tileSize() {
|
|
11040
|
+
return this._helper.tileSize;
|
|
11041
|
+
}
|
|
11042
|
+
get tileZoom() {
|
|
11043
|
+
return this._helper.tileZoom;
|
|
11044
|
+
}
|
|
11045
|
+
get scale() {
|
|
11046
|
+
return this._helper.scale;
|
|
11047
|
+
}
|
|
11048
|
+
get worldSize() {
|
|
11049
|
+
return this._helper.worldSize;
|
|
11050
|
+
}
|
|
11051
|
+
get width() {
|
|
11052
|
+
return this._helper.width;
|
|
11053
|
+
}
|
|
11054
|
+
get height() {
|
|
11055
|
+
return this._helper.height;
|
|
9998
11056
|
}
|
|
9999
11057
|
get lngRange() {
|
|
10000
11058
|
return this._helper.lngRange;
|
|
@@ -10197,7 +11255,7 @@ var MercatorTransform = class MercatorTransform {
|
|
|
10197
11255
|
}
|
|
10198
11256
|
recalculateZoomAndCenter(terrain) {
|
|
10199
11257
|
const center = this.screenPointToLocation(this.centerPoint, terrain);
|
|
10200
|
-
const elevation = terrain ? terrain.
|
|
11258
|
+
const elevation = terrain ? terrain.getElevationForLngLat(center, this) : 0;
|
|
10201
11259
|
this._helper.recalculateZoomAndCenter(elevation);
|
|
10202
11260
|
}
|
|
10203
11261
|
/**
|
|
@@ -10228,17 +11286,64 @@ var MercatorTransform = class MercatorTransform {
|
|
|
10228
11286
|
}
|
|
10229
11287
|
screenPointToMercatorCoordinate(p, terrain) {
|
|
10230
11288
|
if (terrain) {
|
|
10231
|
-
const coordinate =
|
|
11289
|
+
const coordinate = this.screenTerrainPointToMercatorCoordinate(p, terrain);
|
|
10232
11290
|
if (coordinate != null) return coordinate;
|
|
10233
11291
|
}
|
|
10234
11292
|
return this.screenPointToMercatorCoordinateAtZ(p);
|
|
10235
11293
|
}
|
|
11294
|
+
/** {@inheritDoc ITransform.screenTerrainPointToMercatorCoordinate} */
|
|
11295
|
+
screenTerrainPointToMercatorCoordinate(p, terrain) {
|
|
11296
|
+
const index = terrain.getCoverageIndex();
|
|
11297
|
+
if (!index) return null;
|
|
11298
|
+
const { near, far } = this.getRaySegmentFromPixel(p);
|
|
11299
|
+
const worldSize = this.worldSize;
|
|
11300
|
+
const dx = far[0] - near[0];
|
|
11301
|
+
const dy = far[1] - near[1];
|
|
11302
|
+
const dz = far[2] - near[2];
|
|
11303
|
+
const ray = {
|
|
11304
|
+
index,
|
|
11305
|
+
exaggeration: terrain.exaggeration,
|
|
11306
|
+
near,
|
|
11307
|
+
dx,
|
|
11308
|
+
dy,
|
|
11309
|
+
dz,
|
|
11310
|
+
worldSize
|
|
11311
|
+
};
|
|
11312
|
+
let tStart = 0;
|
|
11313
|
+
let tEnd = 1;
|
|
11314
|
+
if (dz === 0) {
|
|
11315
|
+
if (near[2] > index.maxElevation || near[2] < index.minElevation) return null;
|
|
11316
|
+
} else {
|
|
11317
|
+
const tHigh = (index.maxElevation - near[2]) / dz;
|
|
11318
|
+
const tLow = (index.minElevation - near[2]) / dz;
|
|
11319
|
+
tStart = Math.max(tStart, Math.min(tHigh, tLow));
|
|
11320
|
+
tEnd = Math.min(tEnd, Math.max(tHigh, tLow));
|
|
11321
|
+
if (tStart > tEnd) return null;
|
|
11322
|
+
}
|
|
11323
|
+
const horizontalLength = Math.hypot(dx, dy);
|
|
11324
|
+
const samples = clamp(Math.ceil(horizontalLength * (tEnd - tStart) / TARGET_WORLD_STEP_PX), 1, MAX_SAMPLES);
|
|
11325
|
+
let previousT = 0;
|
|
11326
|
+
let aboveTerrain = !mercatorIsBelowTerrain(ray, 0);
|
|
11327
|
+
for (let i = 0; i <= samples; i++) {
|
|
11328
|
+
const t = tStart + (tEnd - tStart) * i / samples;
|
|
11329
|
+
if (!aboveTerrain) aboveTerrain = !mercatorIsBelowTerrain(ray, t);
|
|
11330
|
+
else if (mercatorIsBelowTerrain(ray, t)) {
|
|
11331
|
+
const { lo, hi } = bisect(ray, mercatorIsBelowTerrain, previousT, t, MERCATOR_BISECT_EPSILON_WORLD_PX / horizontalLength);
|
|
11332
|
+
const sampleLo = mercatorSampleAt(ray, lo);
|
|
11333
|
+
const sampleHi = mercatorSampleAt(ray, hi);
|
|
11334
|
+
const fLo = near[2] + lo * dz - sampleLo.elevation;
|
|
11335
|
+
const fHi = near[2] + hi * dz - sampleHi.elevation;
|
|
11336
|
+
const hit = sampleLo.covered && fLo > fHi ? clamp(lo + fLo * (hi - lo) / (fLo - fHi), lo, hi) : hi;
|
|
11337
|
+
return new MercatorCoordinate((near[0] + hit * dx) / worldSize, (near[1] + hit * dy) / worldSize, mercatorSampleAt(ray, hit).elevation);
|
|
11338
|
+
}
|
|
11339
|
+
previousT = t;
|
|
11340
|
+
}
|
|
11341
|
+
return null;
|
|
11342
|
+
}
|
|
10236
11343
|
/**
|
|
10237
|
-
*
|
|
10238
|
-
* given in meters relative to the plane at the center's elevation (not mercator units).
|
|
11344
|
+
* Returns the segment of the ray through the given screen pixel that lies inside the view frustum.
|
|
10239
11345
|
*/
|
|
10240
|
-
|
|
10241
|
-
const targetZ = z ? z : 0;
|
|
11346
|
+
getRaySegmentFromPixel(p) {
|
|
10242
11347
|
const coord0 = [
|
|
10243
11348
|
p.x,
|
|
10244
11349
|
p.y,
|
|
@@ -10255,14 +11360,29 @@ var MercatorTransform = class MercatorTransform {
|
|
|
10255
11360
|
transformMat4(coord1, coord1, this._pixelMatrixInverse);
|
|
10256
11361
|
const w0 = coord0[3];
|
|
10257
11362
|
const w1 = coord1[3];
|
|
10258
|
-
const
|
|
10259
|
-
|
|
10260
|
-
|
|
10261
|
-
|
|
10262
|
-
|
|
10263
|
-
|
|
10264
|
-
|
|
10265
|
-
|
|
11363
|
+
const elevation = this.elevation;
|
|
11364
|
+
return {
|
|
11365
|
+
near: [
|
|
11366
|
+
coord0[0] / w0,
|
|
11367
|
+
coord0[1] / w0,
|
|
11368
|
+
coord0[2] / w0 + elevation
|
|
11369
|
+
],
|
|
11370
|
+
far: [
|
|
11371
|
+
coord1[0] / w1,
|
|
11372
|
+
coord1[1] / w1,
|
|
11373
|
+
coord1[2] / w1 + elevation
|
|
11374
|
+
]
|
|
11375
|
+
};
|
|
11376
|
+
}
|
|
11377
|
+
/**
|
|
11378
|
+
* Intersects the ray through a screen point with the horizontal plane at `z`,
|
|
11379
|
+
* given in meters relative to the plane at the center's elevation (not mercator units).
|
|
11380
|
+
*/
|
|
11381
|
+
screenPointToMercatorCoordinateAtZ(p, z) {
|
|
11382
|
+
const targetZ = z ? z : 0;
|
|
11383
|
+
const { near, far } = this.getRaySegmentFromPixel(p);
|
|
11384
|
+
const t = near[2] === far[2] ? 0 : (targetZ + this.elevation - near[2]) / (far[2] - near[2]);
|
|
11385
|
+
return new MercatorCoordinate(interpolateFactory.number(near[0], far[0], t) / this.worldSize, interpolateFactory.number(near[1], far[1], t) / this.worldSize, targetZ);
|
|
10266
11386
|
}
|
|
10267
11387
|
/**
|
|
10268
11388
|
* Given a coordinate, return the screen point that corresponds to it
|
|
@@ -10286,7 +11406,7 @@ var MercatorTransform = class MercatorTransform {
|
|
|
10286
11406
|
return new LngLatBounds().extend(this.screenPointToLocation(new Point(0, top))).extend(this.screenPointToLocation(new Point(this._helper._width, top))).extend(this.screenPointToLocation(new Point(this._helper._width, this._helper._height))).extend(this.screenPointToLocation(new Point(0, this._helper._height)));
|
|
10287
11407
|
}
|
|
10288
11408
|
isPointOnMapSurface(p, terrain) {
|
|
10289
|
-
if (terrain) return
|
|
11409
|
+
if (terrain) return this.screenTerrainPointToMercatorCoordinate(p, terrain) != null;
|
|
10290
11410
|
return p.y > this.height / 2 - getMercatorHorizon(this);
|
|
10291
11411
|
}
|
|
10292
11412
|
calculatePosMatrix(tileID, aligned = false, useFloat32 = false) {
|
|
@@ -10523,14 +11643,14 @@ var MercatorTransform = class MercatorTransform {
|
|
|
10523
11643
|
getRayDirectionFromPixel(_p) {
|
|
10524
11644
|
throw new Error("Not implemented.");
|
|
10525
11645
|
}
|
|
10526
|
-
projectTileCoordinates(x, y, unwrappedTileID,
|
|
11646
|
+
projectTileCoordinates(x, y, unwrappedTileID, elevation) {
|
|
10527
11647
|
const matrix = this.calculatePosMatrix(unwrappedTileID);
|
|
10528
11648
|
let pos;
|
|
10529
|
-
if (
|
|
11649
|
+
if (elevation != null) {
|
|
10530
11650
|
pos = [
|
|
10531
11651
|
x,
|
|
10532
11652
|
y,
|
|
10533
|
-
|
|
11653
|
+
elevation,
|
|
10534
11654
|
1
|
|
10535
11655
|
];
|
|
10536
11656
|
transformMat4(pos, pos, matrix);
|
|
@@ -10584,6 +11704,12 @@ var MercatorTransform = class MercatorTransform {
|
|
|
10584
11704
|
return this.calculatePosMatrix(tileID);
|
|
10585
11705
|
}
|
|
10586
11706
|
};
|
|
11707
|
+
function mercatorSampleAt(ray, t) {
|
|
11708
|
+
return sampleAt(ray.index, ray.exaggeration, (ray.near[0] + t * ray.dx) / ray.worldSize, (ray.near[1] + t * ray.dy) / ray.worldSize);
|
|
11709
|
+
}
|
|
11710
|
+
function mercatorIsBelowTerrain(ray, t) {
|
|
11711
|
+
return isBelowTerrainSample(mercatorSampleAt(ray, t), ray.near[2] + t * ray.dz);
|
|
11712
|
+
}
|
|
10587
11713
|
//#endregion
|
|
10588
11714
|
//#region src/geo/projection/camera_helper.ts
|
|
10589
11715
|
/**
|
|
@@ -11234,6 +12360,36 @@ function interpolateLngLatForGlobe(start, deltaLng, deltaLat, t) {
|
|
|
11234
12360
|
return new LngLat(interpolatedLng, interpolatedLat);
|
|
11235
12361
|
}
|
|
11236
12362
|
}
|
|
12363
|
+
/**
|
|
12364
|
+
* Returns the two intersection points of the ray and a sphere centered at the origin,
|
|
12365
|
+
* or null if no intersection occurs.
|
|
12366
|
+
* The intersections are encoded as the parameter for parametric ray equation,
|
|
12367
|
+
* with `tMin` being the first intersection and `tMax` being the second.
|
|
12368
|
+
* The quadratic is solved as suggested in Ray Tracing Gems, chapter 7, since the
|
|
12369
|
+
* schoolbook approach leads to floating point precision issues:
|
|
12370
|
+
* https://www.realtimerendering.com/raytracinggems/rtg/index.html
|
|
12371
|
+
* @param origin - The ray origin.
|
|
12372
|
+
* @param direction - The normalized ray direction.
|
|
12373
|
+
* @param radius - The sphere radius, defaults to the unit sphere of the planet.
|
|
12374
|
+
*/
|
|
12375
|
+
function raySphereIntersection(origin, direction, radius = 1) {
|
|
12376
|
+
const originDotDirection = dot(origin, direction);
|
|
12377
|
+
const radiusSquared = radius * radius;
|
|
12378
|
+
const inner = createVec3f64();
|
|
12379
|
+
const scaledDir = createVec3f64();
|
|
12380
|
+
scale$2(scaledDir, direction, originDotDirection);
|
|
12381
|
+
sub(inner, origin, scaledDir);
|
|
12382
|
+
const discriminant = radiusSquared - dot(inner, inner);
|
|
12383
|
+
if (discriminant < 0) return null;
|
|
12384
|
+
const c = dot(origin, origin) - radiusSquared;
|
|
12385
|
+
const q = -originDotDirection + (originDotDirection < 0 ? 1 : -1) * Math.sqrt(discriminant);
|
|
12386
|
+
const t0 = c / q;
|
|
12387
|
+
const t1 = q;
|
|
12388
|
+
return {
|
|
12389
|
+
tMin: Math.min(t0, t1),
|
|
12390
|
+
tMax: Math.max(t0, t1)
|
|
12391
|
+
};
|
|
12392
|
+
}
|
|
11237
12393
|
//#endregion
|
|
11238
12394
|
//#region src/util/primitives/bounding_volume_cache.ts
|
|
11239
12395
|
var BoundingVolumeCache = class {
|
|
@@ -11669,6 +12825,10 @@ function findAxisMinMax(axis, points) {
|
|
|
11669
12825
|
}
|
|
11670
12826
|
//#endregion
|
|
11671
12827
|
//#region src/geo/projection/vertical_perspective_transform.ts
|
|
12828
|
+
const GLOBE_SAMPLES = 256;
|
|
12829
|
+
const GLOBE_BISECT_EPSILON_T = 1e-12;
|
|
12830
|
+
/** Latitudes outside the mercator range project past the world edge; the globe mesh still covers them. */
|
|
12831
|
+
const MAX_MERCATOR_Y = 1 - 1e-9;
|
|
11672
12832
|
var VerticalPerspectiveTransform = class VerticalPerspectiveTransform {
|
|
11673
12833
|
get pixelsToClipSpaceMatrix() {
|
|
11674
12834
|
return this._helper.pixelsToClipSpaceMatrix;
|
|
@@ -12008,10 +13168,10 @@ var VerticalPerspectiveTransform = class VerticalPerspectiveTransform {
|
|
|
12008
13168
|
const angular = mercatorCoordinatesToAngularCoordinatesRadians(mercator.x, mercator.y);
|
|
12009
13169
|
return this.getCircleRadiusCorrection() / Math.cos(angular[1]);
|
|
12010
13170
|
}
|
|
12011
|
-
projectTileCoordinates(x, y, unwrappedTileID,
|
|
13171
|
+
projectTileCoordinates(x, y, unwrappedTileID, elevation) {
|
|
12012
13172
|
const canonical = unwrappedTileID.canonical;
|
|
12013
13173
|
const spherePos = projectTileCoordinatesToSphere(x, y, canonical.x, canonical.y, canonical.z);
|
|
12014
|
-
const vectorMultiplier = 1 + (
|
|
13174
|
+
const vectorMultiplier = 1 + (elevation ?? 0) / earthRadius;
|
|
12015
13175
|
const pos = [
|
|
12016
13176
|
spherePos[0] * vectorMultiplier,
|
|
12017
13177
|
spherePos[1] * vectorMultiplier,
|
|
@@ -12271,11 +13431,41 @@ var VerticalPerspectiveTransform = class VerticalPerspectiveTransform {
|
|
|
12271
13431
|
}
|
|
12272
13432
|
screenPointToMercatorCoordinate(p, terrain) {
|
|
12273
13433
|
if (terrain) {
|
|
12274
|
-
const coordinate =
|
|
13434
|
+
const coordinate = this.screenTerrainPointToMercatorCoordinate(p, terrain);
|
|
12275
13435
|
if (coordinate) return coordinate;
|
|
12276
13436
|
}
|
|
12277
13437
|
return MercatorCoordinate.fromLngLat(this.unprojectScreenPoint(p));
|
|
12278
13438
|
}
|
|
13439
|
+
/** {@inheritDoc ITransform.screenTerrainPointToMercatorCoordinate} */
|
|
13440
|
+
screenTerrainPointToMercatorCoordinate(p, terrain) {
|
|
13441
|
+
const index = terrain.getCoverageIndex();
|
|
13442
|
+
if (!index) return null;
|
|
13443
|
+
const origin = this.cameraPosition;
|
|
13444
|
+
const direction = this.getRayDirectionFromPixel(p);
|
|
13445
|
+
const outer = raySphereIntersection(origin, direction, 1 + index.maxElevation / earthRadius);
|
|
13446
|
+
if (!outer) return null;
|
|
13447
|
+
const inner = raySphereIntersection(origin, direction, 1 + index.minElevation / earthRadius);
|
|
13448
|
+
const tStart = Math.max(outer.tMin, 0);
|
|
13449
|
+
const tEnd = inner ? inner.tMin : outer.tMax;
|
|
13450
|
+
if (tEnd <= tStart) return null;
|
|
13451
|
+
const ray = {
|
|
13452
|
+
index,
|
|
13453
|
+
exaggeration: terrain.exaggeration,
|
|
13454
|
+
origin,
|
|
13455
|
+
direction
|
|
13456
|
+
};
|
|
13457
|
+
let previousT = 0;
|
|
13458
|
+
for (let i = 0; i <= GLOBE_SAMPLES; i++) {
|
|
13459
|
+
const t = tStart + (tEnd - tStart) * i / GLOBE_SAMPLES;
|
|
13460
|
+
if (globeIsBelowTerrain(ray, t)) {
|
|
13461
|
+
const { hi } = bisect(ray, globeIsBelowTerrain, previousT, t, GLOBE_BISECT_EPSILON_T);
|
|
13462
|
+
const { sample, mercator } = globeSampleAt(ray, hi);
|
|
13463
|
+
return new MercatorCoordinate(mercator.x, mercator.y, sample.elevation);
|
|
13464
|
+
}
|
|
13465
|
+
previousT = t;
|
|
13466
|
+
}
|
|
13467
|
+
return null;
|
|
13468
|
+
}
|
|
12279
13469
|
screenPointToLocation(p, terrain) {
|
|
12280
13470
|
return this.screenPointToMercatorCoordinate(p, terrain)?.toLngLat();
|
|
12281
13471
|
}
|
|
@@ -12284,8 +13474,7 @@ var VerticalPerspectiveTransform = class VerticalPerspectiveTransform {
|
|
|
12284
13474
|
}
|
|
12285
13475
|
isPointOnMapSurface(p, _terrain) {
|
|
12286
13476
|
const rayOrigin = this._cameraPosition;
|
|
12287
|
-
|
|
12288
|
-
return !!this.rayPlanetIntersection(rayOrigin, rayDirection);
|
|
13477
|
+
return !!raySphereIntersection(rayOrigin, this.getRayDirectionFromPixel(p));
|
|
12289
13478
|
}
|
|
12290
13479
|
/**
|
|
12291
13480
|
* Computes normalized direction of a ray from the camera to the given screen pixel.
|
|
@@ -12330,33 +13519,6 @@ var VerticalPerspectiveTransform = class VerticalPerspectiveTransform {
|
|
|
12330
13519
|
return projected[0] > -1 && projected[0] < 1 && projected[1] > -1 && projected[1] < 1 && projected[2] > -1 && projected[2] < 1;
|
|
12331
13520
|
}
|
|
12332
13521
|
/**
|
|
12333
|
-
* Returns the two intersection points of the ray and the planet's sphere,
|
|
12334
|
-
* or null if no intersection occurs.
|
|
12335
|
-
* The intersections are encoded as the parameter for parametric ray equation,
|
|
12336
|
-
* with `tMin` being the first intersection and `tMax` being the second.
|
|
12337
|
-
* Eg. the nearer intersection point can then be computed as `origin + direction * tMin`.
|
|
12338
|
-
* @param origin - The ray origin.
|
|
12339
|
-
* @param direction - The normalized ray direction.
|
|
12340
|
-
*/
|
|
12341
|
-
rayPlanetIntersection(origin, direction) {
|
|
12342
|
-
const originDotDirection = dot(origin, direction);
|
|
12343
|
-
const planetRadiusSquared = 1;
|
|
12344
|
-
const inner = createVec3f64();
|
|
12345
|
-
const scaledDir = createVec3f64();
|
|
12346
|
-
scale$2(scaledDir, direction, originDotDirection);
|
|
12347
|
-
sub(inner, origin, scaledDir);
|
|
12348
|
-
const discriminant = planetRadiusSquared - dot(inner, inner);
|
|
12349
|
-
if (discriminant < 0) return null;
|
|
12350
|
-
const c = dot(origin, origin) - planetRadiusSquared;
|
|
12351
|
-
const q = -originDotDirection + (originDotDirection < 0 ? 1 : -1) * Math.sqrt(discriminant);
|
|
12352
|
-
const t0 = c / q;
|
|
12353
|
-
const t1 = q;
|
|
12354
|
-
return {
|
|
12355
|
-
tMin: Math.min(t0, t1),
|
|
12356
|
-
tMax: Math.max(t0, t1)
|
|
12357
|
-
};
|
|
12358
|
-
}
|
|
12359
|
-
/**
|
|
12360
13522
|
* @internal
|
|
12361
13523
|
* Returns a {@link LngLat} representing geographical coordinates that correspond to the specified pixel coordinates.
|
|
12362
13524
|
* Note: if the point does not lie on the globe, returns a location on the visible globe horizon (edge) that is
|
|
@@ -12367,7 +13529,7 @@ var VerticalPerspectiveTransform = class VerticalPerspectiveTransform {
|
|
|
12367
13529
|
unprojectScreenPoint(p) {
|
|
12368
13530
|
const rayOrigin = this._cameraPosition;
|
|
12369
13531
|
const rayDirection = this.getRayDirectionFromPixel(p);
|
|
12370
|
-
const intersection =
|
|
13532
|
+
const intersection = raySphereIntersection(rayOrigin, rayDirection);
|
|
12371
13533
|
if (intersection) {
|
|
12372
13534
|
const intersectionPoint = createVec3f64();
|
|
12373
13535
|
add(intersectionPoint, rayOrigin, [
|
|
@@ -12421,6 +13583,30 @@ var VerticalPerspectiveTransform = class VerticalPerspectiveTransform {
|
|
|
12421
13583
|
}
|
|
12422
13584
|
getFastPathSimpleProjectionMatrix(_tileID) {}
|
|
12423
13585
|
};
|
|
13586
|
+
function globeSampleAt(ray, t) {
|
|
13587
|
+
const position = createVec3f64();
|
|
13588
|
+
scaleAndAdd(position, ray.origin, ray.direction, t);
|
|
13589
|
+
const radius = length$1(position);
|
|
13590
|
+
const surface = createVec3f64();
|
|
13591
|
+
scale$2(surface, position, 1 / radius);
|
|
13592
|
+
const lngLat = sphereSurfacePointToCoordinates(surface);
|
|
13593
|
+
const projected = MercatorCoordinate.fromLngLat(lngLat);
|
|
13594
|
+
const mercator = new MercatorCoordinate(projected.x, clamp(projected.y, 0, MAX_MERCATOR_Y));
|
|
13595
|
+
const sample = sampleAt(ray.index, ray.exaggeration, mercator.x, mercator.y);
|
|
13596
|
+
const elevation = Math.abs(lngLat.lat) > 85.051129 ? 0 : sample.elevation;
|
|
13597
|
+
return {
|
|
13598
|
+
sample: {
|
|
13599
|
+
...sample,
|
|
13600
|
+
elevation
|
|
13601
|
+
},
|
|
13602
|
+
radius,
|
|
13603
|
+
mercator
|
|
13604
|
+
};
|
|
13605
|
+
}
|
|
13606
|
+
function globeIsBelowTerrain(ray, t) {
|
|
13607
|
+
const { sample, radius } = globeSampleAt(ray, t);
|
|
13608
|
+
return isBelowTerrainSample(sample, (radius - 1) * earthRadius);
|
|
13609
|
+
}
|
|
12424
13610
|
//#endregion
|
|
12425
13611
|
//#region src/geo/projection/globe_transform.ts
|
|
12426
13612
|
/**
|
|
@@ -12703,8 +13889,8 @@ var GlobeTransform = class GlobeTransform {
|
|
|
12703
13889
|
const verticalCorrection = this._verticalPerspectiveTransform.getPitchedTextCorrection(textAnchorX, textAnchorY, tileID);
|
|
12704
13890
|
return lerp(mercatorCorrection, verticalCorrection, this._globeness);
|
|
12705
13891
|
}
|
|
12706
|
-
projectTileCoordinates(x, y, unwrappedTileID,
|
|
12707
|
-
return this.currentTransform.projectTileCoordinates(x, y, unwrappedTileID,
|
|
13892
|
+
projectTileCoordinates(x, y, unwrappedTileID, elevation) {
|
|
13893
|
+
return this.currentTransform.projectTileCoordinates(x, y, unwrappedTileID, elevation);
|
|
12708
13894
|
}
|
|
12709
13895
|
_calcMatrices() {
|
|
12710
13896
|
if (!this._helper._width || !this._helper._height) return;
|
|
@@ -12777,6 +13963,10 @@ var GlobeTransform = class GlobeTransform {
|
|
|
12777
13963
|
screenPointToMercatorCoordinate(p, terrain) {
|
|
12778
13964
|
return this.currentTransform.screenPointToMercatorCoordinate(p, terrain);
|
|
12779
13965
|
}
|
|
13966
|
+
/** {@inheritDoc ITransform.screenTerrainPointToMercatorCoordinate} */
|
|
13967
|
+
screenTerrainPointToMercatorCoordinate(p, terrain) {
|
|
13968
|
+
return this.currentTransform.screenTerrainPointToMercatorCoordinate(p, terrain);
|
|
13969
|
+
}
|
|
12780
13970
|
screenPointToLocation(p, terrain) {
|
|
12781
13971
|
return this.currentTransform.screenPointToLocation(p, terrain);
|
|
12782
13972
|
}
|
|
@@ -13286,6 +14476,8 @@ var Style = class extends Evented {
|
|
|
13286
14476
|
this._glyphsDidChange = false;
|
|
13287
14477
|
this._updatedPaintProps = {};
|
|
13288
14478
|
this._layerOrderChanged = false;
|
|
14479
|
+
this._symbolPlacementTriggered = false;
|
|
14480
|
+
this._placedProjectionTransition = void 0;
|
|
13289
14481
|
this.crossTileSymbolIndex = new ((this.crossTileSymbolIndex?.constructor) || Object)();
|
|
13290
14482
|
this.pauseablePlacement = void 0;
|
|
13291
14483
|
this.placement = void 0;
|
|
@@ -13380,6 +14572,7 @@ var Style = class extends Evented {
|
|
|
13380
14572
|
if (nextState.sprite) this._loadSprite(nextState.sprite);
|
|
13381
14573
|
else this.imageManager.setLoaded(true);
|
|
13382
14574
|
this.glyphManager.setURL(nextState.glyphs);
|
|
14575
|
+
this.glyphManager.setFontFaces(nextState["font-faces"]);
|
|
13383
14576
|
this._createLayers();
|
|
13384
14577
|
this.light = new Light(this.stylesheet.light ?? {}, this._globalState);
|
|
13385
14578
|
this._setProjectionInternal(this.stylesheet.projection?.type || "mercator");
|
|
@@ -13643,6 +14836,9 @@ var Style = class extends Evented {
|
|
|
13643
14836
|
case "setGlyphs":
|
|
13644
14837
|
operations.push(() => this.setGlyphs.apply(this, op.args));
|
|
13645
14838
|
break;
|
|
14839
|
+
case "setFontFaces":
|
|
14840
|
+
operations.push(() => this.setFontFaces.apply(this, op.args));
|
|
14841
|
+
break;
|
|
13646
14842
|
case "setSprite":
|
|
13647
14843
|
operations.push(() => this.setSprite.apply(this, op.args));
|
|
13648
14844
|
break;
|
|
@@ -13968,6 +15164,7 @@ var Style = class extends Evented {
|
|
|
13968
15164
|
if (isRasterStyleLayer(layer) && name === "raster-fade-duration") this.tileManagers[layer.source].setRasterFadeDuration(value);
|
|
13969
15165
|
this._changed = true;
|
|
13970
15166
|
this._updatedPaintProps[layer.id] = true;
|
|
15167
|
+
if (layer.type === "symbol") this.triggerSymbolPlacement();
|
|
13971
15168
|
this._serializedLayers = null;
|
|
13972
15169
|
}
|
|
13973
15170
|
getPaintProperty(layer, name) {
|
|
@@ -14066,8 +15263,10 @@ var Style = class extends Evented {
|
|
|
14066
15263
|
pitch: myStyleSheet.pitch,
|
|
14067
15264
|
sprite: myStyleSheet.sprite,
|
|
14068
15265
|
glyphs: myStyleSheet.glyphs,
|
|
15266
|
+
"font-faces": myStyleSheet["font-faces"],
|
|
14069
15267
|
transition: myStyleSheet.transition,
|
|
14070
15268
|
projection: myStyleSheet.projection,
|
|
15269
|
+
state: myStyleSheet.state,
|
|
14071
15270
|
sources,
|
|
14072
15271
|
layers,
|
|
14073
15272
|
terrain
|
|
@@ -14225,6 +15424,7 @@ var Style = class extends Evented {
|
|
|
14225
15424
|
for (const key in this.tileManagers) this.tileManagers[key].reload();
|
|
14226
15425
|
}
|
|
14227
15426
|
_validate(validate, key, value, props, options = {}) {
|
|
15427
|
+
if (options.validate === false) return false;
|
|
14228
15428
|
return validateAndEmit(this, validate, {
|
|
14229
15429
|
key,
|
|
14230
15430
|
style: this.serialize(),
|
|
@@ -14270,6 +15470,26 @@ var Style = class extends Evented {
|
|
|
14270
15470
|
_generateCollisionBoxes() {
|
|
14271
15471
|
for (const id in this.tileManagers) this._reloadSource(id);
|
|
14272
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
|
+
}
|
|
14273
15493
|
_updatePlacement(transform, showCollisionBoxes, fadeDuration, crossSourceCollisions, forceFullPlacement = false) {
|
|
14274
15494
|
let symbolBucketsChanged = false;
|
|
14275
15495
|
let placementCommitted = false;
|
|
@@ -14286,12 +15506,17 @@ var Style = class extends Evented {
|
|
|
14286
15506
|
}
|
|
14287
15507
|
this.crossTileSymbolIndex.pruneUnusedLayers(this._order);
|
|
14288
15508
|
forceFullPlacement ||= this._layerOrderChanged || fadeDuration === 0;
|
|
14289
|
-
|
|
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;
|
|
14290
15514
|
this.pauseablePlacement = new PauseablePlacement(transform, this.map.terrain, this._order, forceFullPlacement, showCollisionBoxes, fadeDuration, crossSourceCollisions, this.placement);
|
|
14291
15515
|
this._layerOrderChanged = false;
|
|
14292
15516
|
}
|
|
14293
|
-
if (this.pauseablePlacement.isDone())
|
|
14294
|
-
|
|
15517
|
+
if (this.pauseablePlacement.isDone()) {
|
|
15518
|
+
if (placementInputsChanged) this.placement.setStale();
|
|
15519
|
+
} else {
|
|
14295
15520
|
this.pauseablePlacement.continuePlacement(this._order, this._layers, layerTiles);
|
|
14296
15521
|
if (this.pauseablePlacement.isDone()) {
|
|
14297
15522
|
this.placement = this.pauseablePlacement.commit(now());
|
|
@@ -14328,11 +15553,22 @@ var Style = class extends Evented {
|
|
|
14328
15553
|
setGlyphs(glyphsUrl, options = {}) {
|
|
14329
15554
|
this._checkLoaded();
|
|
14330
15555
|
if (glyphsUrl && this._validate(validateStyle.glyphs, "glyphs", glyphsUrl, null, options)) return;
|
|
15556
|
+
this._changed = true;
|
|
14331
15557
|
this._glyphsDidChange = true;
|
|
14332
15558
|
this.stylesheet.glyphs = glyphsUrl;
|
|
14333
15559
|
this.glyphManager.entries = {};
|
|
14334
15560
|
this.glyphManager.setURL(glyphsUrl);
|
|
14335
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
|
+
}
|
|
14336
15572
|
async getDashes(mapId, params) {
|
|
14337
15573
|
const result = {};
|
|
14338
15574
|
for (const [key, dash] of Object.entries(params.dashes)) result[key] = this.lineAtlas.getDash(dash.dasharray, dash.round);
|
|
@@ -14556,11 +15792,6 @@ const terrainUniforms = (context, locations) => ({
|
|
|
14556
15792
|
"u_is_globe_mode": new Uniform1f(context, locations.u_is_globe_mode)
|
|
14557
15793
|
});
|
|
14558
15794
|
const terrainDepthUniforms = (context, locations) => ({ "u_ele_delta": new Uniform1f(context, locations.u_ele_delta) });
|
|
14559
|
-
const terrainCoordsUniforms = (context, locations) => ({
|
|
14560
|
-
"u_texture": new Uniform1i(context, locations.u_texture),
|
|
14561
|
-
"u_terrain_coords_id": new Uniform1f(context, locations.u_terrain_coords_id),
|
|
14562
|
-
"u_ele_delta": new Uniform1f(context, locations.u_ele_delta)
|
|
14563
|
-
});
|
|
14564
15795
|
const terrainUniformValues = (eleDelta, fogMatrix, sky, pitch, isGlobeMode) => ({
|
|
14565
15796
|
"u_texture": 0,
|
|
14566
15797
|
"u_ele_delta": eleDelta,
|
|
@@ -14573,11 +15804,6 @@ const terrainUniformValues = (eleDelta, fogMatrix, sky, pitch, isGlobeMode) => (
|
|
|
14573
15804
|
"u_is_globe_mode": isGlobeMode ? 1 : 0
|
|
14574
15805
|
});
|
|
14575
15806
|
const terrainDepthUniformValues = (eleDelta) => ({ "u_ele_delta": eleDelta });
|
|
14576
|
-
const terrainCoordsUniformValues = (coordsId, eleDelta) => ({
|
|
14577
|
-
"u_terrain_coords_id": coordsId / 255,
|
|
14578
|
-
"u_texture": 0,
|
|
14579
|
-
"u_ele_delta": eleDelta
|
|
14580
|
-
});
|
|
14581
15807
|
//#endregion
|
|
14582
15808
|
//#region src/webgl/program/projection_program.ts
|
|
14583
15809
|
const projectionUniforms = (context, locations) => ({
|
|
@@ -15207,7 +16433,8 @@ const symbolIconUniforms = (context, locations) => ({
|
|
|
15207
16433
|
"u_texture": new Uniform1i(context, locations.u_texture),
|
|
15208
16434
|
"u_translation": new Uniform2f(context, locations.u_translation),
|
|
15209
16435
|
"u_pitched_scale": new Uniform1f(context, locations.u_pitched_scale),
|
|
15210
|
-
"u_is_offset": new Uniform1i(context, locations.u_is_offset)
|
|
16436
|
+
"u_is_offset": new Uniform1i(context, locations.u_is_offset),
|
|
16437
|
+
"u_height_anchor_ground": new Uniform1i(context, locations.u_height_anchor_ground)
|
|
15211
16438
|
});
|
|
15212
16439
|
const symbolSDFUniforms = (context, locations) => ({
|
|
15213
16440
|
"u_is_size_zoom_constant": new Uniform1i(context, locations.u_is_size_zoom_constant),
|
|
@@ -15233,7 +16460,8 @@ const symbolSDFUniforms = (context, locations) => ({
|
|
|
15233
16460
|
"u_is_plain": new Uniform1i(context, locations.u_is_plain),
|
|
15234
16461
|
"u_translation": new Uniform2f(context, locations.u_translation),
|
|
15235
16462
|
"u_pitched_scale": new Uniform1f(context, locations.u_pitched_scale),
|
|
15236
|
-
"u_is_offset": new Uniform1i(context, locations.u_is_offset)
|
|
16463
|
+
"u_is_offset": new Uniform1i(context, locations.u_is_offset),
|
|
16464
|
+
"u_height_anchor_ground": new Uniform1i(context, locations.u_height_anchor_ground)
|
|
15237
16465
|
});
|
|
15238
16466
|
const symbolTextAndIconUniforms = (context, locations) => ({
|
|
15239
16467
|
"u_is_size_zoom_constant": new Uniform1i(context, locations.u_is_size_zoom_constant),
|
|
@@ -15260,9 +16488,10 @@ const symbolTextAndIconUniforms = (context, locations) => ({
|
|
|
15260
16488
|
"u_is_halo": new Uniform1i(context, locations.u_is_halo),
|
|
15261
16489
|
"u_translation": new Uniform2f(context, locations.u_translation),
|
|
15262
16490
|
"u_pitched_scale": new Uniform1f(context, locations.u_pitched_scale),
|
|
15263
|
-
"u_is_offset": new Uniform1i(context, locations.u_is_offset)
|
|
16491
|
+
"u_is_offset": new Uniform1i(context, locations.u_is_offset),
|
|
16492
|
+
"u_height_anchor_ground": new Uniform1i(context, locations.u_height_anchor_ground)
|
|
15264
16493
|
});
|
|
15265
|
-
const symbolIconUniformValues = (functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, isText, texSize, pitchedScale, isOffset) => {
|
|
16494
|
+
const symbolIconUniformValues = (functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, isText, texSize, pitchedScale, isOffset, heightAnchorGround) => {
|
|
15266
16495
|
const transform = painter.transform;
|
|
15267
16496
|
return {
|
|
15268
16497
|
"u_is_size_zoom_constant": +(functionType === "constant" || functionType === "source"),
|
|
@@ -15284,20 +16513,21 @@ const symbolIconUniformValues = (functionType, size, rotateInShader, pitchWithMa
|
|
|
15284
16513
|
"u_texture": 0,
|
|
15285
16514
|
"u_translation": translation,
|
|
15286
16515
|
"u_pitched_scale": pitchedScale,
|
|
15287
|
-
"u_is_offset": isOffset
|
|
16516
|
+
"u_is_offset": isOffset,
|
|
16517
|
+
"u_height_anchor_ground": +heightAnchorGround
|
|
15288
16518
|
};
|
|
15289
16519
|
};
|
|
15290
|
-
const symbolSDFUniformValues = (functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, isText, texSize, isHalo, pitchedScale, isOffset) => {
|
|
16520
|
+
const symbolSDFUniformValues = (functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, isText, texSize, isHalo, pitchedScale, isOffset, heightAnchorGround) => {
|
|
15291
16521
|
const transform = painter.transform;
|
|
15292
|
-
return extend(symbolIconUniformValues(functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, isText, texSize, pitchedScale, isOffset), {
|
|
16522
|
+
return extend(symbolIconUniformValues(functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, isText, texSize, pitchedScale, isOffset, heightAnchorGround), {
|
|
15293
16523
|
"u_gamma_scale": pitchWithMap ? Math.cos(transform.pitch * Math.PI / 180) * transform.cameraToCenterDistance : 1,
|
|
15294
16524
|
"u_device_pixel_ratio": painter.pixelRatio,
|
|
15295
16525
|
"u_is_halo": isHalo ? 1 : 0,
|
|
15296
16526
|
"u_is_plain": 1
|
|
15297
16527
|
});
|
|
15298
16528
|
};
|
|
15299
|
-
const symbolTextAndIconUniformValues = (functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, texSizeSDF, texSizeIcon, pitchedScale, isOffset) => {
|
|
15300
|
-
return extend(symbolSDFUniformValues(functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, true, texSizeSDF, true, pitchedScale, isOffset), {
|
|
16529
|
+
const symbolTextAndIconUniformValues = (functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, texSizeSDF, texSizeIcon, pitchedScale, isOffset, heightAnchorGround) => {
|
|
16530
|
+
return extend(symbolSDFUniformValues(functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, true, texSizeSDF, true, pitchedScale, isOffset, heightAnchorGround), {
|
|
15301
16531
|
"u_texsize_icon": texSizeIcon,
|
|
15302
16532
|
"u_texture_icon": 1
|
|
15303
16533
|
});
|
|
@@ -15409,7 +16639,6 @@ const programUniforms = {
|
|
|
15409
16639
|
backgroundPattern: backgroundPatternUniforms,
|
|
15410
16640
|
terrain: terrainUniforms,
|
|
15411
16641
|
terrainDepth: terrainDepthUniforms,
|
|
15412
|
-
terrainCoords: terrainCoordsUniforms,
|
|
15413
16642
|
atmosphere: atmosphereUniforms,
|
|
15414
16643
|
sky: skyUniforms
|
|
15415
16644
|
};
|
|
@@ -16383,7 +17612,7 @@ function drawSymbols(painter, tileManager, layer, coords, variableOffsets, rende
|
|
|
16383
17612
|
const stencilMode = StencilMode.disabled;
|
|
16384
17613
|
const colorMode = painter.colorModeForRenderPass();
|
|
16385
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);
|
|
16386
|
-
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);
|
|
16387
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);
|
|
16388
17617
|
if (tileManager.map.showCollisionBoxes) {
|
|
16389
17618
|
drawCollisionDebug(painter, tileManager, layer, coords, true);
|
|
@@ -16412,8 +17641,8 @@ function updateVariableAnchors(coords, painter, layer, tileManager, rotationAlig
|
|
|
16412
17641
|
const updateTextFitIcon = layer.layout.get("icon-text-fit") !== "none" && bucket.hasIconData();
|
|
16413
17642
|
if (size) {
|
|
16414
17643
|
const tileScale = Math.pow(2, transform.zoom - tile.tileID.overscaledZ);
|
|
16415
|
-
const getElevation = terrain ? (x, y) => terrain.getElevation(coord, x, y) :
|
|
16416
|
-
updateVariableAnchorsForBucket(bucket, rotateWithMap, pitchWithMap, variableOffsets, transform, pitchedLabelPlaneMatrix, tileScale, size, updateTextFitIcon, translatePosition(transform, tile, translate, translateAnchor), coord.toUnwrapped(), getElevation);
|
|
17644
|
+
const getElevation = terrain ? (x, y) => terrain.getElevation(coord, x, y) : void 0;
|
|
17645
|
+
updateVariableAnchorsForBucket(bucket, rotateWithMap, pitchWithMap, variableOffsets, transform, pitchedLabelPlaneMatrix, tileScale, size, updateTextFitIcon, translatePosition(transform, tile, translate, translateAnchor), coord.toUnwrapped(), getElevation, layer.layout.get("symbol-height-anchor") === "ground");
|
|
16417
17646
|
}
|
|
16418
17647
|
}
|
|
16419
17648
|
}
|
|
@@ -16423,14 +17652,14 @@ function getShiftedAnchor(projectedAnchorPoint, projectionContext, rotateWithMap
|
|
|
16423
17652
|
let adjustedShift = shift.mult(pitchedTextShiftCorrection);
|
|
16424
17653
|
if (!rotateWithMap) adjustedShift = adjustedShift.rotate(-transformAngle);
|
|
16425
17654
|
const tileAnchorShifted = translatedAnchor.add(adjustedShift);
|
|
16426
|
-
return projectWithMatrix(tileAnchorShifted.x, tileAnchorShifted.y, projectionContext.pitchedLabelPlaneMatrix, projectionContext.
|
|
17655
|
+
return projectWithMatrix(tileAnchorShifted.x, tileAnchorShifted.y, projectionContext.pitchedLabelPlaneMatrix, elevationAt(projectionContext, tileAnchorShifted.x, tileAnchorShifted.y)).point;
|
|
16427
17656
|
} else if (rotateWithMap) {
|
|
16428
17657
|
const east = projectTileCoordinatesToLabelPlane(projectionContext.tileAnchorPoint.x + 1, projectionContext.tileAnchorPoint.y, projectionContext).point.sub(projectedAnchorPoint);
|
|
16429
17658
|
const angle = Math.atan(east.y / east.x) + (east.x < 0 ? Math.PI : 0);
|
|
16430
17659
|
return projectedAnchorPoint.add(shift.rotate(angle));
|
|
16431
17660
|
} else return projectedAnchorPoint.add(shift);
|
|
16432
17661
|
}
|
|
16433
|
-
function updateVariableAnchorsForBucket(bucket, rotateWithMap, pitchWithMap, variableOffsets, transform, pitchedLabelPlaneMatrix, tileScale, size, updateTextFitIcon, translation, unwrappedTileID, getElevation) {
|
|
17662
|
+
function updateVariableAnchorsForBucket(bucket, rotateWithMap, pitchWithMap, variableOffsets, transform, pitchedLabelPlaneMatrix, tileScale, size, updateTextFitIcon, translation, unwrappedTileID, getElevation, heightAnchorGround) {
|
|
16434
17663
|
const placedSymbols = bucket.text.placedSymbolArray;
|
|
16435
17664
|
const dynamicTextLayoutVertexArray = bucket.text.dynamicLayoutVertexArray;
|
|
16436
17665
|
const dynamicIconLayoutVertexArray = bucket.icon.dynamicLayoutVertexArray;
|
|
@@ -16445,6 +17674,8 @@ function updateVariableAnchorsForBucket(bucket, rotateWithMap, pitchWithMap, var
|
|
|
16445
17674
|
const tileAnchor = new Point(symbol.anchorX, symbol.anchorY);
|
|
16446
17675
|
const projectionContext = {
|
|
16447
17676
|
getElevation,
|
|
17677
|
+
heightOffset: symbol.heightOffset,
|
|
17678
|
+
heightAnchorGround,
|
|
16448
17679
|
width: transform.width,
|
|
16449
17680
|
height: transform.height,
|
|
16450
17681
|
pitchedLabelPlaneMatrix,
|
|
@@ -16554,10 +17785,11 @@ function drawLayerSymbols(painter, tileManager, layer, coords, isText, translate
|
|
|
16554
17785
|
const hasVariableAnchors = hasVariablePlacement && bucket.hasTextData();
|
|
16555
17786
|
const updateTextFitIcon = layer.layout.get("icon-text-fit") !== "none" && hasVariableAnchors && bucket.hasIconData();
|
|
16556
17787
|
const isOffset = layer._unevaluatedLayout.hasValue("icon-offset");
|
|
17788
|
+
const heightAnchorGround = layer.layout.get("symbol-height-anchor") === "ground";
|
|
16557
17789
|
if (alongLine) {
|
|
16558
17790
|
const pitchedLabelPlaneMatrixInverse = create$1();
|
|
16559
17791
|
fastInvertTransformMat4(pitchedLabelPlaneMatrixInverse, pitchedLabelPlaneMatrix);
|
|
16560
|
-
const getElevation = painter.style.map.terrain ? (x, y) => painter.style.map.terrain.getElevation(coord, x, y) :
|
|
17792
|
+
const getElevation = painter.style.map.terrain ? (x, y) => painter.style.map.terrain.getElevation(coord, x, y) : void 0;
|
|
16561
17793
|
updateLineLabels(bucket, painter, isText, pitchedLabelPlaneMatrix, pitchedLabelPlaneMatrixInverse, pitchWithMap, keepUpright, layer.layout.get("text-rotation-alignment") === "map", coord.toUnwrapped(), transform.width, transform.height, translation, getElevation);
|
|
16562
17794
|
}
|
|
16563
17795
|
const shaderVariableAnchor = isText && hasVariablePlacement || updateTextFitIcon;
|
|
@@ -16566,9 +17798,9 @@ function drawLayerSymbols(painter, tileManager, layer, coords, isText, translate
|
|
|
16566
17798
|
const hasHalo = isSDF && layer.paint.get(isText ? "text-halo-width" : "icon-halo-width").constantOr(1) !== 0;
|
|
16567
17799
|
let uniformValues;
|
|
16568
17800
|
if (isSDF) {
|
|
16569
|
-
if (!bucket.iconsInText) uniformValues = symbolSDFUniformValues(sizeData.kind, size, rotateInShader, pitchWithMap, alongLine, shaderVariableAnchor, painter, uLabelPlaneMatrix, glCoordMatrixForShader, translation, isText, texSize, hasHalo, pitchedTextRescaling, isOffset);
|
|
16570
|
-
else uniformValues = symbolTextAndIconUniformValues(sizeData.kind, size, rotateInShader, pitchWithMap, alongLine, shaderVariableAnchor, painter, uLabelPlaneMatrix, glCoordMatrixForShader, translation, texSize, texSizeIcon, pitchedTextRescaling, isOffset);
|
|
16571
|
-
} else uniformValues = symbolIconUniformValues(sizeData.kind, size, rotateInShader, pitchWithMap, alongLine, shaderVariableAnchor, painter, uLabelPlaneMatrix, glCoordMatrixForShader, translation, isText, texSize, pitchedTextRescaling, isOffset);
|
|
17801
|
+
if (!bucket.iconsInText) uniformValues = symbolSDFUniformValues(sizeData.kind, size, rotateInShader, pitchWithMap, alongLine, shaderVariableAnchor, painter, uLabelPlaneMatrix, glCoordMatrixForShader, translation, isText, texSize, hasHalo, pitchedTextRescaling, isOffset, heightAnchorGround);
|
|
17802
|
+
else uniformValues = symbolTextAndIconUniformValues(sizeData.kind, size, rotateInShader, pitchWithMap, alongLine, shaderVariableAnchor, painter, uLabelPlaneMatrix, glCoordMatrixForShader, translation, texSize, texSizeIcon, pitchedTextRescaling, isOffset, heightAnchorGround);
|
|
17803
|
+
} else uniformValues = symbolIconUniformValues(sizeData.kind, size, rotateInShader, pitchWithMap, alongLine, shaderVariableAnchor, painter, uLabelPlaneMatrix, glCoordMatrixForShader, translation, isText, texSize, pitchedTextRescaling, isOffset, heightAnchorGround);
|
|
16572
17804
|
const state = {
|
|
16573
17805
|
program,
|
|
16574
17806
|
buffers,
|
|
@@ -17380,18 +18612,18 @@ function renderColorRelief(painter, tileManager, layer, coords, stencilModes, de
|
|
|
17380
18612
|
}
|
|
17381
18613
|
if (!dem?.data) continue;
|
|
17382
18614
|
const textureStride = dem.stride;
|
|
17383
|
-
const pixelData = dem.getPixels();
|
|
17384
18615
|
context.activeTexture.set(gl.TEXTURE0);
|
|
17385
|
-
|
|
17386
|
-
|
|
17387
|
-
|
|
17388
|
-
|
|
17389
|
-
|
|
17390
|
-
|
|
17391
|
-
|
|
17392
|
-
tile.demTexture
|
|
17393
|
-
tile.
|
|
18616
|
+
if (!tile.demTexture || tile.needsColorReliefPrepare) {
|
|
18617
|
+
context.pixelStoreUnpackPremultiplyAlpha.set(false);
|
|
18618
|
+
tile.demTexture ||= painter.getTileTexture(textureStride) ?? new Texture(context, {
|
|
18619
|
+
width: textureStride,
|
|
18620
|
+
height: textureStride,
|
|
18621
|
+
data: null
|
|
18622
|
+
}, gl.RGBA);
|
|
18623
|
+
tile.demTexture.update(dem.getPixels(), { premultiply: false });
|
|
18624
|
+
tile.needsColorReliefPrepare = false;
|
|
17394
18625
|
}
|
|
18626
|
+
tile.demTexture.bind(textureFilter, gl.CLAMP_TO_EDGE);
|
|
17395
18627
|
const mesh = projection.getMeshFromTileID(context, coord.canonical, useBorder, true, "raster");
|
|
17396
18628
|
const terrainData = painter.getTerrainDataForTile(coord, isRenderingToTexture);
|
|
17397
18629
|
const projectionData = transform.getProjectionData({
|
|
@@ -17746,7 +18978,7 @@ function drawDepth(painter, terrain) {
|
|
|
17746
18978
|
const depthMode = new DepthMode(gl.LEQUAL, DepthMode.ReadWrite, [0, 1]);
|
|
17747
18979
|
const tiles = terrain.tileManager.getRenderableTiles();
|
|
17748
18980
|
const program = painter.useProgram("terrainDepth");
|
|
17749
|
-
context.bindFramebuffer.set(terrain.getFramebuffer(
|
|
18981
|
+
context.bindFramebuffer.set(terrain.getFramebuffer().framebuffer);
|
|
17750
18982
|
context.viewport.set([
|
|
17751
18983
|
0,
|
|
17752
18984
|
0,
|
|
@@ -17754,7 +18986,7 @@ function drawDepth(painter, terrain) {
|
|
|
17754
18986
|
painter.height / devicePixelRatio
|
|
17755
18987
|
]);
|
|
17756
18988
|
context.clear({
|
|
17757
|
-
color: Color.
|
|
18989
|
+
color: Color.white,
|
|
17758
18990
|
depth: 1
|
|
17759
18991
|
});
|
|
17760
18992
|
for (const tile of tiles) {
|
|
@@ -17776,54 +19008,6 @@ function drawDepth(painter, terrain) {
|
|
|
17776
19008
|
painter.height
|
|
17777
19009
|
]);
|
|
17778
19010
|
}
|
|
17779
|
-
/**
|
|
17780
|
-
* Redraw the Coords Framebuffers
|
|
17781
|
-
* @param painter - the painter
|
|
17782
|
-
* @param terrain - the terrain
|
|
17783
|
-
*/
|
|
17784
|
-
function drawCoords(painter, terrain) {
|
|
17785
|
-
const context = painter.context;
|
|
17786
|
-
const gl = context.gl;
|
|
17787
|
-
const tr = painter.transform;
|
|
17788
|
-
const colorMode = ColorMode.unblended;
|
|
17789
|
-
const depthMode = new DepthMode(gl.LEQUAL, DepthMode.ReadWrite, [0, 1]);
|
|
17790
|
-
const coords = terrain.getCoordsTexture();
|
|
17791
|
-
const tiles = terrain.tileManager.getRenderableTiles();
|
|
17792
|
-
const program = painter.useProgram("terrainCoords");
|
|
17793
|
-
context.bindFramebuffer.set(terrain.getFramebuffer("coords").framebuffer);
|
|
17794
|
-
context.viewport.set([
|
|
17795
|
-
0,
|
|
17796
|
-
0,
|
|
17797
|
-
painter.width / devicePixelRatio,
|
|
17798
|
-
painter.height / devicePixelRatio
|
|
17799
|
-
]);
|
|
17800
|
-
context.clear({
|
|
17801
|
-
color: Color.transparent,
|
|
17802
|
-
depth: 1
|
|
17803
|
-
});
|
|
17804
|
-
terrain.coordsIndex = [];
|
|
17805
|
-
for (const tile of tiles) {
|
|
17806
|
-
const mesh = terrain.getTerrainMesh(tile.tileID);
|
|
17807
|
-
const terrainData = terrain.getTerrainData(tile.tileID);
|
|
17808
|
-
context.activeTexture.set(gl.TEXTURE0);
|
|
17809
|
-
gl.bindTexture(gl.TEXTURE_2D, coords.texture);
|
|
17810
|
-
const uniformValues = terrainCoordsUniformValues(255 - terrain.coordsIndex.length, terrain.getSkirtLength(tr.zoom));
|
|
17811
|
-
const projectionData = tr.getProjectionData({
|
|
17812
|
-
overscaledTileID: tile.tileID,
|
|
17813
|
-
applyTerrainMatrix: false,
|
|
17814
|
-
applyGlobeMatrix: true
|
|
17815
|
-
});
|
|
17816
|
-
program.draw(context, gl.TRIANGLES, depthMode, StencilMode.disabled, colorMode, CullFaceMode.backCCW, uniformValues, terrainData, projectionData, "terrain", mesh.vertexBuffer, mesh.indexBuffer, mesh.segments);
|
|
17817
|
-
terrain.coordsIndex.push(tile.tileID.key);
|
|
17818
|
-
}
|
|
17819
|
-
context.bindFramebuffer.set(null);
|
|
17820
|
-
context.viewport.set([
|
|
17821
|
-
0,
|
|
17822
|
-
0,
|
|
17823
|
-
painter.width,
|
|
17824
|
-
painter.height
|
|
17825
|
-
]);
|
|
17826
|
-
}
|
|
17827
19011
|
function drawTerrain(painter, terrain, tiles, renderOptions) {
|
|
17828
19012
|
const { isRenderingGlobe } = renderOptions;
|
|
17829
19013
|
const context = painter.context;
|
|
@@ -17952,8 +19136,7 @@ const webglDrawFunctions = {
|
|
|
17952
19136
|
custom: drawCustom,
|
|
17953
19137
|
debug: drawDebug,
|
|
17954
19138
|
debugPadding: drawDebugPadding,
|
|
17955
|
-
terrainDepth: drawDepth
|
|
17956
|
-
terrainCoords: drawCoords
|
|
19139
|
+
terrainDepth: drawDepth
|
|
17957
19140
|
};
|
|
17958
19141
|
//#endregion
|
|
17959
19142
|
//#region src/render/painter.ts
|
|
@@ -17972,7 +19155,6 @@ var Painter = class Painter {
|
|
|
17972
19155
|
this._rttSharedFbo = null;
|
|
17973
19156
|
this.terrainFacilitator = {
|
|
17974
19157
|
depthDirty: true,
|
|
17975
|
-
coordsDirty: false,
|
|
17976
19158
|
matrix: identity(/* @__PURE__ */ new Float64Array(16)),
|
|
17977
19159
|
renderTime: 0
|
|
17978
19160
|
};
|
|
@@ -18273,7 +19455,7 @@ var Painter = class Painter {
|
|
|
18273
19455
|
break;
|
|
18274
19456
|
}
|
|
18275
19457
|
}
|
|
18276
|
-
this.maybeDrawDepth(
|
|
19458
|
+
this.maybeDrawDepth();
|
|
18277
19459
|
if (this.renderToTexture) {
|
|
18278
19460
|
this.renderToTexture.prepareForRender(this.style, this.transform.zoom);
|
|
18279
19461
|
this.opaquePassCutoff = 0;
|
|
@@ -18335,30 +19517,20 @@ var Painter = class Painter {
|
|
|
18335
19517
|
}
|
|
18336
19518
|
/**
|
|
18337
19519
|
* Update the depth framebuffer if the camera has moved or tiles have reloaded.
|
|
18338
|
-
* Marks coords as depthDirty so they are re-rendered on next demand.
|
|
18339
19520
|
*/
|
|
18340
|
-
maybeDrawDepth(
|
|
19521
|
+
maybeDrawDepth() {
|
|
18341
19522
|
if (!this.style?.map?.terrain) return;
|
|
18342
19523
|
const prevMatrix = this.terrainFacilitator.matrix;
|
|
18343
19524
|
const currMatrix = this.transform.modelViewProjectionMatrix;
|
|
18344
19525
|
let doUpdate = this.terrainFacilitator.depthDirty;
|
|
18345
|
-
doUpdate ||=
|
|
19526
|
+
doUpdate ||= !equals(prevMatrix, currMatrix);
|
|
18346
19527
|
doUpdate ||= this.style.map.terrain.tileManager.anyTilesAfterTime(this.terrainFacilitator.renderTime);
|
|
18347
19528
|
if (!doUpdate) return;
|
|
18348
19529
|
copy(prevMatrix, currMatrix);
|
|
18349
19530
|
this.terrainFacilitator.renderTime = now();
|
|
18350
19531
|
this.terrainFacilitator.depthDirty = false;
|
|
18351
|
-
this.terrainFacilitator.coordsDirty = true;
|
|
18352
19532
|
this.drawFunctions.terrainDepth(this, this.style.map.terrain);
|
|
18353
19533
|
}
|
|
18354
|
-
/**
|
|
18355
|
-
* Render the coords framebuffer if it is coordsDirty
|
|
18356
|
-
*/
|
|
18357
|
-
maybeDrawCoords() {
|
|
18358
|
-
if (!this.style?.map?.terrain || !this.terrainFacilitator.coordsDirty) return;
|
|
18359
|
-
this.terrainFacilitator.coordsDirty = false;
|
|
18360
|
-
this.drawFunctions.terrainCoords(this, this.style.map.terrain);
|
|
18361
|
-
}
|
|
18362
19534
|
renderLayer(painter, tileManager, layer, coords, renderOptions) {
|
|
18363
19535
|
if (layer.isHidden(this.transform.zoom)) return;
|
|
18364
19536
|
if (layer.type !== "background" && layer.type !== "custom" && !(coords || []).length) return;
|
|
@@ -18551,7 +19723,8 @@ var Painter = class Painter {
|
|
|
18551
19723
|
//#endregion
|
|
18552
19724
|
//#region src/util/gpu_initialization_error.ts
|
|
18553
19725
|
/**
|
|
18554
|
-
* 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.
|
|
18555
19728
|
*
|
|
18556
19729
|
* Carries the canvas attributes that were requested and, when the browser provided one,
|
|
18557
19730
|
* the originating `webglcontextcreationerror` `statusMessage`.
|
|
@@ -21094,7 +22267,7 @@ var HandlerManager = class {
|
|
|
21094
22267
|
_terrainGestureElevation(terrain, around, aroundOnSurface, tr, combinedEventsInProgress) {
|
|
21095
22268
|
if (!aroundOnSurface) return;
|
|
21096
22269
|
if (!this._terrainMovement && (combinedEventsInProgress.drag || combinedEventsInProgress.zoom)) {
|
|
21097
|
-
const anchor =
|
|
22270
|
+
const anchor = tr.screenTerrainPointToMercatorCoordinate(around, terrain);
|
|
21098
22271
|
this._terrainGestureAnchorElevation = anchor ? anchor.z : null;
|
|
21099
22272
|
}
|
|
21100
22273
|
if (this._terrainGestureAnchorElevation === null) return;
|
|
@@ -21430,7 +22603,7 @@ var Camera = class extends Evented {
|
|
|
21430
22603
|
let bearingChanged = false, pitchChanged = false;
|
|
21431
22604
|
let rollChanged = false;
|
|
21432
22605
|
const oldZoom = tr.zoom;
|
|
21433
|
-
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));
|
|
21434
22607
|
this.cameraHelper.handleJumpToCenterZoom(tr, options);
|
|
21435
22608
|
const zoomChanged = tr.zoom !== oldZoom;
|
|
21436
22609
|
if ("elevation" in options && tr.elevation !== +options.elevation) tr.setElevation(+options.elevation);
|
|
@@ -21531,1232 +22704,532 @@ var Camera = class extends Evented {
|
|
|
21531
22704
|
this._easeId = options.easeId;
|
|
21532
22705
|
this._prepareEase(eventData, options.noMoveStart, currently);
|
|
21533
22706
|
if (this.terrain) this._prepareElevation(easeHandler.elevationCenter);
|
|
21534
|
-
this._ease((k) => {
|
|
21535
|
-
easeHandler.easeFunc(k);
|
|
21536
|
-
if (this.terrain && !options.freezeElevation) this._updateElevation(k);
|
|
21537
|
-
this.applyUpdatedTransform(tr);
|
|
21538
|
-
this._fireMoveEvents(eventData);
|
|
21539
|
-
}, (interruptingEaseId) => {
|
|
21540
|
-
if (this.terrain && options.freezeElevation) this._finalizeElevation();
|
|
21541
|
-
this._afterEase(eventData, interruptingEaseId);
|
|
21542
|
-
}, options);
|
|
21543
|
-
return this;
|
|
21544
|
-
}
|
|
21545
|
-
_prepareEase(eventData, noMoveStart, currently = {}) {
|
|
21546
|
-
this._moving = true;
|
|
21547
|
-
if (!noMoveStart && !currently.moving) this.fire(new MapMovementEvent("movestart", eventData));
|
|
21548
|
-
if (this._zooming && !currently.zooming) this.fire(new MapMovementEvent("zoomstart", eventData));
|
|
21549
|
-
if (this._rotating && !currently.rotating) this.fire(new MapMovementEvent("rotatestart", eventData));
|
|
21550
|
-
if (this._pitching && !currently.pitching) this.fire(new MapMovementEvent("pitchstart", eventData));
|
|
21551
|
-
if (this._rolling && !currently.rolling) this.fire(new MapMovementEvent("rollstart", eventData));
|
|
21552
|
-
}
|
|
21553
|
-
_prepareElevation(center) {
|
|
21554
|
-
this._elevationCenter = center;
|
|
21555
|
-
this._elevationStart = this.transform.elevation;
|
|
21556
|
-
this._elevationTarget = this.terrain.getElevationForLngLatZoom(center, this.transform.tileZoom);
|
|
21557
|
-
this.elevationFreeze = true;
|
|
21558
|
-
}
|
|
21559
|
-
_updateElevation(k) {
|
|
21560
|
-
if (this._elevationStart === void 0 || this._elevationCenter === void 0) this._prepareElevation(this.transform.center);
|
|
21561
|
-
this.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this._elevationCenter, this.transform.tileZoom));
|
|
21562
|
-
const elevation = this.terrain.getElevationForLngLatZoom(this._elevationCenter, this.transform.tileZoom);
|
|
21563
|
-
if (k < 1 && elevation !== this._elevationTarget) {
|
|
21564
|
-
const pitch1 = this._elevationTarget - this._elevationStart;
|
|
21565
|
-
const pitch2 = (elevation - (pitch1 * k + this._elevationStart)) / (1 - k);
|
|
21566
|
-
this._elevationStart += k * (pitch1 - pitch2);
|
|
21567
|
-
this._elevationTarget = elevation;
|
|
21568
|
-
}
|
|
21569
|
-
this.transform.setElevation(interpolateFactory.number(this._elevationStart, this._elevationTarget, k));
|
|
21570
|
-
}
|
|
21571
|
-
_finalizeElevation() {
|
|
21572
|
-
this.elevationFreeze = false;
|
|
21573
|
-
if (this.getCenterClampedToGround()) this.transform.recalculateZoomAndCenter(this.terrain);
|
|
21574
|
-
}
|
|
21575
|
-
/**
|
|
21576
|
-
* @internal
|
|
21577
|
-
* Called when the camera is about to be manipulated.
|
|
21578
|
-
* If `transformCameraUpdate` is specified or terrain is enabled, a copy of
|
|
21579
|
-
* the current transform is created to track the accumulated changes.
|
|
21580
|
-
* This underlying transform represents the "desired state" proposed by input handlers / animations / UI controls.
|
|
21581
|
-
* It may differ from the state used for rendering (`this.transform`).
|
|
21582
|
-
* @returns Transform to apply changes to
|
|
21583
|
-
*/
|
|
21584
|
-
getTransformForUpdate() {
|
|
21585
|
-
if (!this.transformCameraUpdate && !this.terrain) return this.transform;
|
|
21586
|
-
this._requestedCameraState ||= this.transform.clone();
|
|
21587
|
-
return this._requestedCameraState;
|
|
21588
|
-
}
|
|
21589
|
-
/**
|
|
21590
|
-
* @internal
|
|
21591
|
-
* Checks the given transform for the camera being below terrain surface and
|
|
21592
|
-
* returns new pitch and zoom to fix that.
|
|
21593
|
-
*
|
|
21594
|
-
* With the new pitch and zoom, the camera will be at the same ground
|
|
21595
|
-
* position but at higher altitude. It will still point to the same spot on
|
|
21596
|
-
* the map.
|
|
21597
|
-
*
|
|
21598
|
-
* @param tr - The transform to check.
|
|
21599
|
-
*/
|
|
21600
|
-
_elevateCameraIfInsideTerrain(tr) {
|
|
21601
|
-
if (!this.terrain && tr.elevation >= 0 && tr.pitch <= 90) return {};
|
|
21602
|
-
const cameraLngLat = tr.getCameraLngLat();
|
|
21603
|
-
const cameraAltitude = tr.getCameraAltitude();
|
|
21604
|
-
const minAltitude = this.terrain ? this.terrain.getElevationForLngLatZoom(cameraLngLat, tr.zoom) : 0;
|
|
21605
|
-
if (cameraAltitude < minAltitude) {
|
|
21606
|
-
const newCamera = this.calculateCameraOptionsFromTo(cameraLngLat, minAltitude, tr.center, tr.elevation);
|
|
21607
|
-
return {
|
|
21608
|
-
pitch: newCamera.pitch,
|
|
21609
|
-
zoom: newCamera.zoom
|
|
21610
|
-
};
|
|
21611
|
-
}
|
|
21612
|
-
return {};
|
|
21613
|
-
}
|
|
21614
|
-
/**
|
|
21615
|
-
* @internal
|
|
21616
|
-
* Called after the camera is done being manipulated.
|
|
21617
|
-
* @param tr - the requested camera end state
|
|
21618
|
-
* If the camera is inside terrain, it gets elevated.
|
|
21619
|
-
* Call `transformCameraUpdate` if present, and then apply the "approved" changes.
|
|
21620
|
-
*/
|
|
21621
|
-
applyUpdatedTransform(tr) {
|
|
21622
|
-
const modifiers = [];
|
|
21623
|
-
modifiers.push((tr) => this._elevateCameraIfInsideTerrain(tr));
|
|
21624
|
-
if (this.transformCameraUpdate) modifiers.push((tr) => this.transformCameraUpdate(tr));
|
|
21625
|
-
if (!modifiers.length) return;
|
|
21626
|
-
const finalTransform = tr.clone();
|
|
21627
|
-
for (const modifier of modifiers) {
|
|
21628
|
-
const nextTransform = finalTransform.clone();
|
|
21629
|
-
const { center, zoom, roll, pitch, bearing, elevation } = modifier(nextTransform);
|
|
21630
|
-
if (center) nextTransform.setCenter(center);
|
|
21631
|
-
if (elevation !== void 0) nextTransform.setElevation(elevation);
|
|
21632
|
-
if (zoom !== void 0) nextTransform.setZoom(zoom);
|
|
21633
|
-
if (roll !== void 0) nextTransform.setRoll(roll);
|
|
21634
|
-
if (pitch !== void 0) nextTransform.setPitch(pitch);
|
|
21635
|
-
if (bearing !== void 0) nextTransform.setBearing(bearing);
|
|
21636
|
-
finalTransform.apply(nextTransform, false);
|
|
21637
|
-
}
|
|
21638
|
-
this.transform.apply(finalTransform, false);
|
|
21639
|
-
}
|
|
21640
|
-
_fireMoveEvents(eventData) {
|
|
21641
|
-
this.fire(new MapMovementEvent("move", eventData));
|
|
21642
|
-
if (this._zooming) this.fire(new MapMovementEvent("zoom", eventData));
|
|
21643
|
-
if (this._rotating) this.fire(new MapMovementEvent("rotate", eventData));
|
|
21644
|
-
if (this._pitching) this.fire(new MapMovementEvent("pitch", eventData));
|
|
21645
|
-
if (this._rolling) this.fire(new MapMovementEvent("roll", eventData));
|
|
21646
|
-
}
|
|
21647
|
-
_afterEase(eventData, easeId) {
|
|
21648
|
-
if (this._easeId && easeId && this._easeId === easeId) return;
|
|
21649
|
-
delete this._easeId;
|
|
21650
|
-
const wasZooming = this._zooming;
|
|
21651
|
-
const wasRotating = this._rotating;
|
|
21652
|
-
const wasPitching = this._pitching;
|
|
21653
|
-
const wasRolling = this._rolling;
|
|
21654
|
-
this._moving = false;
|
|
21655
|
-
this._zooming = false;
|
|
21656
|
-
this._rotating = false;
|
|
21657
|
-
this._pitching = false;
|
|
21658
|
-
this._rolling = false;
|
|
21659
|
-
this._padding = false;
|
|
21660
|
-
if (wasZooming) this.fire(new MapMovementEvent("zoomend", eventData));
|
|
21661
|
-
if (wasRotating) this.fire(new MapMovementEvent("rotateend", eventData));
|
|
21662
|
-
if (wasPitching) this.fire(new MapMovementEvent("pitchend", eventData));
|
|
21663
|
-
if (wasRolling) this.fire(new MapMovementEvent("rollend", eventData));
|
|
21664
|
-
this.fire(new MapMovementEvent("moveend", eventData));
|
|
21665
|
-
}
|
|
21666
|
-
flyTo(options, eventData) {
|
|
21667
|
-
if (!options.essential && browser.prefersReducedMotion) {
|
|
21668
|
-
const coercedOptions = pick(options, [
|
|
21669
|
-
"center",
|
|
21670
|
-
"zoom",
|
|
21671
|
-
"bearing",
|
|
21672
|
-
"pitch",
|
|
21673
|
-
"roll",
|
|
21674
|
-
"elevation",
|
|
21675
|
-
"padding"
|
|
21676
|
-
]);
|
|
21677
|
-
return this.jumpTo(coercedOptions, eventData);
|
|
21678
|
-
}
|
|
21679
|
-
this.stop();
|
|
21680
|
-
options = extend({
|
|
21681
|
-
offset: [0, 0],
|
|
21682
|
-
speed: 1.2,
|
|
21683
|
-
curve: 1.42,
|
|
21684
|
-
easing: defaultEasing
|
|
21685
|
-
}, options);
|
|
21686
|
-
if ("zoom" in options && this._zoomSnap) options.zoom = evaluateZoomSnap(options.zoom, this._zoomSnap);
|
|
21687
|
-
const tr = this.getTransformForUpdate(), startBearing = tr.bearing, startPitch = tr.pitch, startRoll = tr.roll, startPadding = tr.padding;
|
|
21688
|
-
const bearing = "bearing" in options ? this._normalizeBearing(options.bearing, startBearing) : startBearing;
|
|
21689
|
-
const pitch = "pitch" in options ? +options.pitch : startPitch;
|
|
21690
|
-
const roll = "roll" in options ? this._normalizeBearing(options.roll, startRoll) : startRoll;
|
|
21691
|
-
const padding = "padding" in options ? options.padding : tr.padding;
|
|
21692
|
-
const offsetAsPoint = Point.convert(options.offset);
|
|
21693
|
-
let pointAtOffset = tr.centerPoint.add(offsetAsPoint);
|
|
21694
|
-
const locationAtOffset = tr.screenPointToLocation(pointAtOffset);
|
|
21695
|
-
const flyToHandler = this.cameraHelper.handleFlyTo(tr, {
|
|
21696
|
-
bearing,
|
|
21697
|
-
pitch,
|
|
21698
|
-
roll,
|
|
21699
|
-
padding,
|
|
21700
|
-
locationAtOffset,
|
|
21701
|
-
offsetAsPoint,
|
|
21702
|
-
center: options.center,
|
|
21703
|
-
minZoom: options.minZoom,
|
|
21704
|
-
zoom: options.zoom
|
|
21705
|
-
});
|
|
21706
|
-
let rho = options.curve;
|
|
21707
|
-
const w0 = Math.max(tr.width, tr.height);
|
|
21708
|
-
const w1 = w0 / flyToHandler.scaleOfZoom;
|
|
21709
|
-
const u1 = flyToHandler.pixelPathLength;
|
|
21710
|
-
const wMax = w0 / flyToHandler.scaleOfMinZoom;
|
|
21711
|
-
rho = Math.min(rho, Math.sqrt(wMax / u1 * 2));
|
|
21712
|
-
const rho2 = rho * rho;
|
|
21713
|
-
/**
|
|
21714
|
-
* rᵢ: Returns the zoom-out factor at one end of the animation.
|
|
21715
|
-
*
|
|
21716
|
-
* @param descent - `true` for the descent, `false` for the ascent
|
|
21717
|
-
*/
|
|
21718
|
-
function zoomOutFactor(descent) {
|
|
21719
|
-
const b = (w1 * w1 - w0 * w0 + (descent ? -1 : 1) * rho2 * rho2 * u1 * u1) / (2 * (descent ? w1 : w0) * rho2 * u1);
|
|
21720
|
-
return Math.log(Math.sqrt(b * b + 1) - b);
|
|
21721
|
-
}
|
|
21722
|
-
function sinh(n) {
|
|
21723
|
-
return (Math.exp(n) - Math.exp(-n)) / 2;
|
|
21724
|
-
}
|
|
21725
|
-
function cosh(n) {
|
|
21726
|
-
return (Math.exp(n) + Math.exp(-n)) / 2;
|
|
21727
|
-
}
|
|
21728
|
-
function tanh(n) {
|
|
21729
|
-
return sinh(n) / cosh(n);
|
|
21730
|
-
}
|
|
21731
|
-
const r0 = zoomOutFactor(false);
|
|
21732
|
-
let w = function(s) {
|
|
21733
|
-
return cosh(r0) / cosh(r0 + rho * s);
|
|
21734
|
-
};
|
|
21735
|
-
let u = function(s) {
|
|
21736
|
-
return w0 * ((cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2) / u1;
|
|
21737
|
-
};
|
|
21738
|
-
let S = (zoomOutFactor(true) - r0) / rho;
|
|
21739
|
-
if (Math.abs(u1) < 2e-6 || !isFinite(S)) {
|
|
21740
|
-
if (Math.abs(w0 - w1) < 1e-6) return this.easeTo(options, eventData);
|
|
21741
|
-
const k = w1 < w0 ? -1 : 1;
|
|
21742
|
-
S = Math.abs(Math.log(w1 / w0)) / rho;
|
|
21743
|
-
u = () => 0;
|
|
21744
|
-
w = (s) => Math.exp(k * rho * s);
|
|
21745
|
-
}
|
|
21746
|
-
if ("duration" in options) options.duration = +options.duration;
|
|
21747
|
-
else {
|
|
21748
|
-
const V = "screenSpeed" in options ? +options.screenSpeed / rho : +options.speed;
|
|
21749
|
-
options.duration = 1e3 * S / V;
|
|
21750
|
-
}
|
|
21751
|
-
if (options.maxDuration && options.duration > options.maxDuration) options.duration = 0;
|
|
21752
|
-
this._zooming = true;
|
|
21753
|
-
this._rotating = startBearing !== bearing;
|
|
21754
|
-
this._pitching = pitch !== startPitch;
|
|
21755
|
-
this._rolling = roll !== startRoll;
|
|
21756
|
-
this._padding = !tr.isPaddingEqual(padding);
|
|
21757
|
-
this._prepareEase(eventData, false);
|
|
21758
|
-
if (this.terrain) this._prepareElevation(flyToHandler.targetCenter);
|
|
21759
|
-
this._ease((k) => {
|
|
21760
|
-
const s = k * S;
|
|
21761
|
-
const scale = 1 / w(s);
|
|
21762
|
-
const centerFactor = u(s);
|
|
21763
|
-
if (this._rotating) tr.setBearing(interpolateFactory.number(startBearing, bearing, k));
|
|
21764
|
-
if (this._pitching) tr.setPitch(interpolateFactory.number(startPitch, pitch, k));
|
|
21765
|
-
if (this._rolling) tr.setRoll(interpolateFactory.number(startRoll, roll, k));
|
|
21766
|
-
if (this._padding) {
|
|
21767
|
-
tr.interpolatePadding(startPadding, padding, k);
|
|
21768
|
-
pointAtOffset = tr.centerPoint.add(offsetAsPoint);
|
|
21769
|
-
}
|
|
21770
|
-
flyToHandler.easeFunc(k, scale, centerFactor, pointAtOffset);
|
|
21771
|
-
if (this.terrain && !options.freezeElevation) this._updateElevation(k);
|
|
21772
|
-
this.applyUpdatedTransform(tr);
|
|
21773
|
-
this._fireMoveEvents(eventData);
|
|
21774
|
-
}, () => {
|
|
21775
|
-
if (this.terrain && options.freezeElevation) this._finalizeElevation();
|
|
21776
|
-
this._afterEase(eventData);
|
|
21777
|
-
}, options);
|
|
21778
|
-
return this;
|
|
21779
|
-
}
|
|
21780
|
-
isEasing() {
|
|
21781
|
-
return !!this._easeFrameId;
|
|
21782
|
-
}
|
|
21783
|
-
stop(allowGestures) {
|
|
21784
|
-
return this._stop(allowGestures);
|
|
21785
|
-
}
|
|
21786
|
-
_stop(allowGestures, easeId) {
|
|
21787
|
-
if (this._easeFrameId) {
|
|
21788
|
-
this._cancelRenderFrame(this._easeFrameId);
|
|
21789
|
-
delete this._easeFrameId;
|
|
21790
|
-
delete this._onEaseFrame;
|
|
21791
|
-
}
|
|
21792
|
-
if (this._onEaseEnd) {
|
|
21793
|
-
const onEaseEnd = this._onEaseEnd;
|
|
21794
|
-
delete this._onEaseEnd;
|
|
21795
|
-
onEaseEnd.call(this, easeId);
|
|
21796
|
-
}
|
|
21797
|
-
if (!allowGestures) this._stopHandlers();
|
|
21798
|
-
return this;
|
|
21799
|
-
}
|
|
21800
|
-
_ease(frame, finish, options) {
|
|
21801
|
-
if (options.animate === false || options.duration === 0) {
|
|
21802
|
-
frame(1);
|
|
21803
|
-
finish();
|
|
21804
|
-
} else {
|
|
21805
|
-
this._easeStart = now();
|
|
21806
|
-
this._easeOptions = options;
|
|
21807
|
-
this._onEaseFrame = frame;
|
|
21808
|
-
this._onEaseEnd = finish;
|
|
21809
|
-
this._easeFrameId = this._requestRenderFrame(this._renderFrameCallback);
|
|
21810
|
-
}
|
|
21811
|
-
}
|
|
21812
|
-
_normalizeBearing(bearing, currentBearing) {
|
|
21813
|
-
bearing = wrap(bearing, -180, 180);
|
|
21814
|
-
const diff = Math.abs(bearing - currentBearing);
|
|
21815
|
-
if (Math.abs(bearing - 360 - currentBearing) < diff) bearing -= 360;
|
|
21816
|
-
if (Math.abs(bearing + 360 - currentBearing) < diff) bearing += 360;
|
|
21817
|
-
return bearing;
|
|
21818
|
-
}
|
|
21819
|
-
isMoving() {
|
|
21820
|
-
return this._moving;
|
|
21821
|
-
}
|
|
21822
|
-
isZooming() {
|
|
21823
|
-
return this._zooming;
|
|
21824
|
-
}
|
|
21825
|
-
isRotating() {
|
|
21826
|
-
return this._rotating;
|
|
21827
|
-
}
|
|
21828
|
-
};
|
|
21829
|
-
//#endregion
|
|
21830
|
-
//#region src/ui/control/attribution_control.ts
|
|
21831
|
-
const defaultAttributionControlOptions = {
|
|
21832
|
-
compact: true,
|
|
21833
|
-
customAttribution: "<a href=\"https://maplibre.org/\" target=\"_blank\">MapLibre</a>"
|
|
21834
|
-
};
|
|
21835
|
-
/**
|
|
21836
|
-
* An `AttributionControl` control presents the map's attribution information. By default, the attribution control is expanded (regardless of map width).
|
|
21837
|
-
* @group Markers and Controls
|
|
21838
|
-
* @example
|
|
21839
|
-
* ```ts
|
|
21840
|
-
* let map = new Map({attributionControl: false})
|
|
21841
|
-
* .addControl(new AttributionControl({
|
|
21842
|
-
* compact: true
|
|
21843
|
-
* }));
|
|
21844
|
-
* ```
|
|
21845
|
-
* @see [Change the default position for attribution](https://maplibre.org/maplibre-gl-js/docs/examples/change-the-default-position-for-attribution/)
|
|
21846
|
-
*/
|
|
21847
|
-
var AttributionControl = class {
|
|
21848
|
-
/**
|
|
21849
|
-
* @param options - the attribution options
|
|
21850
|
-
*/
|
|
21851
|
-
constructor(options = defaultAttributionControlOptions) {
|
|
21852
|
-
this._toggleAttribution = () => {
|
|
21853
|
-
if (this._container.classList.contains("maplibregl-compact")) {
|
|
21854
|
-
if (this._container.classList.contains("maplibregl-compact-show")) {
|
|
21855
|
-
this._container.setAttribute("open", "");
|
|
21856
|
-
this._container.classList.remove("maplibregl-compact-show");
|
|
21857
|
-
} else {
|
|
21858
|
-
this._container.classList.add("maplibregl-compact-show");
|
|
21859
|
-
this._container.removeAttribute("open");
|
|
21860
|
-
}
|
|
21861
|
-
}
|
|
21862
|
-
};
|
|
21863
|
-
this._updateData = (e) => {
|
|
21864
|
-
if (e && (e.type === "terrain" || e.dataType === "style" || e.dataType === "source" && (e.sourceDataType === "metadata" || e.sourceDataType === "visibility"))) this._updateAttributions();
|
|
21865
|
-
};
|
|
21866
|
-
this._updateCompact = () => {
|
|
21867
|
-
if (this._map.getCanvasContainer().offsetWidth <= 640 || this._compact) {
|
|
21868
|
-
if (this._compact === false) this._container.setAttribute("open", "");
|
|
21869
|
-
else if (!this._container.classList.contains("maplibregl-compact") && !this._container.classList.contains("maplibregl-attrib-empty")) {
|
|
21870
|
-
this._container.setAttribute("open", "");
|
|
21871
|
-
this._container.classList.add("maplibregl-compact", "maplibregl-compact-show");
|
|
21872
|
-
}
|
|
21873
|
-
} else {
|
|
21874
|
-
this._container.setAttribute("open", "");
|
|
21875
|
-
if (this._container.classList.contains("maplibregl-compact")) this._container.classList.remove("maplibregl-compact", "maplibregl-compact-show");
|
|
21876
|
-
}
|
|
21877
|
-
};
|
|
21878
|
-
this._updateCompactMinimize = () => {
|
|
21879
|
-
if (this._container.classList.contains("maplibregl-compact")) {
|
|
21880
|
-
if (this._container.classList.contains("maplibregl-compact-show")) this._container.classList.remove("maplibregl-compact-show");
|
|
21881
|
-
}
|
|
21882
|
-
};
|
|
21883
|
-
this.options = options;
|
|
21884
|
-
}
|
|
21885
|
-
getDefaultPosition() {
|
|
21886
|
-
return "bottom-right";
|
|
21887
|
-
}
|
|
21888
|
-
/** {@inheritDoc IControl.onAdd} */
|
|
21889
|
-
onAdd(map) {
|
|
21890
|
-
this._map = map;
|
|
21891
|
-
this._compact = this.options.compact;
|
|
21892
|
-
this._container = DOM.create("details", "maplibregl-ctrl maplibregl-ctrl-attrib");
|
|
21893
|
-
this._compactButton = DOM.create("summary", "maplibregl-ctrl-attrib-button", this._container);
|
|
21894
|
-
this._compactButton.addEventListener("click", this._toggleAttribution);
|
|
21895
|
-
this._setElementTitle(this._compactButton, "ToggleAttribution");
|
|
21896
|
-
this._innerContainer = DOM.create("div", "maplibregl-ctrl-attrib-inner", this._container);
|
|
21897
|
-
this._updateAttributions();
|
|
21898
|
-
this._updateCompact();
|
|
21899
|
-
this._map.on("styledata", this._updateData);
|
|
21900
|
-
this._map.on("sourcedata", this._updateData);
|
|
21901
|
-
this._map.on("terrain", this._updateData);
|
|
21902
|
-
this._map.on("resize", this._updateCompact);
|
|
21903
|
-
this._map.on("drag", this._updateCompactMinimize);
|
|
21904
|
-
return this._container;
|
|
21905
|
-
}
|
|
21906
|
-
/** {@inheritDoc IControl.onRemove} */
|
|
21907
|
-
onRemove() {
|
|
21908
|
-
this._container.remove();
|
|
21909
|
-
this._map.off("styledata", this._updateData);
|
|
21910
|
-
this._map.off("sourcedata", this._updateData);
|
|
21911
|
-
this._map.off("terrain", this._updateData);
|
|
21912
|
-
this._map.off("resize", this._updateCompact);
|
|
21913
|
-
this._map.off("drag", this._updateCompactMinimize);
|
|
21914
|
-
this._map = void 0;
|
|
21915
|
-
this._compact = void 0;
|
|
21916
|
-
this._attribHTML = void 0;
|
|
21917
|
-
}
|
|
21918
|
-
_setElementTitle(element, title) {
|
|
21919
|
-
const str = this._map._getUIString(`AttributionControl.${title}`);
|
|
21920
|
-
element.title = str;
|
|
21921
|
-
element.setAttribute("aria-label", str);
|
|
21922
|
-
}
|
|
21923
|
-
_updateAttributions() {
|
|
21924
|
-
if (!this._map.style) return;
|
|
21925
|
-
let attributions = [];
|
|
21926
|
-
if (this.options.customAttribution) {
|
|
21927
|
-
if (Array.isArray(this.options.customAttribution)) attributions = attributions.concat(this.options.customAttribution.map((attribution) => {
|
|
21928
|
-
if (typeof attribution !== "string") return "";
|
|
21929
|
-
return attribution;
|
|
21930
|
-
}));
|
|
21931
|
-
else if (typeof this.options.customAttribution === "string") attributions.push(this.options.customAttribution);
|
|
21932
|
-
}
|
|
21933
|
-
if (this._map.style.stylesheet) {
|
|
21934
|
-
const stylesheet = this._map.style.stylesheet;
|
|
21935
|
-
this.styleOwner = stylesheet.owner;
|
|
21936
|
-
this.styleId = stylesheet.id;
|
|
21937
|
-
}
|
|
21938
|
-
const tileManagers = this._map.style.tileManagers;
|
|
21939
|
-
for (const id in tileManagers) {
|
|
21940
|
-
const tileManager = tileManagers[id];
|
|
21941
|
-
if (tileManager.used || tileManager.usedForTerrain) {
|
|
21942
|
-
const source = tileManager.getSource();
|
|
21943
|
-
if (source.attribution && !attributions.includes(source.attribution)) attributions.push(source.attribution);
|
|
21944
|
-
}
|
|
21945
|
-
}
|
|
21946
|
-
attributions = attributions.filter((e) => String(e).trim());
|
|
21947
|
-
attributions.sort((a, b) => a.length - b.length);
|
|
21948
|
-
attributions = attributions.filter((attrib, i) => {
|
|
21949
|
-
for (let j = i + 1; j < attributions.length; j++) if (attributions[j].includes(attrib)) return false;
|
|
21950
|
-
return true;
|
|
21951
|
-
});
|
|
21952
|
-
const attribHTML = attributions.join(" | ");
|
|
21953
|
-
if (attribHTML === this._attribHTML) return;
|
|
21954
|
-
this._attribHTML = attribHTML;
|
|
21955
|
-
if (attributions.length) {
|
|
21956
|
-
this._innerContainer.innerHTML = DOM.sanitize(attribHTML);
|
|
21957
|
-
this._container.classList.remove("maplibregl-attrib-empty");
|
|
21958
|
-
} else this._container.classList.add("maplibregl-attrib-empty");
|
|
21959
|
-
this._updateCompact();
|
|
21960
|
-
this._editLink = null;
|
|
21961
|
-
}
|
|
21962
|
-
};
|
|
21963
|
-
//#endregion
|
|
21964
|
-
//#region src/ui/control/logo_control.ts
|
|
21965
|
-
/**
|
|
21966
|
-
* A `LogoControl` is a control that adds the watermark.
|
|
21967
|
-
*
|
|
21968
|
-
* @group Markers and Controls
|
|
21969
|
-
*
|
|
21970
|
-
* @example
|
|
21971
|
-
* ```ts
|
|
21972
|
-
* map.addControl(new LogoControl({compact: false}));
|
|
21973
|
-
* ```
|
|
21974
|
-
**/
|
|
21975
|
-
var LogoControl = class {
|
|
21976
|
-
/**
|
|
21977
|
-
* @param options - the control's options
|
|
21978
|
-
*/
|
|
21979
|
-
constructor(options = {}) {
|
|
21980
|
-
this._updateCompact = () => {
|
|
21981
|
-
const containerChildren = this._container.children;
|
|
21982
|
-
if (containerChildren.length) {
|
|
21983
|
-
const anchor = containerChildren[0];
|
|
21984
|
-
if (this._map.getCanvasContainer().offsetWidth <= 640 || this._compact) {
|
|
21985
|
-
if (this._compact !== false) anchor.classList.add("maplibregl-compact");
|
|
21986
|
-
} else anchor.classList.remove("maplibregl-compact");
|
|
21987
|
-
}
|
|
21988
|
-
};
|
|
21989
|
-
this.options = options;
|
|
21990
|
-
}
|
|
21991
|
-
getDefaultPosition() {
|
|
21992
|
-
return "bottom-left";
|
|
21993
|
-
}
|
|
21994
|
-
/** {@inheritDoc IControl.onAdd} */
|
|
21995
|
-
onAdd(map) {
|
|
21996
|
-
this._map = map;
|
|
21997
|
-
this._compact = this.options?.compact;
|
|
21998
|
-
this._container = DOM.create("div", "maplibregl-ctrl");
|
|
21999
|
-
const anchor = DOM.create("a", "maplibregl-ctrl-logo");
|
|
22000
|
-
anchor.target = "_blank";
|
|
22001
|
-
anchor.rel = "noopener nofollow";
|
|
22002
|
-
anchor.href = "https://maplibre.org/";
|
|
22003
|
-
anchor.setAttribute("aria-label", this._map._getUIString("LogoControl.Title"));
|
|
22004
|
-
anchor.setAttribute("rel", "noopener nofollow");
|
|
22005
|
-
this._container.appendChild(anchor);
|
|
22006
|
-
this._container.style.display = "block";
|
|
22007
|
-
this._map.on("resize", this._updateCompact);
|
|
22008
|
-
this._updateCompact();
|
|
22009
|
-
return this._container;
|
|
22010
|
-
}
|
|
22011
|
-
/** {@inheritDoc IControl.onRemove} */
|
|
22012
|
-
onRemove() {
|
|
22013
|
-
this._container.remove();
|
|
22014
|
-
this._map.off("resize", this._updateCompact);
|
|
22015
|
-
this._map = void 0;
|
|
22016
|
-
this._compact = void 0;
|
|
22017
|
-
}
|
|
22018
|
-
};
|
|
22019
|
-
//#endregion
|
|
22020
|
-
//#region src/util/task_queue.ts
|
|
22021
|
-
var TaskQueue = class {
|
|
22022
|
-
constructor() {
|
|
22023
|
-
this._queue = [];
|
|
22024
|
-
this._id = 0;
|
|
22025
|
-
this._cleared = false;
|
|
22026
|
-
this._currentlyRunning = false;
|
|
22027
|
-
}
|
|
22028
|
-
add(callback) {
|
|
22029
|
-
const id = ++this._id;
|
|
22030
|
-
this._queue.push({
|
|
22031
|
-
callback,
|
|
22032
|
-
id,
|
|
22033
|
-
cancelled: false
|
|
22034
|
-
});
|
|
22035
|
-
return id;
|
|
22036
|
-
}
|
|
22037
|
-
remove(id) {
|
|
22038
|
-
const running = this._currentlyRunning;
|
|
22039
|
-
const queue = running ? this._queue.concat(running) : this._queue;
|
|
22040
|
-
for (const task of queue) if (task.id === id) {
|
|
22041
|
-
task.cancelled = true;
|
|
22042
|
-
return;
|
|
22043
|
-
}
|
|
22044
|
-
}
|
|
22045
|
-
run(timeStamp = 0) {
|
|
22046
|
-
if (this._currentlyRunning) throw new Error("Attempting to run(), but is already running.");
|
|
22047
|
-
const queue = this._currentlyRunning = this._queue;
|
|
22048
|
-
this._queue = [];
|
|
22049
|
-
for (const task of queue) {
|
|
22050
|
-
if (task.cancelled) continue;
|
|
22051
|
-
task.callback(timeStamp);
|
|
22052
|
-
if (this._cleared) break;
|
|
22053
|
-
}
|
|
22054
|
-
this._cleared = false;
|
|
22055
|
-
this._currentlyRunning = false;
|
|
22056
|
-
}
|
|
22057
|
-
clear() {
|
|
22058
|
-
if (this._currentlyRunning) this._cleared = true;
|
|
22059
|
-
this._queue = [];
|
|
22060
|
-
}
|
|
22061
|
-
};
|
|
22062
|
-
//#endregion
|
|
22063
|
-
//#region src/data/pos3d_attributes.ts
|
|
22064
|
-
const pos3dAttributes = createLayout([{
|
|
22065
|
-
name: "a_pos3d",
|
|
22066
|
-
type: "Int16",
|
|
22067
|
-
components: 3
|
|
22068
|
-
}]);
|
|
22069
|
-
//#endregion
|
|
22070
|
-
//#region src/tile/terrain_tile_manager.ts
|
|
22071
|
-
/**
|
|
22072
|
-
* @internal
|
|
22073
|
-
* This class is a helper for the Terrain-class, it:
|
|
22074
|
-
*
|
|
22075
|
-
* - loads raster-dem tiles
|
|
22076
|
-
* - manages all renderToTexture tiles.
|
|
22077
|
-
* - caches previous rendered tiles.
|
|
22078
|
-
* - finds all necessary renderToTexture tiles for a OverscaledTileID area
|
|
22079
|
-
* - finds the corresponding raster-dem tile for OverscaledTileID
|
|
22080
|
-
*/
|
|
22081
|
-
var TerrainTileManager = class extends Evented {
|
|
22082
|
-
constructor(tileManager) {
|
|
22083
|
-
super();
|
|
22084
|
-
this._lastTilesetChange = now();
|
|
22085
|
-
this.tileManager = tileManager;
|
|
22086
|
-
this._tiles = {};
|
|
22087
|
-
this._renderableTilesKeys = [];
|
|
22088
|
-
this._sourceTileCache = {};
|
|
22089
|
-
this.minzoom = 0;
|
|
22090
|
-
this.maxzoom = 22;
|
|
22091
|
-
this.deltaZoom = 1;
|
|
22092
|
-
this.tileSize = tileManager._source.tileSize * 2 ** this.deltaZoom;
|
|
22093
|
-
tileManager.usedForTerrain = true;
|
|
22094
|
-
tileManager.tileSize = this.tileSize;
|
|
22095
|
-
}
|
|
22096
|
-
destruct() {
|
|
22097
|
-
this.tileManager.usedForTerrain = false;
|
|
22098
|
-
this.tileManager.tileSize = null;
|
|
22099
|
-
this.releaseAllRTT();
|
|
22100
|
-
}
|
|
22101
|
-
getSource() {
|
|
22102
|
-
return this.tileManager._source;
|
|
22103
|
-
}
|
|
22104
|
-
/**
|
|
22105
|
-
* Load Terrain Tiles, create internal render-to-texture tiles, free GPU memory.
|
|
22106
|
-
* @param transform - the operation to do
|
|
22107
|
-
* @param terrain - the terrain
|
|
22108
|
-
*/
|
|
22109
|
-
update(transform, terrain) {
|
|
22110
|
-
this.tileManager.update(transform, terrain);
|
|
22111
|
-
this._renderableTilesKeys = [];
|
|
22112
|
-
const keys = {};
|
|
22113
|
-
for (const tileID of coveringTiles(transform, {
|
|
22114
|
-
tileSize: this.tileSize,
|
|
22115
|
-
minzoom: this.minzoom,
|
|
22116
|
-
maxzoom: this.maxzoom,
|
|
22117
|
-
reparseOverscaled: false,
|
|
22118
|
-
terrain,
|
|
22119
|
-
calculateTileZoom: this.tileManager._source.calculateTileZoom
|
|
22120
|
-
})) {
|
|
22121
|
-
keys[tileID.key] = true;
|
|
22122
|
-
this._renderableTilesKeys.push(tileID.key);
|
|
22123
|
-
if (!this._tiles[tileID.key]) {
|
|
22124
|
-
tileID.terrainRttPosMatrix32f = /* @__PURE__ */ new Float32Array(16);
|
|
22125
|
-
ortho(tileID.terrainRttPosMatrix32f, 0, EXTENT, EXTENT, 0, 0, 1);
|
|
22126
|
-
this._tiles[tileID.key] = new Tile(tileID, this.tileSize);
|
|
22127
|
-
this._lastTilesetChange = now();
|
|
22128
|
-
}
|
|
22129
|
-
}
|
|
22130
|
-
for (const key in this._tiles) if (!keys[key]) {
|
|
22131
|
-
this._tiles[key].releaseRTT(this.tileManager.map.painter);
|
|
22132
|
-
delete this._tiles[key];
|
|
22133
|
-
}
|
|
22707
|
+
this._ease((k) => {
|
|
22708
|
+
easeHandler.easeFunc(k);
|
|
22709
|
+
if (this.terrain && !options.freezeElevation) this._updateElevation(k);
|
|
22710
|
+
this.applyUpdatedTransform(tr);
|
|
22711
|
+
this._fireMoveEvents(eventData);
|
|
22712
|
+
}, (interruptingEaseId) => {
|
|
22713
|
+
if (this.terrain && options.freezeElevation) this._finalizeElevation();
|
|
22714
|
+
this._afterEase(eventData, interruptingEaseId);
|
|
22715
|
+
}, options);
|
|
22716
|
+
return this;
|
|
22134
22717
|
}
|
|
22135
|
-
|
|
22136
|
-
|
|
22137
|
-
|
|
22138
|
-
|
|
22139
|
-
|
|
22140
|
-
|
|
22141
|
-
|
|
22142
|
-
}
|
|
22718
|
+
_prepareEase(eventData, noMoveStart, currently = {}) {
|
|
22719
|
+
this._moving = true;
|
|
22720
|
+
if (!noMoveStart && !currently.moving) this.fire(new MapMovementEvent("movestart", eventData));
|
|
22721
|
+
if (this._zooming && !currently.zooming) this.fire(new MapMovementEvent("zoomstart", eventData));
|
|
22722
|
+
if (this._rotating && !currently.rotating) this.fire(new MapMovementEvent("rotatestart", eventData));
|
|
22723
|
+
if (this._pitching && !currently.pitching) this.fire(new MapMovementEvent("pitchstart", eventData));
|
|
22724
|
+
if (this._rolling && !currently.rolling) this.fire(new MapMovementEvent("rollstart", eventData));
|
|
22143
22725
|
}
|
|
22144
|
-
|
|
22145
|
-
|
|
22146
|
-
|
|
22147
|
-
|
|
22148
|
-
|
|
22726
|
+
_prepareElevation(center) {
|
|
22727
|
+
this._elevationCenter = center;
|
|
22728
|
+
this._elevationStart = this.transform.elevation;
|
|
22729
|
+
this._elevationTarget = this.terrain.getElevationForLngLat(center, this.transform);
|
|
22730
|
+
this.elevationFreeze = true;
|
|
22149
22731
|
}
|
|
22150
|
-
|
|
22151
|
-
|
|
22152
|
-
|
|
22153
|
-
|
|
22154
|
-
|
|
22155
|
-
|
|
22732
|
+
_updateElevation(k) {
|
|
22733
|
+
if (this._elevationStart === void 0 || this._elevationCenter === void 0) this._prepareElevation(this.transform.center);
|
|
22734
|
+
this.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this._elevationCenter, this.transform.tileZoom));
|
|
22735
|
+
const elevation = this.terrain.getElevationForLngLat(this._elevationCenter, this.transform);
|
|
22736
|
+
if (k < 1 && elevation !== this._elevationTarget) {
|
|
22737
|
+
const pitch1 = this._elevationTarget - this._elevationStart;
|
|
22738
|
+
const pitch2 = (elevation - (pitch1 * k + this._elevationStart)) / (1 - k);
|
|
22739
|
+
this._elevationStart += k * (pitch1 - pitch2);
|
|
22740
|
+
this._elevationTarget = elevation;
|
|
22741
|
+
}
|
|
22742
|
+
this.transform.setElevation(interpolateFactory.number(this._elevationStart, this._elevationTarget, k));
|
|
22156
22743
|
}
|
|
22157
|
-
|
|
22158
|
-
|
|
22159
|
-
|
|
22160
|
-
* @returns the tile
|
|
22161
|
-
*/
|
|
22162
|
-
getTileByID(id) {
|
|
22163
|
-
return this._tiles[id];
|
|
22744
|
+
_finalizeElevation() {
|
|
22745
|
+
this.elevationFreeze = false;
|
|
22746
|
+
if (this.getCenterClampedToGround()) this.transform.recalculateZoomAndCenter(this.terrain);
|
|
22164
22747
|
}
|
|
22165
22748
|
/**
|
|
22166
|
-
*
|
|
22167
|
-
*
|
|
22168
|
-
*
|
|
22749
|
+
* @internal
|
|
22750
|
+
* Called when the camera is about to be manipulated.
|
|
22751
|
+
* If `transformCameraUpdate` is specified or terrain is enabled, a copy of
|
|
22752
|
+
* the current transform is created to track the accumulated changes.
|
|
22753
|
+
* This underlying transform represents the "desired state" proposed by input handlers / animations / UI controls.
|
|
22754
|
+
* It may differ from the state used for rendering (`this.transform`).
|
|
22755
|
+
* @returns Transform to apply changes to
|
|
22169
22756
|
*/
|
|
22170
|
-
|
|
22171
|
-
if (
|
|
22172
|
-
|
|
22757
|
+
getTransformForUpdate() {
|
|
22758
|
+
if (!this.transformCameraUpdate && !this.terrain) return this.transform;
|
|
22759
|
+
this._requestedCameraState ||= this.transform.clone();
|
|
22760
|
+
return this._requestedCameraState;
|
|
22173
22761
|
}
|
|
22174
22762
|
/**
|
|
22175
|
-
*
|
|
22176
|
-
*
|
|
22177
|
-
*
|
|
22178
|
-
*
|
|
22179
|
-
*
|
|
22180
|
-
*
|
|
22181
|
-
*
|
|
22763
|
+
* @internal
|
|
22764
|
+
* Checks the given transform for the camera being below terrain surface and
|
|
22765
|
+
* returns new pitch and zoom to fix that.
|
|
22766
|
+
*
|
|
22767
|
+
* With the new pitch and zoom, the camera will be at the same ground
|
|
22768
|
+
* position but at higher altitude. It will still point to the same spot on
|
|
22769
|
+
* the map.
|
|
22770
|
+
*
|
|
22771
|
+
* @param tr - The transform to check.
|
|
22182
22772
|
*/
|
|
22183
|
-
|
|
22184
|
-
|
|
22185
|
-
|
|
22186
|
-
|
|
22187
|
-
|
|
22188
|
-
|
|
22189
|
-
|
|
22190
|
-
|
|
22191
|
-
|
|
22192
|
-
|
|
22193
|
-
|
|
22194
|
-
const size = EXTENT >> dz;
|
|
22195
|
-
ortho(mat, 0, size, size, 0, 0, 1);
|
|
22196
|
-
translate(mat, mat, [
|
|
22197
|
-
-dx * size,
|
|
22198
|
-
-dy * size,
|
|
22199
|
-
0
|
|
22200
|
-
]);
|
|
22201
|
-
} else if (tileID.canonical.isChildOf(terrainTileID.canonical)) {
|
|
22202
|
-
const dz = tileID.canonical.z - terrainTileID.canonical.z;
|
|
22203
|
-
const dx = tileID.canonical.x - (tileID.canonical.x >> dz << dz);
|
|
22204
|
-
const dy = tileID.canonical.y - (tileID.canonical.y >> dz << dz);
|
|
22205
|
-
const size = EXTENT >> dz;
|
|
22206
|
-
ortho(mat, 0, EXTENT, EXTENT, 0, 0, 1);
|
|
22207
|
-
translate(mat, mat, [
|
|
22208
|
-
dx * size,
|
|
22209
|
-
dy * size,
|
|
22210
|
-
0
|
|
22211
|
-
]);
|
|
22212
|
-
scale(mat, mat, [
|
|
22213
|
-
1 / 2 ** dz,
|
|
22214
|
-
1 / 2 ** dz,
|
|
22215
|
-
0
|
|
22216
|
-
]);
|
|
22217
|
-
} else continue;
|
|
22218
|
-
coord.terrainRttPosMatrix32f = new Float32Array(mat);
|
|
22219
|
-
coords[key] = coord;
|
|
22773
|
+
_elevateCameraIfInsideTerrain(tr) {
|
|
22774
|
+
if (!this.terrain && tr.elevation >= 0 && tr.pitch <= 90) return {};
|
|
22775
|
+
const cameraLngLat = tr.getCameraLngLat();
|
|
22776
|
+
const cameraAltitude = tr.getCameraAltitude();
|
|
22777
|
+
const minAltitude = this.terrain ? this.terrain.getElevationForLngLatZoom(cameraLngLat, tr.zoom) : 0;
|
|
22778
|
+
if (cameraAltitude < minAltitude) {
|
|
22779
|
+
const newCamera = this.calculateCameraOptionsFromTo(cameraLngLat, minAltitude, tr.center, tr.elevation);
|
|
22780
|
+
return {
|
|
22781
|
+
pitch: newCamera.pitch,
|
|
22782
|
+
zoom: newCamera.zoom
|
|
22783
|
+
};
|
|
22220
22784
|
}
|
|
22221
|
-
return
|
|
22785
|
+
return {};
|
|
22222
22786
|
}
|
|
22223
22787
|
/**
|
|
22224
|
-
*
|
|
22225
|
-
*
|
|
22226
|
-
* @param
|
|
22227
|
-
*
|
|
22788
|
+
* @internal
|
|
22789
|
+
* Called after the camera is done being manipulated.
|
|
22790
|
+
* @param tr - the requested camera end state
|
|
22791
|
+
* If the camera is inside terrain, it gets elevated.
|
|
22792
|
+
* Call `transformCameraUpdate` if present, and then apply the "approved" changes.
|
|
22228
22793
|
*/
|
|
22229
|
-
|
|
22230
|
-
const
|
|
22231
|
-
|
|
22232
|
-
|
|
22233
|
-
|
|
22234
|
-
|
|
22235
|
-
|
|
22236
|
-
|
|
22237
|
-
|
|
22238
|
-
|
|
22239
|
-
|
|
22240
|
-
|
|
22241
|
-
|
|
22242
|
-
|
|
22243
|
-
|
|
22244
|
-
|
|
22245
|
-
} else if (terrainTileID.canonical.z > tileID.canonical.z) {
|
|
22246
|
-
const dz = terrainTileID.canonical.z - tileID.canonical.z;
|
|
22247
|
-
const dx = terrainTileID.canonical.x - (terrainTileID.canonical.x >> dz << dz) + tileID.wrap * (1 << terrainTileID.canonical.z);
|
|
22248
|
-
const dy = terrainTileID.canonical.y - (terrainTileID.canonical.y >> dz << dz);
|
|
22249
|
-
const dx2 = tileID.canonical.x - (terrainTileID.canonical.x >> dz);
|
|
22250
|
-
const dy2 = tileID.canonical.y - (terrainTileID.canonical.y >> dz);
|
|
22251
|
-
const size = EXTENT >> dz;
|
|
22252
|
-
ortho(mat, 0, size, size, 0, 0, 1);
|
|
22253
|
-
translate(mat, mat, [
|
|
22254
|
-
-dx * size + dx2 * EXTENT,
|
|
22255
|
-
-dy * size + dy2 * EXTENT,
|
|
22256
|
-
0
|
|
22257
|
-
]);
|
|
22258
|
-
} else {
|
|
22259
|
-
const dz = tileID.canonical.z - terrainTileID.canonical.z;
|
|
22260
|
-
const dx = tileID.canonical.x - (tileID.canonical.x >> dz << dz) + tileID.wrap * (1 << tileID.canonical.z);
|
|
22261
|
-
const dy = tileID.canonical.y - (tileID.canonical.y >> dz << dz);
|
|
22262
|
-
const dx2 = (tileID.canonical.x >> dz) - terrainTileID.canonical.x;
|
|
22263
|
-
const dy2 = (tileID.canonical.y >> dz) - terrainTileID.canonical.y;
|
|
22264
|
-
const size = EXTENT << dz;
|
|
22265
|
-
ortho(mat, 0, size, size, 0, 0, 1);
|
|
22266
|
-
translate(mat, mat, [
|
|
22267
|
-
dx * EXTENT + dx2 * size,
|
|
22268
|
-
dy * EXTENT + dy2 * size,
|
|
22269
|
-
0
|
|
22270
|
-
]);
|
|
22271
|
-
}
|
|
22272
|
-
coord.terrainRttPosMatrix32f = new Float32Array(mat);
|
|
22273
|
-
coords[key] = coord;
|
|
22794
|
+
applyUpdatedTransform(tr) {
|
|
22795
|
+
const modifiers = [];
|
|
22796
|
+
modifiers.push((tr) => this._elevateCameraIfInsideTerrain(tr));
|
|
22797
|
+
if (this.transformCameraUpdate) modifiers.push((tr) => this.transformCameraUpdate(tr));
|
|
22798
|
+
if (!modifiers.length) return;
|
|
22799
|
+
const finalTransform = tr.clone();
|
|
22800
|
+
for (const modifier of modifiers) {
|
|
22801
|
+
const nextTransform = finalTransform.clone();
|
|
22802
|
+
const { center, zoom, roll, pitch, bearing, elevation } = modifier(nextTransform);
|
|
22803
|
+
if (center) nextTransform.setCenter(center);
|
|
22804
|
+
if (elevation !== void 0) nextTransform.setElevation(elevation);
|
|
22805
|
+
if (zoom !== void 0) nextTransform.setZoom(zoom);
|
|
22806
|
+
if (roll !== void 0) nextTransform.setRoll(roll);
|
|
22807
|
+
if (pitch !== void 0) nextTransform.setPitch(pitch);
|
|
22808
|
+
if (bearing !== void 0) nextTransform.setBearing(bearing);
|
|
22809
|
+
finalTransform.apply(nextTransform, false);
|
|
22274
22810
|
}
|
|
22275
|
-
|
|
22276
|
-
}
|
|
22277
|
-
/**
|
|
22278
|
-
* find the covering raster-dem tile
|
|
22279
|
-
* @param tileID - the tile to look for
|
|
22280
|
-
* @param searchForDEM - Optional parameter to search for (parent) source tiles with loaded dem.
|
|
22281
|
-
* @returns the tile
|
|
22282
|
-
*/
|
|
22283
|
-
getSourceTile(tileID, searchForDEM) {
|
|
22284
|
-
const source = this.tileManager._source;
|
|
22285
|
-
let z = tileID.overscaledZ - this.deltaZoom;
|
|
22286
|
-
if (z > source.maxzoom) z = source.maxzoom;
|
|
22287
|
-
if (z < source.minzoom) return void 0;
|
|
22288
|
-
this._sourceTileCache[tileID.key] ||= tileID.scaledTo(z).key;
|
|
22289
|
-
let tile = this.findTileInCaches(this._sourceTileCache[tileID.key]);
|
|
22290
|
-
if (!tile?.dem && searchForDEM) while (z >= source.minzoom && !tile?.dem) tile = this.findTileInCaches(tileID.scaledTo(z--).key);
|
|
22291
|
-
return tile;
|
|
22292
|
-
}
|
|
22293
|
-
findTileInCaches(key) {
|
|
22294
|
-
let tile = this.tileManager.getTileByID(key);
|
|
22295
|
-
if (tile) return tile;
|
|
22296
|
-
tile = this.tileManager._outOfViewCache.getByKey(key);
|
|
22297
|
-
return tile;
|
|
22298
|
-
}
|
|
22299
|
-
/**
|
|
22300
|
-
* gets whether any tiles were loaded after a specific time. This is used to update depth & coords framebuffers.
|
|
22301
|
-
* @param time - the time
|
|
22302
|
-
* @returns true if any tiles came into view at or after the specified time
|
|
22303
|
-
*/
|
|
22304
|
-
anyTilesAfterTime(time = now()) {
|
|
22305
|
-
return this._lastTilesetChange >= time;
|
|
22811
|
+
this.transform.apply(finalTransform, false);
|
|
22306
22812
|
}
|
|
22307
|
-
|
|
22308
|
-
|
|
22309
|
-
|
|
22310
|
-
|
|
22311
|
-
|
|
22312
|
-
|
|
22313
|
-
_isWithinTileRanges(tileID, canonicalTileRanges) {
|
|
22314
|
-
const range = canonicalTileRanges[tileID.canonical.z];
|
|
22315
|
-
return !!range && (tileID.wrap > range.minWrap || tileID.wrap < range.maxWrap || tileID.canonical.x >= range.minTileXWrapped && tileID.canonical.x <= range.maxTileXWrapped && tileID.canonical.y >= range.minTileY && tileID.canonical.y <= range.maxTileY);
|
|
22813
|
+
_fireMoveEvents(eventData) {
|
|
22814
|
+
this.fire(new MapMovementEvent("move", eventData));
|
|
22815
|
+
if (this._zooming) this.fire(new MapMovementEvent("zoom", eventData));
|
|
22816
|
+
if (this._rotating) this.fire(new MapMovementEvent("rotate", eventData));
|
|
22817
|
+
if (this._pitching) this.fire(new MapMovementEvent("pitch", eventData));
|
|
22818
|
+
if (this._rolling) this.fire(new MapMovementEvent("roll", eventData));
|
|
22316
22819
|
}
|
|
22317
|
-
|
|
22318
|
-
|
|
22319
|
-
|
|
22320
|
-
|
|
22321
|
-
|
|
22322
|
-
|
|
22323
|
-
|
|
22324
|
-
|
|
22325
|
-
|
|
22326
|
-
|
|
22327
|
-
|
|
22328
|
-
|
|
22329
|
-
|
|
22330
|
-
|
|
22331
|
-
|
|
22332
|
-
|
|
22333
|
-
|
|
22334
|
-
|
|
22335
|
-
* - one for the depth & coords framebuffer with the size of the map-div.
|
|
22336
|
-
* - one for rendering a tile to texture with the size of tileSize (= 512x512).
|
|
22337
|
-
*
|
|
22338
|
-
* Textures:
|
|
22339
|
-
*
|
|
22340
|
-
* - one texture for an empty raster-dem tile with size 1x1
|
|
22341
|
-
* - one texture for an empty depth-buffer, when terrain is disabled with size 1x1
|
|
22342
|
-
* - one texture for an each loaded raster-dem with size of the source.tileSize
|
|
22343
|
-
* - one texture for the coords-framebuffer with the size of the map-div.
|
|
22344
|
-
* - one texture for the depth-framebuffer with the size of the map-div.
|
|
22345
|
-
* - one texture for the encoded tile-coords with the size 2*tileSize (=1024x1024)
|
|
22346
|
-
* - finally for each render-to-texture tile (= this._tiles) a set of textures
|
|
22347
|
-
* for each render stack (The stack-concept is documented in painter.ts).
|
|
22348
|
-
*
|
|
22349
|
-
* Normally there exists 1-3 Textures per tile, depending on the stylesheet.
|
|
22350
|
-
* Each Textures has the size 2*tileSize (= 1024x1024). Also there exists a
|
|
22351
|
-
* cache of the last 150 newest rendered tiles.
|
|
22352
|
-
*
|
|
22353
|
-
*/
|
|
22354
|
-
var Terrain = class {
|
|
22355
|
-
constructor(painter, tileManager, options, terrainSkirtLength = "auto") {
|
|
22356
|
-
this._meshCache = {};
|
|
22357
|
-
this.painter = painter;
|
|
22358
|
-
this.tileManager = new TerrainTileManager(tileManager);
|
|
22359
|
-
this.options = options;
|
|
22360
|
-
this.exaggeration = typeof options.exaggeration === "number" ? options.exaggeration : 1;
|
|
22361
|
-
this._terrainSkirtLength = terrainSkirtLength;
|
|
22362
|
-
this.qualityFactor = 2;
|
|
22363
|
-
this.meshSize = 128;
|
|
22364
|
-
this._demMatrixCache = /* @__PURE__ */ new Map();
|
|
22365
|
-
this._elevationSamplerCache = /* @__PURE__ */ new Map();
|
|
22366
|
-
this.coordsIndex = [];
|
|
22367
|
-
this._coordsTextureSize = 1024;
|
|
22820
|
+
_afterEase(eventData, easeId) {
|
|
22821
|
+
if (this._easeId && easeId && this._easeId === easeId) return;
|
|
22822
|
+
delete this._easeId;
|
|
22823
|
+
const wasZooming = this._zooming;
|
|
22824
|
+
const wasRotating = this._rotating;
|
|
22825
|
+
const wasPitching = this._pitching;
|
|
22826
|
+
const wasRolling = this._rolling;
|
|
22827
|
+
this._moving = false;
|
|
22828
|
+
this._zooming = false;
|
|
22829
|
+
this._rotating = false;
|
|
22830
|
+
this._pitching = false;
|
|
22831
|
+
this._rolling = false;
|
|
22832
|
+
this._padding = false;
|
|
22833
|
+
if (wasZooming) this.fire(new MapMovementEvent("zoomend", eventData));
|
|
22834
|
+
if (wasRotating) this.fire(new MapMovementEvent("rotateend", eventData));
|
|
22835
|
+
if (wasPitching) this.fire(new MapMovementEvent("pitchend", eventData));
|
|
22836
|
+
if (wasRolling) this.fire(new MapMovementEvent("rollend", eventData));
|
|
22837
|
+
this.fire(new MapMovementEvent("moveend", eventData));
|
|
22368
22838
|
}
|
|
22369
|
-
|
|
22370
|
-
if (
|
|
22371
|
-
|
|
22372
|
-
|
|
22839
|
+
flyTo(options, eventData) {
|
|
22840
|
+
if (!options.essential && browser.prefersReducedMotion) {
|
|
22841
|
+
const coercedOptions = pick(options, [
|
|
22842
|
+
"center",
|
|
22843
|
+
"zoom",
|
|
22844
|
+
"bearing",
|
|
22845
|
+
"pitch",
|
|
22846
|
+
"roll",
|
|
22847
|
+
"elevation",
|
|
22848
|
+
"padding"
|
|
22849
|
+
]);
|
|
22850
|
+
return this.jumpTo(coercedOptions, eventData);
|
|
22373
22851
|
}
|
|
22374
|
-
|
|
22375
|
-
|
|
22376
|
-
|
|
22852
|
+
this.stop();
|
|
22853
|
+
options = extend({
|
|
22854
|
+
offset: [0, 0],
|
|
22855
|
+
speed: 1.2,
|
|
22856
|
+
curve: 1.42,
|
|
22857
|
+
easing: defaultEasing
|
|
22858
|
+
}, options);
|
|
22859
|
+
if ("zoom" in options && this._zoomSnap) options.zoom = evaluateZoomSnap(options.zoom, this._zoomSnap);
|
|
22860
|
+
const tr = this.getTransformForUpdate(), startBearing = tr.bearing, startPitch = tr.pitch, startRoll = tr.roll, startPadding = tr.padding;
|
|
22861
|
+
const bearing = "bearing" in options ? this._normalizeBearing(options.bearing, startBearing) : startBearing;
|
|
22862
|
+
const pitch = "pitch" in options ? +options.pitch : startPitch;
|
|
22863
|
+
const roll = "roll" in options ? this._normalizeBearing(options.roll, startRoll) : startRoll;
|
|
22864
|
+
const padding = "padding" in options ? options.padding : tr.padding;
|
|
22865
|
+
const offsetAsPoint = Point.convert(options.offset);
|
|
22866
|
+
let pointAtOffset = tr.centerPoint.add(offsetAsPoint);
|
|
22867
|
+
const locationAtOffset = tr.screenPointToLocation(pointAtOffset);
|
|
22868
|
+
const flyToHandler = this.cameraHelper.handleFlyTo(tr, {
|
|
22869
|
+
bearing,
|
|
22870
|
+
pitch,
|
|
22871
|
+
roll,
|
|
22872
|
+
padding,
|
|
22873
|
+
locationAtOffset,
|
|
22874
|
+
offsetAsPoint,
|
|
22875
|
+
center: options.center,
|
|
22876
|
+
minZoom: options.minZoom,
|
|
22877
|
+
zoom: options.zoom
|
|
22878
|
+
});
|
|
22879
|
+
let rho = options.curve;
|
|
22880
|
+
const w0 = Math.max(tr.width, tr.height);
|
|
22881
|
+
const w1 = w0 / flyToHandler.scaleOfZoom;
|
|
22882
|
+
const u1 = flyToHandler.pixelPathLength;
|
|
22883
|
+
const wMax = w0 / flyToHandler.scaleOfMinZoom;
|
|
22884
|
+
rho = Math.min(rho, Math.sqrt(wMax / u1 * 2));
|
|
22885
|
+
const rho2 = rho * rho;
|
|
22886
|
+
/**
|
|
22887
|
+
* rᵢ: Returns the zoom-out factor at one end of the animation.
|
|
22888
|
+
*
|
|
22889
|
+
* @param descent - `true` for the descent, `false` for the ascent
|
|
22890
|
+
*/
|
|
22891
|
+
function zoomOutFactor(descent) {
|
|
22892
|
+
const b = (w1 * w1 - w0 * w0 + (descent ? -1 : 1) * rho2 * rho2 * u1 * u1) / (2 * (descent ? w1 : w0) * rho2 * u1);
|
|
22893
|
+
return Math.log(Math.sqrt(b * b + 1) - b);
|
|
22377
22894
|
}
|
|
22378
|
-
|
|
22379
|
-
|
|
22380
|
-
this._fboDepthTexture = null;
|
|
22895
|
+
function sinh(n) {
|
|
22896
|
+
return (Math.exp(n) - Math.exp(-n)) / 2;
|
|
22381
22897
|
}
|
|
22382
|
-
|
|
22383
|
-
|
|
22384
|
-
this._emptyDemTexture = null;
|
|
22898
|
+
function cosh(n) {
|
|
22899
|
+
return (Math.exp(n) + Math.exp(-n)) / 2;
|
|
22385
22900
|
}
|
|
22386
|
-
|
|
22387
|
-
|
|
22388
|
-
|
|
22901
|
+
function tanh(n) {
|
|
22902
|
+
return sinh(n) / cosh(n);
|
|
22903
|
+
}
|
|
22904
|
+
const r0 = zoomOutFactor(false);
|
|
22905
|
+
let w = function(s) {
|
|
22906
|
+
return cosh(r0) / cosh(r0 + rho * s);
|
|
22907
|
+
};
|
|
22908
|
+
let u = function(s) {
|
|
22909
|
+
return w0 * ((cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2) / u1;
|
|
22910
|
+
};
|
|
22911
|
+
let S = (zoomOutFactor(true) - r0) / rho;
|
|
22912
|
+
if (Math.abs(u1) < 2e-6 || !isFinite(S)) {
|
|
22913
|
+
if (Math.abs(w0 - w1) < 1e-6) return this.easeTo(options, eventData);
|
|
22914
|
+
const k = w1 < w0 ? -1 : 1;
|
|
22915
|
+
S = Math.abs(Math.log(w1 / w0)) / rho;
|
|
22916
|
+
u = () => 0;
|
|
22917
|
+
w = (s) => Math.exp(k * rho * s);
|
|
22389
22918
|
}
|
|
22390
|
-
if (
|
|
22391
|
-
|
|
22392
|
-
|
|
22919
|
+
if ("duration" in options) options.duration = +options.duration;
|
|
22920
|
+
else {
|
|
22921
|
+
const V = "screenSpeed" in options ? +options.screenSpeed / rho : +options.speed;
|
|
22922
|
+
options.duration = 1e3 * S / V;
|
|
22393
22923
|
}
|
|
22394
|
-
|
|
22395
|
-
this.
|
|
22396
|
-
this.
|
|
22924
|
+
if (options.maxDuration && options.duration > options.maxDuration) options.duration = 0;
|
|
22925
|
+
this._zooming = true;
|
|
22926
|
+
this._rotating = startBearing !== bearing;
|
|
22927
|
+
this._pitching = pitch !== startPitch;
|
|
22928
|
+
this._rolling = roll !== startRoll;
|
|
22929
|
+
this._padding = !tr.isPaddingEqual(padding);
|
|
22930
|
+
this._prepareEase(eventData, false);
|
|
22931
|
+
if (this.terrain) this._prepareElevation(flyToHandler.targetCenter);
|
|
22932
|
+
this._ease((k) => {
|
|
22933
|
+
const s = k * S;
|
|
22934
|
+
const scale = 1 / w(s);
|
|
22935
|
+
const centerFactor = u(s);
|
|
22936
|
+
if (this._rotating) tr.setBearing(interpolateFactory.number(startBearing, bearing, k));
|
|
22937
|
+
if (this._pitching) tr.setPitch(interpolateFactory.number(startPitch, pitch, k));
|
|
22938
|
+
if (this._rolling) tr.setRoll(interpolateFactory.number(startRoll, roll, k));
|
|
22939
|
+
if (this._padding) {
|
|
22940
|
+
tr.interpolatePadding(startPadding, padding, k);
|
|
22941
|
+
pointAtOffset = tr.centerPoint.add(offsetAsPoint);
|
|
22942
|
+
}
|
|
22943
|
+
flyToHandler.easeFunc(k, scale, centerFactor, pointAtOffset);
|
|
22944
|
+
if (this.terrain && !options.freezeElevation) this._updateElevation(k);
|
|
22945
|
+
this.applyUpdatedTransform(tr);
|
|
22946
|
+
this._fireMoveEvents(eventData);
|
|
22947
|
+
}, () => {
|
|
22948
|
+
if (this.terrain && options.freezeElevation) this._finalizeElevation();
|
|
22949
|
+
this._afterEase(eventData);
|
|
22950
|
+
}, options);
|
|
22951
|
+
return this;
|
|
22397
22952
|
}
|
|
22398
|
-
|
|
22399
|
-
|
|
22400
|
-
* Coordinates that fall outside `[0, extent)` are normalized to the
|
|
22401
|
-
* appropriate neighbor tile before lookup.
|
|
22402
|
-
* @param tileID - the tile to get the elevation for
|
|
22403
|
-
* @param x - x coordinate relative to the tile, may be outside `[0, extent)`
|
|
22404
|
-
* @param y - y coordinate relative to the tile, may be outside `[0, extent)`
|
|
22405
|
-
* @param extent - optional, default 8192
|
|
22406
|
-
* @returns the elevation
|
|
22407
|
-
*/
|
|
22408
|
-
getDEMElevation(tileID, x, y, extent = EXTENT) {
|
|
22409
|
-
const normalized = tileID.normalizeCoordinates(x, y, extent);
|
|
22410
|
-
if (!normalized) return 0;
|
|
22411
|
-
const sampler = this._getElevationSampler(normalized.tileID);
|
|
22412
|
-
return sampler ? sampler(normalized.x, normalized.y, extent) : 0;
|
|
22953
|
+
isEasing() {
|
|
22954
|
+
return !!this._easeFrameId;
|
|
22413
22955
|
}
|
|
22414
|
-
|
|
22415
|
-
|
|
22416
|
-
* @param lnglat - the location
|
|
22417
|
-
* @param zoom - the zoom, use {@link getElevationForLngLat} if you don't want a specific zoom level, but more accurate results.
|
|
22418
|
-
* @returns the elevation
|
|
22419
|
-
*/
|
|
22420
|
-
getElevationForLngLatZoom(lnglat, zoom) {
|
|
22421
|
-
if (!isInBoundsForZoomLngLat(zoom, lnglat.wrap())) return 0;
|
|
22422
|
-
const { tileID, mercatorX, mercatorY } = this._getOverscaledTileIDFromLngLatZoom(lnglat, zoom);
|
|
22423
|
-
return this.getElevation(tileID, mercatorX % EXTENT, mercatorY % EXTENT, EXTENT);
|
|
22956
|
+
stop(allowGestures) {
|
|
22957
|
+
return this._stop(allowGestures);
|
|
22424
22958
|
}
|
|
22425
|
-
|
|
22426
|
-
|
|
22427
|
-
|
|
22428
|
-
|
|
22429
|
-
|
|
22430
|
-
|
|
22431
|
-
|
|
22432
|
-
|
|
22433
|
-
|
|
22434
|
-
|
|
22435
|
-
|
|
22436
|
-
|
|
22437
|
-
|
|
22438
|
-
let zoom = 0;
|
|
22439
|
-
for (const tile of terrainCoveringTiles) if (tile.canonical.z > zoom) zoom = Math.min(tile.canonical.z, this.tileManager.maxzoom);
|
|
22440
|
-
return this.getElevationForLngLatZoom(lnglat, zoom);
|
|
22959
|
+
_stop(allowGestures, easeId) {
|
|
22960
|
+
if (this._easeFrameId) {
|
|
22961
|
+
this._cancelRenderFrame(this._easeFrameId);
|
|
22962
|
+
delete this._easeFrameId;
|
|
22963
|
+
delete this._onEaseFrame;
|
|
22964
|
+
}
|
|
22965
|
+
if (this._onEaseEnd) {
|
|
22966
|
+
const onEaseEnd = this._onEaseEnd;
|
|
22967
|
+
delete this._onEaseEnd;
|
|
22968
|
+
onEaseEnd.call(this, easeId);
|
|
22969
|
+
}
|
|
22970
|
+
if (!allowGestures) this._stopHandlers();
|
|
22971
|
+
return this;
|
|
22441
22972
|
}
|
|
22442
|
-
|
|
22443
|
-
|
|
22444
|
-
|
|
22445
|
-
|
|
22446
|
-
|
|
22447
|
-
|
|
22448
|
-
|
|
22449
|
-
|
|
22450
|
-
|
|
22451
|
-
|
|
22973
|
+
_ease(frame, finish, options) {
|
|
22974
|
+
if (options.animate === false || options.duration === 0) {
|
|
22975
|
+
frame(1);
|
|
22976
|
+
finish();
|
|
22977
|
+
} else {
|
|
22978
|
+
this._easeStart = now();
|
|
22979
|
+
this._easeOptions = options;
|
|
22980
|
+
this._onEaseFrame = frame;
|
|
22981
|
+
this._onEaseEnd = finish;
|
|
22982
|
+
this._easeFrameId = this._requestRenderFrame(this._renderFrameCallback);
|
|
22983
|
+
}
|
|
22452
22984
|
}
|
|
22453
|
-
|
|
22454
|
-
|
|
22455
|
-
|
|
22456
|
-
|
|
22457
|
-
|
|
22458
|
-
|
|
22985
|
+
_normalizeBearing(bearing, currentBearing) {
|
|
22986
|
+
bearing = wrap(bearing, -180, 180);
|
|
22987
|
+
const diff = Math.abs(bearing - currentBearing);
|
|
22988
|
+
if (Math.abs(bearing - 360 - currentBearing) < diff) bearing -= 360;
|
|
22989
|
+
if (Math.abs(bearing + 360 - currentBearing) < diff) bearing += 360;
|
|
22990
|
+
return bearing;
|
|
22459
22991
|
}
|
|
22460
|
-
|
|
22461
|
-
|
|
22462
|
-
const cachedSampler = this._elevationSamplerCache.get(key);
|
|
22463
|
-
if (cachedSampler) return cachedSampler;
|
|
22464
|
-
const sourceTile = this.tileManager.getSourceTile(tileID, true);
|
|
22465
|
-
const dem = sourceTile?.dem;
|
|
22466
|
-
if (!sourceTile || !dem) return null;
|
|
22467
|
-
const matrix = this._getDEMTileMatrix(tileID, sourceTile);
|
|
22468
|
-
const demPixelScaleX = matrix[0] * dem.dim;
|
|
22469
|
-
const demPixelScaleY = matrix[5] * dem.dim;
|
|
22470
|
-
const demPixelOffsetX = matrix[12] * dem.dim;
|
|
22471
|
-
const demPixelOffsetY = matrix[13] * dem.dim;
|
|
22472
|
-
const sampler = (x, y, extent) => {
|
|
22473
|
-
const extentScale = extent === 8192 ? 1 : EXTENT / extent;
|
|
22474
|
-
return dem.sampleBilinear(x * extentScale * demPixelScaleX + demPixelOffsetX, y * extentScale * demPixelScaleY + demPixelOffsetY);
|
|
22475
|
-
};
|
|
22476
|
-
this._elevationSamplerCache.set(key, sampler);
|
|
22477
|
-
return sampler;
|
|
22992
|
+
isMoving() {
|
|
22993
|
+
return this._moving;
|
|
22478
22994
|
}
|
|
22479
|
-
|
|
22480
|
-
|
|
22481
|
-
const cachedMatrix = this._demMatrixCache.get(matrixKey);
|
|
22482
|
-
if (cachedMatrix) return cachedMatrix;
|
|
22483
|
-
const maxzoom = this.tileManager.getSource().maxzoom;
|
|
22484
|
-
let dz = tileID.canonical.z - sourceTile.tileID.canonical.z;
|
|
22485
|
-
if (tileID.overscaledZ > tileID.canonical.z) {
|
|
22486
|
-
if (tileID.canonical.z >= maxzoom) dz = tileID.canonical.z - maxzoom;
|
|
22487
|
-
else warnOnce("cannot calculate elevation if elevation maxzoom > source.maxzoom");
|
|
22488
|
-
}
|
|
22489
|
-
const dx = tileID.canonical.x - (tileID.canonical.x >> dz << dz);
|
|
22490
|
-
const dy = tileID.canonical.y - (tileID.canonical.y >> dz << dz);
|
|
22491
|
-
const demMatrix = fromScaling(/* @__PURE__ */ new Float64Array(16), [
|
|
22492
|
-
1 / (EXTENT << dz),
|
|
22493
|
-
1 / (EXTENT << dz),
|
|
22494
|
-
0
|
|
22495
|
-
]);
|
|
22496
|
-
translate(demMatrix, demMatrix, [
|
|
22497
|
-
dx * EXTENT,
|
|
22498
|
-
dy * EXTENT,
|
|
22499
|
-
0
|
|
22500
|
-
]);
|
|
22501
|
-
this._demMatrixCache.set(matrixKey, demMatrix);
|
|
22502
|
-
return demMatrix;
|
|
22995
|
+
isZooming() {
|
|
22996
|
+
return this._zooming;
|
|
22503
22997
|
}
|
|
22998
|
+
isRotating() {
|
|
22999
|
+
return this._rotating;
|
|
23000
|
+
}
|
|
23001
|
+
};
|
|
23002
|
+
//#endregion
|
|
23003
|
+
//#region src/ui/control/attribution_control.ts
|
|
23004
|
+
const defaultAttributionControlOptions = {
|
|
23005
|
+
compact: true,
|
|
23006
|
+
customAttribution: "<a href=\"https://maplibre.org/\" target=\"_blank\">MapLibre</a>"
|
|
23007
|
+
};
|
|
23008
|
+
/**
|
|
23009
|
+
* An `AttributionControl` control presents the map's attribution information. By default, the attribution control is expanded (regardless of map width).
|
|
23010
|
+
* @group Markers and Controls
|
|
23011
|
+
* @example
|
|
23012
|
+
* ```ts
|
|
23013
|
+
* let map = new Map({attributionControl: false})
|
|
23014
|
+
* .addControl(new AttributionControl({
|
|
23015
|
+
* compact: true
|
|
23016
|
+
* }));
|
|
23017
|
+
* ```
|
|
23018
|
+
* @see [Change the default position for attribution](https://maplibre.org/maplibre-gl-js/docs/examples/change-the-default-position-for-attribution/)
|
|
23019
|
+
*/
|
|
23020
|
+
var AttributionControl = class {
|
|
22504
23021
|
/**
|
|
22505
|
-
*
|
|
22506
|
-
* @param tileID - the tile to get the terrain for
|
|
22507
|
-
* @returns the terrain data to use in the program
|
|
23022
|
+
* @param options - the attribution options
|
|
22508
23023
|
*/
|
|
22509
|
-
|
|
22510
|
-
|
|
22511
|
-
|
|
22512
|
-
|
|
22513
|
-
|
|
22514
|
-
|
|
22515
|
-
|
|
22516
|
-
|
|
22517
|
-
|
|
22518
|
-
|
|
22519
|
-
|
|
22520
|
-
|
|
22521
|
-
|
|
22522
|
-
|
|
22523
|
-
|
|
22524
|
-
|
|
22525
|
-
|
|
22526
|
-
|
|
22527
|
-
|
|
22528
|
-
|
|
22529
|
-
|
|
22530
|
-
|
|
22531
|
-
|
|
22532
|
-
|
|
22533
|
-
|
|
22534
|
-
|
|
22535
|
-
|
|
22536
|
-
|
|
22537
|
-
|
|
22538
|
-
|
|
22539
|
-
|
|
22540
|
-
return {
|
|
22541
|
-
"u_depth": 2,
|
|
22542
|
-
"u_terrain": 3,
|
|
22543
|
-
"u_terrain_dim": sourceTile?.dem?.dim || 1,
|
|
22544
|
-
"u_terrain_matrix": terrainMatrix,
|
|
22545
|
-
"u_terrain_unpack": sourceTile?.dem?.getUnpackVector() || this._emptyDemUnpack,
|
|
22546
|
-
"u_terrain_exaggeration": this.exaggeration,
|
|
22547
|
-
texture: (sourceTile?.demTexture || this._emptyDemTexture).texture,
|
|
22548
|
-
depthTexture: (this._fboDepthTexture || this._emptyDepthTexture).texture,
|
|
22549
|
-
tile: sourceTile
|
|
23024
|
+
constructor(options = defaultAttributionControlOptions) {
|
|
23025
|
+
this._toggleAttribution = () => {
|
|
23026
|
+
if (this._container.classList.contains("maplibregl-compact")) {
|
|
23027
|
+
if (this._container.classList.contains("maplibregl-compact-show")) {
|
|
23028
|
+
this._container.setAttribute("open", "");
|
|
23029
|
+
this._container.classList.remove("maplibregl-compact-show");
|
|
23030
|
+
} else {
|
|
23031
|
+
this._container.classList.add("maplibregl-compact-show");
|
|
23032
|
+
this._container.removeAttribute("open");
|
|
23033
|
+
}
|
|
23034
|
+
}
|
|
23035
|
+
};
|
|
23036
|
+
this._updateData = (e) => {
|
|
23037
|
+
if (e && (e.type === "terrain" || e.dataType === "style" || e.dataType === "source" && (e.sourceDataType === "metadata" || e.sourceDataType === "visibility"))) this._updateAttributions();
|
|
23038
|
+
};
|
|
23039
|
+
this._updateCompact = () => {
|
|
23040
|
+
if (this._map.getCanvasContainer().offsetWidth <= 640 || this._compact) {
|
|
23041
|
+
if (this._compact === false) this._container.setAttribute("open", "");
|
|
23042
|
+
else if (!this._container.classList.contains("maplibregl-compact") && !this._container.classList.contains("maplibregl-attrib-empty")) {
|
|
23043
|
+
this._container.setAttribute("open", "");
|
|
23044
|
+
this._container.classList.add("maplibregl-compact", "maplibregl-compact-show");
|
|
23045
|
+
}
|
|
23046
|
+
} else {
|
|
23047
|
+
this._container.setAttribute("open", "");
|
|
23048
|
+
if (this._container.classList.contains("maplibregl-compact")) this._container.classList.remove("maplibregl-compact", "maplibregl-compact-show");
|
|
23049
|
+
}
|
|
23050
|
+
};
|
|
23051
|
+
this._updateCompactMinimize = () => {
|
|
23052
|
+
if (this._container.classList.contains("maplibregl-compact")) {
|
|
23053
|
+
if (this._container.classList.contains("maplibregl-compact-show")) this._container.classList.remove("maplibregl-compact-show");
|
|
23054
|
+
}
|
|
22550
23055
|
};
|
|
23056
|
+
this.options = options;
|
|
22551
23057
|
}
|
|
22552
|
-
|
|
22553
|
-
|
|
22554
|
-
|
|
22555
|
-
|
|
22556
|
-
|
|
22557
|
-
|
|
22558
|
-
|
|
22559
|
-
|
|
22560
|
-
|
|
22561
|
-
|
|
22562
|
-
|
|
22563
|
-
|
|
22564
|
-
|
|
22565
|
-
|
|
22566
|
-
|
|
22567
|
-
|
|
22568
|
-
|
|
22569
|
-
|
|
22570
|
-
|
|
22571
|
-
|
|
22572
|
-
|
|
22573
|
-
|
|
22574
|
-
|
|
22575
|
-
|
|
23058
|
+
getDefaultPosition() {
|
|
23059
|
+
return "bottom-right";
|
|
23060
|
+
}
|
|
23061
|
+
/** {@inheritDoc IControl.onAdd} */
|
|
23062
|
+
onAdd(map) {
|
|
23063
|
+
this._map = map;
|
|
23064
|
+
this._compact = this.options.compact;
|
|
23065
|
+
this._container = DOM.create("details", "maplibregl-ctrl maplibregl-ctrl-attrib");
|
|
23066
|
+
this._compactButton = DOM.create("summary", "maplibregl-ctrl-attrib-button", this._container);
|
|
23067
|
+
this._compactButton.addEventListener("click", this._toggleAttribution);
|
|
23068
|
+
this._setElementTitle(this._compactButton, "ToggleAttribution");
|
|
23069
|
+
this._innerContainer = DOM.create("div", "maplibregl-ctrl-attrib-inner", this._container);
|
|
23070
|
+
this._updateAttributions();
|
|
23071
|
+
this._updateCompact();
|
|
23072
|
+
this._map.on("styledata", this._updateData);
|
|
23073
|
+
this._map.on("sourcedata", this._updateData);
|
|
23074
|
+
this._map.on("terrain", this._updateData);
|
|
23075
|
+
this._map.on("resize", this._updateCompact);
|
|
23076
|
+
this._map.on("drag", this._updateCompactMinimize);
|
|
23077
|
+
return this._container;
|
|
23078
|
+
}
|
|
23079
|
+
/** {@inheritDoc IControl.onRemove} */
|
|
23080
|
+
onRemove() {
|
|
23081
|
+
this._container.remove();
|
|
23082
|
+
this._map.off("styledata", this._updateData);
|
|
23083
|
+
this._map.off("sourcedata", this._updateData);
|
|
23084
|
+
this._map.off("terrain", this._updateData);
|
|
23085
|
+
this._map.off("resize", this._updateCompact);
|
|
23086
|
+
this._map.off("drag", this._updateCompactMinimize);
|
|
23087
|
+
this._map = void 0;
|
|
23088
|
+
this._compact = void 0;
|
|
23089
|
+
this._attribHTML = void 0;
|
|
23090
|
+
}
|
|
23091
|
+
_setElementTitle(element, title) {
|
|
23092
|
+
const str = this._map._getUIString(`AttributionControl.${title}`);
|
|
23093
|
+
element.title = str;
|
|
23094
|
+
element.setAttribute("aria-label", str);
|
|
23095
|
+
}
|
|
23096
|
+
_updateAttributions() {
|
|
23097
|
+
if (!this._map.style) return;
|
|
23098
|
+
let attributions = [];
|
|
23099
|
+
if (this.options.customAttribution) {
|
|
23100
|
+
if (Array.isArray(this.options.customAttribution)) attributions = attributions.concat(this.options.customAttribution.map((attribution) => {
|
|
23101
|
+
if (typeof attribution !== "string") return "";
|
|
23102
|
+
return attribution;
|
|
23103
|
+
}));
|
|
23104
|
+
else if (typeof this.options.customAttribution === "string") attributions.push(this.options.customAttribution);
|
|
22576
23105
|
}
|
|
22577
|
-
if (
|
|
22578
|
-
|
|
22579
|
-
|
|
22580
|
-
|
|
22581
|
-
data: null
|
|
22582
|
-
}, painter.context.gl.RGBA, { premultiply: false });
|
|
22583
|
-
this._fboDepthTexture.bind(painter.context.gl.NEAREST, painter.context.gl.CLAMP_TO_EDGE);
|
|
23106
|
+
if (this._map.style.stylesheet) {
|
|
23107
|
+
const stylesheet = this._map.style.stylesheet;
|
|
23108
|
+
this.styleOwner = stylesheet.owner;
|
|
23109
|
+
this.styleId = stylesheet.id;
|
|
22584
23110
|
}
|
|
22585
|
-
|
|
22586
|
-
|
|
22587
|
-
|
|
23111
|
+
const tileManagers = this._map.style.tileManagers;
|
|
23112
|
+
for (const id in tileManagers) {
|
|
23113
|
+
const tileManager = tileManagers[id];
|
|
23114
|
+
if (tileManager.used || tileManager.usedForTerrain) {
|
|
23115
|
+
const source = tileManager.getSource();
|
|
23116
|
+
if (source.attribution && !attributions.includes(source.attribution)) attributions.push(source.attribution);
|
|
23117
|
+
}
|
|
22588
23118
|
}
|
|
22589
|
-
|
|
22590
|
-
|
|
22591
|
-
|
|
22592
|
-
|
|
22593
|
-
|
|
22594
|
-
|
|
22595
|
-
|
|
22596
|
-
|
|
22597
|
-
|
|
22598
|
-
|
|
22599
|
-
|
|
22600
|
-
|
|
22601
|
-
|
|
22602
|
-
|
|
22603
|
-
|
|
22604
|
-
if (this._coordsTexture) return this._coordsTexture;
|
|
22605
|
-
const data = new Uint8Array(this._coordsTextureSize * this._coordsTextureSize * 4);
|
|
22606
|
-
for (let y = 0, i = 0; y < this._coordsTextureSize; y++) for (let x = 0; x < this._coordsTextureSize; x++, i += 4) {
|
|
22607
|
-
data[i + 0] = x & 255;
|
|
22608
|
-
data[i + 1] = y & 255;
|
|
22609
|
-
data[i + 2] = x >> 8 << 4 | y >> 8;
|
|
22610
|
-
data[i + 3] = 0;
|
|
22611
|
-
}
|
|
22612
|
-
const image = new RGBAImage({
|
|
22613
|
-
width: this._coordsTextureSize,
|
|
22614
|
-
height: this._coordsTextureSize
|
|
22615
|
-
}, new Uint8Array(data.buffer));
|
|
22616
|
-
const texture = new Texture(context, image, context.gl.RGBA, { premultiply: false });
|
|
22617
|
-
texture.bind(context.gl.NEAREST, context.gl.CLAMP_TO_EDGE);
|
|
22618
|
-
this._coordsTexture = texture;
|
|
22619
|
-
return texture;
|
|
22620
|
-
}
|
|
22621
|
-
/**
|
|
22622
|
-
* Reads a pixel from the coords-framebuffer and translate this to mercator, or null, if the pixel doesn't lie on the terrain's surface (but the sky instead).
|
|
22623
|
-
* @param p - Screen-Coordinate
|
|
22624
|
-
* @returns Mercator coordinate for a screen pixel, or null, if the pixel is not covered by terrain (is in the sky).
|
|
22625
|
-
*/
|
|
22626
|
-
pointCoordinate(p) {
|
|
22627
|
-
this.painter.maybeDrawDepth(true);
|
|
22628
|
-
this.painter.maybeDrawCoords();
|
|
22629
|
-
const rgba = /* @__PURE__ */ new Uint8Array(4);
|
|
22630
|
-
const context = this.painter.context, gl = context.gl;
|
|
22631
|
-
const px = Math.round(p.x * this.painter.pixelRatio / devicePixelRatio);
|
|
22632
|
-
const py = Math.round(p.y * this.painter.pixelRatio / devicePixelRatio);
|
|
22633
|
-
const fbHeight = Math.round(this.painter.height / devicePixelRatio);
|
|
22634
|
-
context.bindFramebuffer.set(this.getFramebuffer("coords").framebuffer);
|
|
22635
|
-
gl.readPixels(px, fbHeight - py - 1, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, rgba);
|
|
22636
|
-
context.bindFramebuffer.set(null);
|
|
22637
|
-
const x = rgba[0] + (rgba[2] >> 4 << 8);
|
|
22638
|
-
const y = rgba[1] + ((rgba[2] & 15) << 8);
|
|
22639
|
-
const tileID = this.coordsIndex[255 - rgba[3]];
|
|
22640
|
-
const tile = tileID && this.tileManager.getTileByID(tileID);
|
|
22641
|
-
if (!tile) return null;
|
|
22642
|
-
const coordsSize = this._coordsTextureSize;
|
|
22643
|
-
const worldSize = (1 << tile.tileID.canonical.z) * coordsSize;
|
|
22644
|
-
return new MercatorCoordinate((tile.tileID.canonical.x * coordsSize + x) / worldSize + tile.tileID.wrap, (tile.tileID.canonical.y * coordsSize + y) / worldSize, this.getElevation(tile.tileID, x, y, coordsSize));
|
|
23119
|
+
attributions = attributions.filter((e) => String(e).trim());
|
|
23120
|
+
attributions.sort((a, b) => a.length - b.length);
|
|
23121
|
+
attributions = attributions.filter((attrib, i) => {
|
|
23122
|
+
for (let j = i + 1; j < attributions.length; j++) if (attributions[j].includes(attrib)) return false;
|
|
23123
|
+
return true;
|
|
23124
|
+
});
|
|
23125
|
+
const attribHTML = attributions.join(" | ");
|
|
23126
|
+
if (attribHTML === this._attribHTML) return;
|
|
23127
|
+
this._attribHTML = attribHTML;
|
|
23128
|
+
if (attributions.length) {
|
|
23129
|
+
this._innerContainer.innerHTML = DOM.sanitize(attribHTML);
|
|
23130
|
+
this._container.classList.remove("maplibregl-attrib-empty");
|
|
23131
|
+
} else this._container.classList.add("maplibregl-attrib-empty");
|
|
23132
|
+
this._updateCompact();
|
|
23133
|
+
this._editLink = null;
|
|
22645
23134
|
}
|
|
23135
|
+
};
|
|
23136
|
+
//#endregion
|
|
23137
|
+
//#region src/ui/control/logo_control.ts
|
|
23138
|
+
/**
|
|
23139
|
+
* A `LogoControl` is a control that adds the watermark.
|
|
23140
|
+
*
|
|
23141
|
+
* @group Markers and Controls
|
|
23142
|
+
*
|
|
23143
|
+
* @example
|
|
23144
|
+
* ```ts
|
|
23145
|
+
* map.addControl(new LogoControl({compact: false}));
|
|
23146
|
+
* ```
|
|
23147
|
+
**/
|
|
23148
|
+
var LogoControl = class {
|
|
22646
23149
|
/**
|
|
22647
|
-
*
|
|
22648
|
-
* @param p - Screen coordinate
|
|
22649
|
-
* @returns depth value in clip space (between 0 and 1)
|
|
23150
|
+
* @param options - the control's options
|
|
22650
23151
|
*/
|
|
22651
|
-
|
|
22652
|
-
|
|
22653
|
-
|
|
22654
|
-
|
|
22655
|
-
|
|
22656
|
-
|
|
22657
|
-
|
|
23152
|
+
constructor(options = {}) {
|
|
23153
|
+
this._updateCompact = () => {
|
|
23154
|
+
const containerChildren = this._container.children;
|
|
23155
|
+
if (containerChildren.length) {
|
|
23156
|
+
const anchor = containerChildren[0];
|
|
23157
|
+
if (this._map.getCanvasContainer().offsetWidth <= 640 || this._compact) {
|
|
23158
|
+
if (this._compact !== false) anchor.classList.add("maplibregl-compact");
|
|
23159
|
+
} else anchor.classList.remove("maplibregl-compact");
|
|
23160
|
+
}
|
|
23161
|
+
};
|
|
23162
|
+
this.options = options;
|
|
22658
23163
|
}
|
|
22659
|
-
|
|
22660
|
-
|
|
22661
|
-
* @returns the created regular mesh
|
|
22662
|
-
*/
|
|
22663
|
-
getTerrainMesh(tileId) {
|
|
22664
|
-
const globeEnabled = this.painter.style.projection?.transitionState > 0;
|
|
22665
|
-
const northPole = globeEnabled && tileId.canonical.y === 0;
|
|
22666
|
-
const southPole = globeEnabled && tileId.canonical.y === (1 << tileId.canonical.z) - 1;
|
|
22667
|
-
const key = `m_${northPole ? "n" : ""}_${southPole ? "s" : ""}`;
|
|
22668
|
-
if (this._meshCache[key]) return this._meshCache[key];
|
|
22669
|
-
const context = this.painter.context;
|
|
22670
|
-
const vertexArray = new Pos3dArray();
|
|
22671
|
-
const indexArray = new TriangleIndexArray();
|
|
22672
|
-
const meshSize = this.meshSize;
|
|
22673
|
-
const delta = EXTENT / meshSize;
|
|
22674
|
-
const meshSize2 = meshSize * meshSize;
|
|
22675
|
-
for (let y = 0; y <= meshSize; y++) for (let x = 0; x <= meshSize; x++) vertexArray.emplaceBack(x * delta, y * delta, 0);
|
|
22676
|
-
for (let y = 0; y < meshSize2; y += meshSize + 1) for (let x = 0; x < meshSize; x++) {
|
|
22677
|
-
indexArray.emplaceBack(x + y, meshSize + x + y + 1, meshSize + x + y + 2);
|
|
22678
|
-
indexArray.emplaceBack(x + y, meshSize + x + y + 2, x + y + 1);
|
|
22679
|
-
}
|
|
22680
|
-
if (this._terrainSkirtLength !== "none") this._buildSkirts(vertexArray, indexArray, meshSize, delta, northPole, southPole);
|
|
22681
|
-
const mesh = new Mesh(context.createVertexBuffer(vertexArray, pos3dAttributes.members), context.createIndexBuffer(indexArray), SegmentVector.simpleSegment(0, 0, vertexArray.length, indexArray.length));
|
|
22682
|
-
this._meshCache[key] = mesh;
|
|
22683
|
-
return mesh;
|
|
23164
|
+
getDefaultPosition() {
|
|
23165
|
+
return "bottom-left";
|
|
22684
23166
|
}
|
|
22685
|
-
/**
|
|
22686
|
-
|
|
22687
|
-
|
|
22688
|
-
|
|
22689
|
-
|
|
22690
|
-
|
|
22691
|
-
|
|
22692
|
-
|
|
23167
|
+
/** {@inheritDoc IControl.onAdd} */
|
|
23168
|
+
onAdd(map) {
|
|
23169
|
+
this._map = map;
|
|
23170
|
+
this._compact = this.options?.compact;
|
|
23171
|
+
this._container = DOM.create("div", "maplibregl-ctrl");
|
|
23172
|
+
const anchor = DOM.create("a", "maplibregl-ctrl-logo");
|
|
23173
|
+
anchor.target = "_blank";
|
|
23174
|
+
anchor.rel = "noopener nofollow";
|
|
23175
|
+
anchor.href = "https://maplibre.org/";
|
|
23176
|
+
anchor.setAttribute("aria-label", this._map._getUIString("LogoControl.Title"));
|
|
23177
|
+
anchor.setAttribute("rel", "noopener nofollow");
|
|
23178
|
+
this._container.appendChild(anchor);
|
|
23179
|
+
this._container.style.display = "block";
|
|
23180
|
+
this._map.on("resize", this._updateCompact);
|
|
23181
|
+
this._updateCompact();
|
|
23182
|
+
return this._container;
|
|
22693
23183
|
}
|
|
22694
|
-
|
|
22695
|
-
|
|
22696
|
-
|
|
22697
|
-
|
|
23184
|
+
/** {@inheritDoc IControl.onRemove} */
|
|
23185
|
+
onRemove() {
|
|
23186
|
+
this._container.remove();
|
|
23187
|
+
this._map.off("resize", this._updateCompact);
|
|
23188
|
+
this._map = void 0;
|
|
23189
|
+
this._compact = void 0;
|
|
22698
23190
|
}
|
|
22699
|
-
|
|
22700
|
-
|
|
22701
|
-
|
|
22702
|
-
|
|
22703
|
-
|
|
22704
|
-
|
|
22705
|
-
|
|
22706
|
-
|
|
22707
|
-
|
|
22708
|
-
const tile = this.tileManager.getSourceTile(tileID, true);
|
|
22709
|
-
const minMax = {
|
|
22710
|
-
minElevation: null,
|
|
22711
|
-
maxElevation: null
|
|
22712
|
-
};
|
|
22713
|
-
if (tile?.dem) {
|
|
22714
|
-
minMax.minElevation = tile.dem.min * this.exaggeration;
|
|
22715
|
-
minMax.maxElevation = tile.dem.max * this.exaggeration;
|
|
22716
|
-
}
|
|
22717
|
-
return minMax;
|
|
23191
|
+
};
|
|
23192
|
+
//#endregion
|
|
23193
|
+
//#region src/util/task_queue.ts
|
|
23194
|
+
var TaskQueue = class {
|
|
23195
|
+
constructor() {
|
|
23196
|
+
this._queue = [];
|
|
23197
|
+
this._id = 0;
|
|
23198
|
+
this._cleared = false;
|
|
23199
|
+
this._currentlyRunning = false;
|
|
22718
23200
|
}
|
|
22719
|
-
|
|
22720
|
-
const
|
|
22721
|
-
|
|
22722
|
-
|
|
22723
|
-
|
|
22724
|
-
|
|
22725
|
-
|
|
22726
|
-
|
|
22727
|
-
mercatorX,
|
|
22728
|
-
mercatorY
|
|
22729
|
-
};
|
|
23201
|
+
add(callback) {
|
|
23202
|
+
const id = ++this._id;
|
|
23203
|
+
this._queue.push({
|
|
23204
|
+
callback,
|
|
23205
|
+
id,
|
|
23206
|
+
cancelled: false
|
|
23207
|
+
});
|
|
23208
|
+
return id;
|
|
22730
23209
|
}
|
|
22731
|
-
|
|
22732
|
-
|
|
22733
|
-
|
|
22734
|
-
|
|
22735
|
-
|
|
22736
|
-
|
|
22737
|
-
const offsetBottom = offsetTop + (meshSize + 1);
|
|
22738
|
-
const offsetBottomEdge = (meshSize + 1) * meshSize;
|
|
22739
|
-
const northY = northPole ? NORTH_POLE_Y : 0;
|
|
22740
|
-
const northZ = northPole ? 0 : 1;
|
|
22741
|
-
const southY = southPole ? SOUTH_POLE_Y : EXTENT;
|
|
22742
|
-
const southZ = southPole ? 0 : 1;
|
|
22743
|
-
for (let x = 0; x <= meshSize; x++) vertexArray.emplaceBack(x * delta, northY, northZ);
|
|
22744
|
-
for (let x = 0; x <= meshSize; x++) vertexArray.emplaceBack(x * delta, southY, southZ);
|
|
22745
|
-
for (let x = 0; x < meshSize; x++) {
|
|
22746
|
-
indexArray.emplaceBack(offsetBottomEdge + x, offsetBottom + x, offsetBottom + x + 1);
|
|
22747
|
-
indexArray.emplaceBack(offsetBottomEdge + x, offsetBottom + x + 1, offsetBottomEdge + x + 1);
|
|
22748
|
-
indexArray.emplaceBack(offsetTopEdge + x, offsetTop + x + 1, offsetTop + x);
|
|
22749
|
-
indexArray.emplaceBack(offsetTopEdge + x, offsetTopEdge + x + 1, offsetTop + x + 1);
|
|
23210
|
+
remove(id) {
|
|
23211
|
+
const running = this._currentlyRunning;
|
|
23212
|
+
const queue = running ? this._queue.concat(running) : this._queue;
|
|
23213
|
+
for (const task of queue) if (task.id === id) {
|
|
23214
|
+
task.cancelled = true;
|
|
23215
|
+
return;
|
|
22750
23216
|
}
|
|
22751
|
-
|
|
22752
|
-
|
|
22753
|
-
|
|
22754
|
-
|
|
22755
|
-
|
|
22756
|
-
|
|
22757
|
-
|
|
22758
|
-
|
|
23217
|
+
}
|
|
23218
|
+
run(timeStamp = 0) {
|
|
23219
|
+
if (this._currentlyRunning) throw new Error("Attempting to run(), but is already running.");
|
|
23220
|
+
const queue = this._currentlyRunning = this._queue;
|
|
23221
|
+
this._queue = [];
|
|
23222
|
+
for (const task of queue) {
|
|
23223
|
+
if (task.cancelled) continue;
|
|
23224
|
+
task.callback(timeStamp);
|
|
23225
|
+
if (this._cleared) break;
|
|
22759
23226
|
}
|
|
23227
|
+
this._cleared = false;
|
|
23228
|
+
this._currentlyRunning = false;
|
|
23229
|
+
}
|
|
23230
|
+
clear() {
|
|
23231
|
+
if (this._currentlyRunning) this._cleared = true;
|
|
23232
|
+
this._queue = [];
|
|
22760
23233
|
}
|
|
22761
23234
|
};
|
|
22762
23235
|
//#endregion
|
|
@@ -23069,8 +23542,12 @@ var Map$1 = class extends Evented {
|
|
|
23069
23542
|
style: null,
|
|
23070
23543
|
images: null
|
|
23071
23544
|
};
|
|
23072
|
-
|
|
23073
|
-
|
|
23545
|
+
try {
|
|
23546
|
+
this._setupPainter();
|
|
23547
|
+
} catch (error) {
|
|
23548
|
+
this.fire(new ErrorEvent(error));
|
|
23549
|
+
return;
|
|
23550
|
+
}
|
|
23074
23551
|
this.resize();
|
|
23075
23552
|
this._update();
|
|
23076
23553
|
this._resizeInternal();
|
|
@@ -23135,13 +23612,17 @@ var Map$1 = class extends Evented {
|
|
|
23135
23612
|
this.cancelPendingTileRequestsWhileZooming = resolvedOptions.cancelPendingTileRequestsWhileZooming === true;
|
|
23136
23613
|
this.setAnisotropicFilterPitch(resolvedOptions.anisotropicFilterPitch);
|
|
23137
23614
|
if (resolvedOptions.reduceMotion !== void 0) browser.prefersReducedMotion = resolvedOptions.reduceMotion;
|
|
23138
|
-
this._imageQueueHandle = ImageRequest.addThrottleControl(() => this.isMoving());
|
|
23139
23615
|
this._requestManager = new RequestManager(resolvedOptions.transformRequest);
|
|
23140
23616
|
this._container = this._resolveContainer(resolvedOptions.container);
|
|
23141
23617
|
if (resolvedOptions.maxBounds) this.setMaxBounds(resolvedOptions.maxBounds);
|
|
23142
23618
|
this._setupContainer();
|
|
23143
|
-
|
|
23144
|
-
|
|
23619
|
+
try {
|
|
23620
|
+
this._setupPainter();
|
|
23621
|
+
} catch (error) {
|
|
23622
|
+
this._cleanupContainer();
|
|
23623
|
+
throw error;
|
|
23624
|
+
}
|
|
23625
|
+
this._imageQueueHandle = ImageRequest.addThrottleControl(() => this.isMoving());
|
|
23145
23626
|
this.on("move", () => this._update(false));
|
|
23146
23627
|
this.on("moveend", () => this._update(false));
|
|
23147
23628
|
this.on("zoom", () => this._update(true));
|
|
@@ -24116,14 +24597,17 @@ var Map$1 = class extends Evented {
|
|
|
24116
24597
|
/**
|
|
24117
24598
|
* Returns the map's minimum allowable zoom level.
|
|
24118
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.
|
|
24119
24602
|
* @returns minZoom
|
|
24120
24603
|
* @example
|
|
24121
24604
|
* ```ts
|
|
24122
24605
|
* let minZoom = map.getMinZoom();
|
|
24123
24606
|
* ```
|
|
24124
24607
|
*/
|
|
24125
|
-
getMinZoom() {
|
|
24126
|
-
|
|
24608
|
+
getMinZoom(constrained = false) {
|
|
24609
|
+
const transform = this._camera.transform;
|
|
24610
|
+
return constrained ? transform.applyConstrain(transform.center, transform.minZoom).zoom : transform.minZoom;
|
|
24127
24611
|
}
|
|
24128
24612
|
/**
|
|
24129
24613
|
* Sets or clears the map's maximum zoom level.
|
|
@@ -24918,10 +25402,11 @@ var Map$1 = class extends Evented {
|
|
|
24918
25402
|
this.painter.renderToTexture = new RenderToTexture(this.painter, this.terrain);
|
|
24919
25403
|
this._camera.terrain = this.terrain;
|
|
24920
25404
|
this._camera.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this._camera.transform.center, this._camera.transform.tileZoom));
|
|
24921
|
-
this._camera.transform.setElevation(this.terrain.
|
|
25405
|
+
this._camera.transform.setElevation(this.terrain.getElevationForLngLat(this._camera.transform.center, this._camera.transform));
|
|
24922
25406
|
this._terrainDataCallback = (e) => this._handleTerrainDataEvent(e, options.source);
|
|
24923
25407
|
this.style.on("data", this._terrainDataCallback);
|
|
24924
25408
|
}
|
|
25409
|
+
this.style.triggerSymbolPlacement();
|
|
24925
25410
|
this.fire(new MapTerrainEvent({ terrain: options }));
|
|
24926
25411
|
return this;
|
|
24927
25412
|
}
|
|
@@ -24931,10 +25416,13 @@ var Map$1 = class extends Evented {
|
|
|
24931
25416
|
return;
|
|
24932
25417
|
}
|
|
24933
25418
|
const isTerrainSourceEvent = event.sourceId === terrainSourceId;
|
|
24934
|
-
if (isTerrainSourceEvent)
|
|
25419
|
+
if (isTerrainSourceEvent) {
|
|
25420
|
+
this.terrain.resetElevationCache();
|
|
25421
|
+
this.style.triggerSymbolPlacement();
|
|
25422
|
+
}
|
|
24935
25423
|
if (isTerrainSourceEvent && event.tile && !this._camera.elevationFreeze) {
|
|
24936
25424
|
this._camera.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this._camera.transform.center, this._camera.transform.tileZoom));
|
|
24937
|
-
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));
|
|
24938
25426
|
}
|
|
24939
25427
|
if (!event.tile) return;
|
|
24940
25428
|
if (event.source?.type === "image") {
|
|
@@ -25589,6 +26077,42 @@ var Map$1 = class extends Evented {
|
|
|
25589
26077
|
return this.style.getGlyphsUrl();
|
|
25590
26078
|
}
|
|
25591
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
|
+
/**
|
|
25592
26116
|
* Adds a sprite to the map's style. Fires the `style` event.
|
|
25593
26117
|
*
|
|
25594
26118
|
* @param id - The ID of the sprite to add. Must not conflict with existing sprites.
|
|
@@ -25924,12 +26448,28 @@ var Map$1 = class extends Evented {
|
|
|
25924
26448
|
]) positions[positionName] = DOM.create("div", `maplibregl-ctrl-${positionName} `, controlContainer);
|
|
25925
26449
|
this._container.addEventListener("scroll", this._onMapScroll, false);
|
|
25926
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
|
+
}
|
|
25927
26463
|
_resizeCanvas(width, height, pixelRatio) {
|
|
25928
26464
|
this._canvas.width = Math.floor(pixelRatio * width);
|
|
25929
26465
|
this._canvas.height = Math.floor(pixelRatio * height);
|
|
25930
26466
|
this._canvas.style.width = `${width}px`;
|
|
25931
26467
|
this._canvas.style.height = `${height}px`;
|
|
25932
26468
|
}
|
|
26469
|
+
/**
|
|
26470
|
+
* @internal
|
|
26471
|
+
* Creates the WebGL2 context and the painter. Throws {@link GPUInitializationError} when the context cannot be created.
|
|
26472
|
+
*/
|
|
25933
26473
|
_setupPainter() {
|
|
25934
26474
|
const attributes = {
|
|
25935
26475
|
...this._canvasContextAttributes,
|
|
@@ -25943,10 +26483,7 @@ var Map$1 = class extends Evented {
|
|
|
25943
26483
|
creationEvent = event;
|
|
25944
26484
|
}, { once: true });
|
|
25945
26485
|
const gl = this._canvas.getContext("webgl2", attributes);
|
|
25946
|
-
if (!gl)
|
|
25947
|
-
this.fire(new ErrorEvent(new GPUInitializationError(attributes, creationEvent)));
|
|
25948
|
-
return;
|
|
25949
|
-
}
|
|
26486
|
+
if (!gl) throw new GPUInitializationError(attributes, creationEvent);
|
|
25950
26487
|
this.painter = new Painter(gl, this._camera.transform);
|
|
25951
26488
|
}
|
|
25952
26489
|
/**
|
|
@@ -26044,10 +26581,9 @@ var Map$1 = class extends Evented {
|
|
|
26044
26581
|
this.style._updateSources(this._camera.transform);
|
|
26045
26582
|
}
|
|
26046
26583
|
if (this.terrain) {
|
|
26047
|
-
this.terrain.tileManager.update(this._camera.transform, this.terrain);
|
|
26048
|
-
this.terrain.resetElevationCache();
|
|
26584
|
+
if (this.terrain.tileManager.update(this._camera.transform, this.terrain)) this.terrain.resetElevationCache();
|
|
26049
26585
|
this._camera.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this._camera.transform.center, this._camera.transform.tileZoom));
|
|
26050
|
-
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));
|
|
26051
26587
|
} else {
|
|
26052
26588
|
this._camera.transform.setMinElevationForCurrentTile(0);
|
|
26053
26589
|
if (this.getCenterClampedToGround()) this._camera.transform.setElevation(0);
|
|
@@ -26120,12 +26656,7 @@ var Map$1 = class extends Evented {
|
|
|
26120
26656
|
this._resizeObserver?.disconnect();
|
|
26121
26657
|
const extension = this.painter.context.gl.getExtension("WEBGL_lose_context");
|
|
26122
26658
|
if (extension?.loseContext) extension.loseContext();
|
|
26123
|
-
this.
|
|
26124
|
-
this._canvas.removeEventListener("webglcontextlost", this._contextLost, false);
|
|
26125
|
-
this._canvasContainer.remove();
|
|
26126
|
-
this._controlContainer.remove();
|
|
26127
|
-
this._container.removeEventListener("scroll", this._onMapScroll, false);
|
|
26128
|
-
this._container.classList.remove("maplibregl-map");
|
|
26659
|
+
this._cleanupContainer();
|
|
26129
26660
|
this._removed = true;
|
|
26130
26661
|
this.fire(new MapLibreEvent("remove"));
|
|
26131
26662
|
}
|
|
@@ -26302,7 +26833,7 @@ var NavigationControl = class {
|
|
|
26302
26833
|
this._updateZoomButtons = () => {
|
|
26303
26834
|
const zoom = this._map.getZoom();
|
|
26304
26835
|
const isMax = zoom === this._map.getMaxZoom();
|
|
26305
|
-
const isMin = zoom === this._map.getMinZoom();
|
|
26836
|
+
const isMin = zoom === this._map.getMinZoom(true);
|
|
26306
26837
|
this._zoomInButton.disabled = isMax;
|
|
26307
26838
|
this._zoomOutButton.disabled = isMin;
|
|
26308
26839
|
this._zoomInButton.setAttribute("aria-disabled", isMax.toString());
|
|
@@ -26356,7 +26887,7 @@ var NavigationControl = class {
|
|
|
26356
26887
|
if (this.options.showZoom) {
|
|
26357
26888
|
this._setButtonTitle(this._zoomInButton, "ZoomIn");
|
|
26358
26889
|
this._setButtonTitle(this._zoomOutButton, "ZoomOut");
|
|
26359
|
-
this._map.on("
|
|
26890
|
+
this._map.on("move", this._updateZoomButtons);
|
|
26360
26891
|
this._updateZoomButtons();
|
|
26361
26892
|
}
|
|
26362
26893
|
if (this.options.showCompass) {
|
|
@@ -26372,7 +26903,7 @@ var NavigationControl = class {
|
|
|
26372
26903
|
/** {@inheritDoc IControl.onRemove} */
|
|
26373
26904
|
onRemove() {
|
|
26374
26905
|
this._container.remove();
|
|
26375
|
-
if (this.options.showZoom) this._map.off("
|
|
26906
|
+
if (this.options.showZoom) this._map.off("move", this._updateZoomButtons);
|
|
26376
26907
|
if (this.options.showCompass) {
|
|
26377
26908
|
if (this.options.visualizePitch) this._map.off("pitch", this._rotateCompassArrow);
|
|
26378
26909
|
if (this.options.visualizeRoll) this._map.off("roll", this._rotateCompassArrow);
|