maplibre-gl 2.1.8 → 2.2.0-pre.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/generate-style-spec.ts +2 -0
- package/dist/maplibre-gl-csp-worker.js +1 -1
- package/dist/maplibre-gl-csp-worker.js.map +1 -1
- package/dist/maplibre-gl-csp.js +1 -1
- package/dist/maplibre-gl-csp.js.map +1 -1
- package/dist/maplibre-gl-dev.js +1517 -290
- package/dist/maplibre-gl.css +1 -1
- package/dist/maplibre-gl.d.ts +3476 -3112
- package/dist/maplibre-gl.js +4 -4
- package/dist/maplibre-gl.js.map +1 -1
- package/dist/package.json +1 -1
- package/package.json +15 -15
- package/src/css/maplibre-gl.css +20 -0
- package/src/css/svg/maplibregl-ctrl-terrain.svg +7 -0
- package/src/data/bucket/fill_extrusion_attributes.ts +4 -0
- package/src/data/bucket/fill_extrusion_bucket.ts +28 -4
- package/src/data/dem_data.test.ts +14 -1
- package/src/data/dem_data.ts +13 -0
- package/src/geo/transform.test.ts +56 -1
- package/src/geo/transform.ts +199 -47
- package/src/index.ts +2 -0
- package/src/render/draw_background.ts +6 -6
- package/src/render/draw_circle.ts +6 -2
- package/src/render/draw_collision_debug.ts +5 -1
- package/src/render/draw_debug.ts +5 -5
- package/src/render/draw_fill.ts +5 -2
- package/src/render/draw_fill_extrusion.ts +3 -2
- package/src/render/draw_heatmap.ts +2 -3
- package/src/render/draw_hillshade.ts +8 -7
- package/src/render/draw_line.ts +7 -5
- package/src/render/draw_raster.ts +8 -6
- package/src/render/draw_symbol.test.ts +34 -10
- package/src/render/draw_symbol.ts +23 -12
- package/src/render/draw_terrain.ts +123 -0
- package/src/render/painter.ts +52 -14
- package/src/render/program/hillshade_program.ts +7 -2
- package/src/render/program/line_program.ts +24 -10
- package/src/render/program/program_uniforms.ts +5 -1
- package/src/render/program/terrain_program.ts +83 -0
- package/src/render/program.ts +29 -5
- package/src/render/render_to_texture.test.ts +41 -0
- package/src/render/render_to_texture.ts +154 -0
- package/src/render/terrain.test.ts +53 -0
- package/src/render/terrain.ts +369 -0
- package/src/render/vertex_array_object.ts +21 -4
- package/src/shaders/_prelude.vertex.glsl +76 -0
- package/src/shaders/_prelude.vertex.glsl.g.ts +1 -1
- package/src/shaders/circle.fragment.glsl +2 -1
- package/src/shaders/circle.fragment.glsl.g.ts +1 -1
- package/src/shaders/circle.vertex.glsl +6 -2
- package/src/shaders/circle.vertex.glsl.g.ts +1 -1
- package/src/shaders/collision_box.vertex.glsl +1 -1
- package/src/shaders/collision_box.vertex.glsl.g.ts +1 -1
- package/src/shaders/debug.vertex.glsl +1 -1
- package/src/shaders/debug.vertex.glsl.g.ts +1 -1
- package/src/shaders/fill_extrusion.vertex.glsl +16 -2
- package/src/shaders/fill_extrusion.vertex.glsl.g.ts +1 -1
- package/src/shaders/fill_extrusion_pattern.vertex.glsl +15 -2
- package/src/shaders/fill_extrusion_pattern.vertex.glsl.g.ts +1 -1
- package/src/shaders/line.vertex.glsl +7 -3
- package/src/shaders/line.vertex.glsl.g.ts +1 -1
- package/src/shaders/line_gradient.vertex.glsl +7 -3
- package/src/shaders/line_gradient.vertex.glsl.g.ts +1 -1
- package/src/shaders/line_pattern.vertex.glsl +7 -3
- package/src/shaders/line_pattern.vertex.glsl.g.ts +1 -1
- package/src/shaders/line_sdf.vertex.glsl +7 -4
- package/src/shaders/line_sdf.vertex.glsl.g.ts +1 -1
- package/src/shaders/shaders.ts +11 -1
- package/src/shaders/symbol_icon.vertex.glsl +8 -8
- package/src/shaders/symbol_icon.vertex.glsl.g.ts +1 -1
- package/src/shaders/symbol_sdf.vertex.glsl +8 -5
- package/src/shaders/symbol_sdf.vertex.glsl.g.ts +1 -1
- package/src/shaders/symbol_text_and_icon.vertex.glsl +8 -5
- package/src/shaders/symbol_text_and_icon.vertex.glsl.g.ts +1 -1
- package/src/shaders/terrain.fragment.glsl +7 -0
- package/src/shaders/terrain.fragment.glsl.g.ts +2 -0
- package/src/shaders/terrain.vertex.glsl +12 -0
- package/src/shaders/terrain.vertex.glsl.g.ts +2 -0
- package/src/shaders/terrain_coords.fragment.glsl +11 -0
- package/src/shaders/terrain_coords.fragment.glsl.g.ts +2 -0
- package/src/shaders/terrain_depth.fragment.glsl +15 -0
- package/src/shaders/terrain_depth.fragment.glsl.g.ts +2 -0
- package/src/source/canvas_source.test.ts +1 -1
- package/src/source/geojson_source.test.ts +25 -0
- package/src/source/geojson_source.ts +1 -8
- package/src/source/geojson_worker_source.test.ts +19 -23
- package/src/source/geojson_worker_source.ts +19 -70
- package/src/source/raster_dem_tile_source.ts +4 -3
- package/src/source/raster_dem_tile_worker_source.ts +0 -1
- package/src/source/source_cache.test.ts +83 -0
- package/src/source/source_cache.ts +72 -11
- package/src/source/terrain_source_cache.test.ts +89 -0
- package/src/source/terrain_source_cache.ts +201 -0
- package/src/source/tile.ts +15 -0
- package/src/source/tile_id.ts +9 -0
- package/src/style/pauseable_placement.ts +3 -1
- package/src/style/style.test.ts +16 -0
- package/src/style/style.ts +57 -3
- package/src/style/validate_style.ts +2 -0
- package/src/style-spec/CHANGELOG.md +6 -0
- package/src/style-spec/error/validation_error.ts +1 -1
- package/src/style-spec/package.json +2 -2
- package/src/style-spec/reference/v8.json +42 -0
- package/src/style-spec/types.g.ts +7 -0
- package/src/style-spec/validate/validate.ts +2 -0
- package/src/style-spec/validate/validate_terrain.test.ts +46 -0
- package/src/style-spec/validate/validate_terrain.ts +41 -0
- package/src/style-spec/validate_style.min.ts +2 -0
- package/src/style-spec/validate_style.ts +1 -0
- package/src/symbol/collision_index.ts +28 -12
- package/src/symbol/placement.ts +24 -9
- package/src/symbol/projection.ts +42 -27
- package/src/ui/camera.ts +2 -0
- package/src/ui/control/terrain_control.ts +77 -0
- package/src/ui/default_locale.ts +3 -2
- package/src/ui/events.ts +18 -3
- package/src/ui/handler_manager.ts +33 -3
- package/src/ui/map.ts +36 -6
- package/src/ui/marker.test.ts +21 -0
- package/src/ui/marker.ts +14 -0
- package/src/util/primitives.ts +14 -11
package/src/symbol/projection.ts
CHANGED
|
@@ -101,9 +101,15 @@ function getGlCoordMatrix(posMatrix: mat4,
|
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
function project(point: Point, matrix: mat4) {
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
function project(point: Point, matrix: mat4, getElevation: (x: number, y: number) => number) {
|
|
105
|
+
let pos;
|
|
106
|
+
if (getElevation) { // slow because of handle z-index
|
|
107
|
+
pos = [point.x, point.y, getElevation(point.x, point.y), 1] as vec4;
|
|
108
|
+
vec4.transformMat4(pos, pos, matrix);
|
|
109
|
+
} else { // fast because of ignore z-index
|
|
110
|
+
pos = [point.x, point.y, 0, 1] as vec4;
|
|
111
|
+
xyTransformMat4(pos, pos, matrix);
|
|
112
|
+
}
|
|
107
113
|
const w = pos[3];
|
|
108
114
|
return {
|
|
109
115
|
point: new Point(pos[0] / w, pos[1] / w),
|
|
@@ -139,7 +145,8 @@ function updateLineLabels(bucket: SymbolBucket,
|
|
|
139
145
|
glCoordMatrix: mat4,
|
|
140
146
|
pitchWithMap: boolean,
|
|
141
147
|
keepUpright: boolean,
|
|
142
|
-
rotateToLine: boolean
|
|
148
|
+
rotateToLine: boolean,
|
|
149
|
+
getElevation: (x: number, y: number) => number) {
|
|
143
150
|
|
|
144
151
|
const sizeData = isText ? bucket.textSizeData : bucket.iconSizeData;
|
|
145
152
|
const partiallyEvaluatedSize = symbolSize.evaluateSizeForZoom(sizeData, painter.transform.zoom);
|
|
@@ -171,8 +178,14 @@ function updateLineLabels(bucket: SymbolBucket,
|
|
|
171
178
|
// Awkward... but we're counting on the paired "vertical" symbol coming immediately after its horizontal counterpart
|
|
172
179
|
useVertical = false;
|
|
173
180
|
|
|
174
|
-
|
|
175
|
-
|
|
181
|
+
let anchorPos;
|
|
182
|
+
if (getElevation) { // slow because of handle z-index
|
|
183
|
+
anchorPos = [symbol.anchorX, symbol.anchorY, getElevation(symbol.anchorX, symbol.anchorY), 1] as vec4;
|
|
184
|
+
vec4.transformMat4(anchorPos, anchorPos, posMatrix);
|
|
185
|
+
} else { // fast because of ignore z-index
|
|
186
|
+
anchorPos = [symbol.anchorX, symbol.anchorY, 0, 1] as vec4;
|
|
187
|
+
xyTransformMat4(anchorPos, anchorPos, posMatrix);
|
|
188
|
+
}
|
|
176
189
|
|
|
177
190
|
// Don't bother calculating the correct point for invisible labels.
|
|
178
191
|
if (!isVisible(anchorPos, clippingBuffer)) {
|
|
@@ -187,18 +200,18 @@ function updateLineLabels(bucket: SymbolBucket,
|
|
|
187
200
|
const pitchScaledFontSize = pitchWithMap ? fontSize / perspectiveRatio : fontSize * perspectiveRatio;
|
|
188
201
|
|
|
189
202
|
const tileAnchorPoint = new Point(symbol.anchorX, symbol.anchorY);
|
|
190
|
-
const anchorPoint = project(tileAnchorPoint, labelPlaneMatrix).point;
|
|
203
|
+
const anchorPoint = project(tileAnchorPoint, labelPlaneMatrix, getElevation).point;
|
|
191
204
|
const projectionCache = {};
|
|
192
205
|
|
|
193
206
|
const placeUnflipped: any = placeGlyphsAlongLine(symbol, pitchScaledFontSize, false /*unflipped*/, keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix,
|
|
194
|
-
bucket.glyphOffsetArray, lineVertexArray, dynamicLayoutVertexArray, anchorPoint, tileAnchorPoint, projectionCache, aspectRatio, rotateToLine);
|
|
207
|
+
bucket.glyphOffsetArray, lineVertexArray, dynamicLayoutVertexArray, anchorPoint, tileAnchorPoint, projectionCache, aspectRatio, rotateToLine, getElevation);
|
|
195
208
|
|
|
196
209
|
useVertical = placeUnflipped.useVertical;
|
|
197
210
|
|
|
198
211
|
if (placeUnflipped.notEnoughRoom || useVertical ||
|
|
199
212
|
(placeUnflipped.needsFlipping &&
|
|
200
213
|
(placeGlyphsAlongLine(symbol, pitchScaledFontSize, true /*flipped*/, keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix,
|
|
201
|
-
bucket.glyphOffsetArray, lineVertexArray, dynamicLayoutVertexArray, anchorPoint, tileAnchorPoint, projectionCache, aspectRatio, rotateToLine) as any).notEnoughRoom)) {
|
|
214
|
+
bucket.glyphOffsetArray, lineVertexArray, dynamicLayoutVertexArray, anchorPoint, tileAnchorPoint, projectionCache, aspectRatio, rotateToLine, getElevation) as any).notEnoughRoom)) {
|
|
202
215
|
hideGlyphs(symbol.numGlyphs, dynamicLayoutVertexArray);
|
|
203
216
|
}
|
|
204
217
|
}
|
|
@@ -210,7 +223,7 @@ function updateLineLabels(bucket: SymbolBucket,
|
|
|
210
223
|
}
|
|
211
224
|
}
|
|
212
225
|
|
|
213
|
-
function placeFirstAndLastGlyph(fontScale: number, glyphOffsetArray: GlyphOffsetArray, lineOffsetX: number, lineOffsetY: number, flip: boolean, anchorPoint: Point, tileAnchorPoint: Point, symbol: any, lineVertexArray: SymbolLineVertexArray, labelPlaneMatrix: mat4, projectionCache: any, rotateToLine: boolean) {
|
|
226
|
+
function placeFirstAndLastGlyph(fontScale: number, glyphOffsetArray: GlyphOffsetArray, lineOffsetX: number, lineOffsetY: number, flip: boolean, anchorPoint: Point, tileAnchorPoint: Point, symbol: any, lineVertexArray: SymbolLineVertexArray, labelPlaneMatrix: mat4, projectionCache: any, rotateToLine: boolean, getElevation: (x: number, y: number) => number) {
|
|
214
227
|
const glyphEndIndex = symbol.glyphStartIndex + symbol.numGlyphs;
|
|
215
228
|
const lineStartIndex = symbol.lineStartIndex;
|
|
216
229
|
const lineEndIndex = symbol.lineStartIndex + symbol.lineLength;
|
|
@@ -219,12 +232,12 @@ function placeFirstAndLastGlyph(fontScale: number, glyphOffsetArray: GlyphOffset
|
|
|
219
232
|
const lastGlyphOffset = glyphOffsetArray.getoffsetX(glyphEndIndex - 1);
|
|
220
233
|
|
|
221
234
|
const firstPlacedGlyph = placeGlyphAlongLine(fontScale * firstGlyphOffset, lineOffsetX, lineOffsetY, flip, anchorPoint, tileAnchorPoint, symbol.segment,
|
|
222
|
-
lineStartIndex, lineEndIndex, lineVertexArray, labelPlaneMatrix, projectionCache, rotateToLine);
|
|
235
|
+
lineStartIndex, lineEndIndex, lineVertexArray, labelPlaneMatrix, projectionCache, rotateToLine, getElevation);
|
|
223
236
|
if (!firstPlacedGlyph)
|
|
224
237
|
return null;
|
|
225
238
|
|
|
226
239
|
const lastPlacedGlyph = placeGlyphAlongLine(fontScale * lastGlyphOffset, lineOffsetX, lineOffsetY, flip, anchorPoint, tileAnchorPoint, symbol.segment,
|
|
227
|
-
lineStartIndex, lineEndIndex, lineVertexArray, labelPlaneMatrix, projectionCache, rotateToLine);
|
|
240
|
+
lineStartIndex, lineEndIndex, lineVertexArray, labelPlaneMatrix, projectionCache, rotateToLine, getElevation);
|
|
228
241
|
if (!lastPlacedGlyph)
|
|
229
242
|
return null;
|
|
230
243
|
|
|
@@ -252,7 +265,7 @@ function requiresOrientationChange(writingMode, firstPoint, lastPoint, aspectRat
|
|
|
252
265
|
return null;
|
|
253
266
|
}
|
|
254
267
|
|
|
255
|
-
function placeGlyphsAlongLine(symbol, fontSize, flip, keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, glyphOffsetArray, lineVertexArray, dynamicLayoutVertexArray, anchorPoint, tileAnchorPoint, projectionCache, aspectRatio, rotateToLine) {
|
|
268
|
+
function placeGlyphsAlongLine(symbol, fontSize, flip, keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, glyphOffsetArray, lineVertexArray, dynamicLayoutVertexArray, anchorPoint, tileAnchorPoint, projectionCache, aspectRatio, rotateToLine, getElevation) {
|
|
256
269
|
const fontScale = fontSize / 24;
|
|
257
270
|
const lineOffsetX = symbol.lineOffsetX * fontScale;
|
|
258
271
|
const lineOffsetY = symbol.lineOffsetY * fontScale;
|
|
@@ -265,12 +278,12 @@ function placeGlyphsAlongLine(symbol, fontSize, flip, keepUpright, posMatrix, la
|
|
|
265
278
|
|
|
266
279
|
// Place the first and the last glyph in the label first, so we can figure out
|
|
267
280
|
// the overall orientation of the label and determine whether it needs to be flipped in keepUpright mode
|
|
268
|
-
const firstAndLastGlyph = placeFirstAndLastGlyph(fontScale, glyphOffsetArray, lineOffsetX, lineOffsetY, flip, anchorPoint, tileAnchorPoint, symbol, lineVertexArray, labelPlaneMatrix, projectionCache, rotateToLine);
|
|
281
|
+
const firstAndLastGlyph = placeFirstAndLastGlyph(fontScale, glyphOffsetArray, lineOffsetX, lineOffsetY, flip, anchorPoint, tileAnchorPoint, symbol, lineVertexArray, labelPlaneMatrix, projectionCache, rotateToLine, getElevation);
|
|
269
282
|
if (!firstAndLastGlyph) {
|
|
270
283
|
return {notEnoughRoom: true};
|
|
271
284
|
}
|
|
272
|
-
const firstPoint = project(firstAndLastGlyph.first.point, glCoordMatrix).point;
|
|
273
|
-
const lastPoint = project(firstAndLastGlyph.last.point, glCoordMatrix).point;
|
|
285
|
+
const firstPoint = project(firstAndLastGlyph.first.point, glCoordMatrix, getElevation).point;
|
|
286
|
+
const lastPoint = project(firstAndLastGlyph.last.point, glCoordMatrix, getElevation).point;
|
|
274
287
|
|
|
275
288
|
if (keepUpright && !flip) {
|
|
276
289
|
const orientationChange = requiresOrientationChange(symbol.writingMode, firstPoint, lastPoint, aspectRatio);
|
|
@@ -284,24 +297,24 @@ function placeGlyphsAlongLine(symbol, fontSize, flip, keepUpright, posMatrix, la
|
|
|
284
297
|
// Since first and last glyph fit on the line, we're sure that the rest of the glyphs can be placed
|
|
285
298
|
// $FlowFixMe
|
|
286
299
|
placedGlyphs.push(placeGlyphAlongLine(fontScale * glyphOffsetArray.getoffsetX(glyphIndex), lineOffsetX, lineOffsetY, flip, anchorPoint, tileAnchorPoint, symbol.segment,
|
|
287
|
-
lineStartIndex, lineEndIndex, lineVertexArray, labelPlaneMatrix, projectionCache, rotateToLine));
|
|
300
|
+
lineStartIndex, lineEndIndex, lineVertexArray, labelPlaneMatrix, projectionCache, rotateToLine, getElevation));
|
|
288
301
|
}
|
|
289
302
|
placedGlyphs.push(firstAndLastGlyph.last);
|
|
290
303
|
} else {
|
|
291
304
|
// Only a single glyph to place
|
|
292
305
|
// So, determine whether to flip based on projected angle of the line segment it's on
|
|
293
306
|
if (keepUpright && !flip) {
|
|
294
|
-
const a = project(tileAnchorPoint, posMatrix).point;
|
|
307
|
+
const a = project(tileAnchorPoint, posMatrix, getElevation).point;
|
|
295
308
|
const tileVertexIndex = (symbol.lineStartIndex + symbol.segment + 1);
|
|
296
309
|
// $FlowFixMe
|
|
297
310
|
const tileSegmentEnd = new Point(lineVertexArray.getx(tileVertexIndex), lineVertexArray.gety(tileVertexIndex));
|
|
298
|
-
const projectedVertex = project(tileSegmentEnd, posMatrix);
|
|
311
|
+
const projectedVertex = project(tileSegmentEnd, posMatrix, getElevation);
|
|
299
312
|
// We know the anchor will be in the viewport, but the end of the line segment may be
|
|
300
313
|
// behind the plane of the camera, in which case we can use a point at any arbitrary (closer)
|
|
301
314
|
// point on the segment.
|
|
302
315
|
const b = (projectedVertex.signedDistanceFromCamera > 0) ?
|
|
303
316
|
projectedVertex.point :
|
|
304
|
-
projectTruncatedLineSegment(tileAnchorPoint, tileSegmentEnd, a, 1, posMatrix);
|
|
317
|
+
projectTruncatedLineSegment(tileAnchorPoint, tileSegmentEnd, a, 1, posMatrix, getElevation);
|
|
305
318
|
|
|
306
319
|
const orientationChange = requiresOrientationChange(symbol.writingMode, a, b, aspectRatio);
|
|
307
320
|
if (orientationChange) {
|
|
@@ -310,7 +323,7 @@ function placeGlyphsAlongLine(symbol, fontSize, flip, keepUpright, posMatrix, la
|
|
|
310
323
|
}
|
|
311
324
|
// $FlowFixMe
|
|
312
325
|
const singleGlyph = placeGlyphAlongLine(fontScale * glyphOffsetArray.getoffsetX(symbol.glyphStartIndex), lineOffsetX, lineOffsetY, flip, anchorPoint, tileAnchorPoint, symbol.segment,
|
|
313
|
-
symbol.lineStartIndex, symbol.lineStartIndex + symbol.lineLength, lineVertexArray, labelPlaneMatrix, projectionCache, rotateToLine);
|
|
326
|
+
symbol.lineStartIndex, symbol.lineStartIndex + symbol.lineLength, lineVertexArray, labelPlaneMatrix, projectionCache, rotateToLine, getElevation);
|
|
314
327
|
if (!singleGlyph)
|
|
315
328
|
return {notEnoughRoom: true};
|
|
316
329
|
|
|
@@ -323,18 +336,19 @@ function placeGlyphsAlongLine(symbol, fontSize, flip, keepUpright, posMatrix, la
|
|
|
323
336
|
return {};
|
|
324
337
|
}
|
|
325
338
|
|
|
326
|
-
function projectTruncatedLineSegment(previousTilePoint: Point, currentTilePoint: Point, previousProjectedPoint: Point, minimumLength: number, projectionMatrix: mat4) {
|
|
339
|
+
function projectTruncatedLineSegment(previousTilePoint: Point, currentTilePoint: Point, previousProjectedPoint: Point, minimumLength: number, projectionMatrix: mat4, getElevation: (x: number, y: number) => number) {
|
|
327
340
|
// We are assuming "previousTilePoint" won't project to a point within one unit of the camera plane
|
|
328
341
|
// If it did, that would mean our label extended all the way out from within the viewport to a (very distant)
|
|
329
342
|
// point near the plane of the camera. We wouldn't be able to render the label anyway once it crossed the
|
|
330
343
|
// plane of the camera.
|
|
331
|
-
const projectedUnitVertex = project(previousTilePoint.add(previousTilePoint.sub(currentTilePoint)._unit()), projectionMatrix).point;
|
|
344
|
+
const projectedUnitVertex = project(previousTilePoint.add(previousTilePoint.sub(currentTilePoint)._unit()), projectionMatrix, getElevation).point;
|
|
332
345
|
const projectedUnitSegment = previousProjectedPoint.sub(projectedUnitVertex);
|
|
333
346
|
|
|
334
347
|
return previousProjectedPoint.add(projectedUnitSegment._mult(minimumLength / projectedUnitSegment.mag()));
|
|
335
348
|
}
|
|
336
349
|
|
|
337
|
-
function placeGlyphAlongLine(
|
|
350
|
+
function placeGlyphAlongLine(
|
|
351
|
+
offsetX: number,
|
|
338
352
|
lineOffsetX: number,
|
|
339
353
|
lineOffsetY: number,
|
|
340
354
|
flip: boolean,
|
|
@@ -348,7 +362,8 @@ function placeGlyphAlongLine(offsetX: number,
|
|
|
348
362
|
projectionCache: {
|
|
349
363
|
[_: number]: Point;
|
|
350
364
|
},
|
|
351
|
-
rotateToLine: boolean
|
|
365
|
+
rotateToLine: boolean,
|
|
366
|
+
getElevation: (x: number, y: number) => number) {
|
|
352
367
|
|
|
353
368
|
const combinedOffsetX = flip ?
|
|
354
369
|
offsetX - lineOffsetX :
|
|
@@ -390,7 +405,7 @@ function placeGlyphAlongLine(offsetX: number,
|
|
|
390
405
|
current = projectionCache[currentIndex];
|
|
391
406
|
if (current === undefined) {
|
|
392
407
|
const currentVertex = new Point(lineVertexArray.getx(currentIndex), lineVertexArray.gety(currentIndex));
|
|
393
|
-
const projection = project(currentVertex, labelPlaneMatrix);
|
|
408
|
+
const projection = project(currentVertex, labelPlaneMatrix, getElevation);
|
|
394
409
|
if (projection.signedDistanceFromCamera > 0) {
|
|
395
410
|
current = projectionCache[currentIndex] = projection.point;
|
|
396
411
|
} else {
|
|
@@ -401,7 +416,7 @@ function placeGlyphAlongLine(offsetX: number,
|
|
|
401
416
|
tileAnchorPoint :
|
|
402
417
|
new Point(lineVertexArray.getx(previousLineVertexIndex), lineVertexArray.gety(previousLineVertexIndex));
|
|
403
418
|
// Don't cache because the new vertex might not be far enough out for future glyphs on the same segment
|
|
404
|
-
current = projectTruncatedLineSegment(previousTilePoint, currentVertex, prev, absOffsetX - distanceToPrev + 1, labelPlaneMatrix);
|
|
419
|
+
current = projectTruncatedLineSegment(previousTilePoint, currentVertex, prev, absOffsetX - distanceToPrev + 1, labelPlaneMatrix, getElevation);
|
|
405
420
|
}
|
|
406
421
|
}
|
|
407
422
|
|
package/src/ui/camera.ts
CHANGED
|
@@ -898,6 +898,7 @@ abstract class Camera extends Evented {
|
|
|
898
898
|
|
|
899
899
|
_prepareEase(eventData: any, noMoveStart: boolean, currently: any = {}) {
|
|
900
900
|
this._moving = true;
|
|
901
|
+
this.fire(new Event('freezeElevation', {freeze: true}));
|
|
901
902
|
|
|
902
903
|
if (!noMoveStart && !currently.moving) {
|
|
903
904
|
this.fire(new Event('movestart', eventData));
|
|
@@ -933,6 +934,7 @@ abstract class Camera extends Evented {
|
|
|
933
934
|
return;
|
|
934
935
|
}
|
|
935
936
|
delete this._easeId;
|
|
937
|
+
this.fire(new Event('freezeElevation', {freeze: false}));
|
|
936
938
|
|
|
937
939
|
const wasZooming = this._zooming;
|
|
938
940
|
const wasRotating = this._rotating;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import DOM from '../../util/dom';
|
|
2
|
+
import {bindAll} from '../../util/util';
|
|
3
|
+
|
|
4
|
+
import type Map from '../map';
|
|
5
|
+
import type {IControl} from './control';
|
|
6
|
+
import type {TerrainSpecification} from '../../style-spec/types.g';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* An `TerrainControl` control adds a button to turn terrain on and off.
|
|
10
|
+
*
|
|
11
|
+
* @implements {IControl}
|
|
12
|
+
* @param {Object} [options]
|
|
13
|
+
* @param {string} [options.id] The ID of the raster-dem source to use.
|
|
14
|
+
* @param {exaggeration: number; elevationOffset: number} [options.options] Allowed options are exaggeration: number; elevationOffset: number
|
|
15
|
+
* @example
|
|
16
|
+
* var map = new maplibregl.Map({TerrainControl: false})
|
|
17
|
+
* .addControl(new maplibregl.TerrainControl({
|
|
18
|
+
* source: "terrain"
|
|
19
|
+
* }));
|
|
20
|
+
*/
|
|
21
|
+
class TerrainControl implements IControl {
|
|
22
|
+
options: TerrainSpecification;
|
|
23
|
+
_map: Map;
|
|
24
|
+
_container: HTMLElement;
|
|
25
|
+
_terrainButton: HTMLButtonElement;
|
|
26
|
+
|
|
27
|
+
constructor(options: TerrainSpecification) {
|
|
28
|
+
this.options = options;
|
|
29
|
+
|
|
30
|
+
bindAll([
|
|
31
|
+
'_toggleTerrain',
|
|
32
|
+
'_updateTerrainIcon',
|
|
33
|
+
], this);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
onAdd(map: Map) {
|
|
37
|
+
this._map = map;
|
|
38
|
+
this._container = DOM.create('div', 'maplibregl-ctrl maplibregl-ctrl-group mapboxgl-ctrl mapboxgl-ctrl-group');
|
|
39
|
+
this._terrainButton = DOM.create('button', 'maplibregl-ctrl-terrain mapboxgl-ctrl-terrain', this._container);
|
|
40
|
+
DOM.create('span', 'maplibregl-ctrl-icon mapboxgl-ctrl-icon', this._terrainButton).setAttribute('aria-hidden', 'true');
|
|
41
|
+
this._terrainButton.type = 'button';
|
|
42
|
+
this._terrainButton.addEventListener('click', this._toggleTerrain);
|
|
43
|
+
|
|
44
|
+
this._updateTerrainIcon();
|
|
45
|
+
this._map.on('terrain', this._updateTerrainIcon);
|
|
46
|
+
return this._container;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
onRemove() {
|
|
50
|
+
DOM.remove(this._container);
|
|
51
|
+
this._map.off('terrain', this._updateTerrainIcon);
|
|
52
|
+
this._map = undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
_toggleTerrain() {
|
|
56
|
+
if (this._map.getTerrain()) {
|
|
57
|
+
this._map.setTerrain(null);
|
|
58
|
+
} else {
|
|
59
|
+
this._map.setTerrain(this.options);
|
|
60
|
+
}
|
|
61
|
+
this._updateTerrainIcon();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
_updateTerrainIcon() {
|
|
65
|
+
this._terrainButton.classList.remove('maplibregl-ctrl-terrain', 'mapboxgl-ctrl-terrain');
|
|
66
|
+
this._terrainButton.classList.remove('maplibregl-ctrl-terrain-enabled', 'mapboxgl-ctrl-terrain-enabled');
|
|
67
|
+
if (this._map.style.terrain) {
|
|
68
|
+
this._terrainButton.classList.add('maplibregl-ctrl-terrain-enabled', 'mapboxgl-ctrl-terrain-enabled');
|
|
69
|
+
this._terrainButton.title = this._map._getUIString('TerrainControl.disableTerrain');
|
|
70
|
+
} else {
|
|
71
|
+
this._terrainButton.classList.add('maplibregl-ctrl-terrain', 'mapboxgl-ctrl-terrain');
|
|
72
|
+
this._terrainButton.title = this._map._getUIString('TerrainControl.enableTerrain');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export default TerrainControl;
|
package/src/ui/default_locale.ts
CHANGED
|
@@ -13,8 +13,9 @@ const defaultLocale = {
|
|
|
13
13
|
'ScaleControl.Meters': 'm',
|
|
14
14
|
'ScaleControl.Kilometers': 'km',
|
|
15
15
|
'ScaleControl.Miles': 'mi',
|
|
16
|
-
'ScaleControl.NauticalMiles': 'nm'
|
|
17
|
-
|
|
16
|
+
'ScaleControl.NauticalMiles': 'nm',
|
|
17
|
+
'TerrainControl.enableTerrain': 'Enable terrain',
|
|
18
|
+
'TerrainControl.disableTerrain': 'Disable terrain'
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
export default defaultLocale;
|
package/src/ui/events.ts
CHANGED
|
@@ -311,6 +311,10 @@ export type MapDataEvent = {
|
|
|
311
311
|
sourceDataType: MapSourceDataType;
|
|
312
312
|
};
|
|
313
313
|
|
|
314
|
+
export type MapTerrainEvent = {
|
|
315
|
+
type: 'terrain';
|
|
316
|
+
};
|
|
317
|
+
|
|
314
318
|
export type MapContextEvent = {
|
|
315
319
|
type: 'webglcontextlost' | 'webglcontextrestored';
|
|
316
320
|
originalEvent: WebGLContextEvent;
|
|
@@ -386,6 +390,8 @@ export type MapEventType = {
|
|
|
386
390
|
pitchend: MapLibreEvent<MouseEvent | TouchEvent | undefined>;
|
|
387
391
|
|
|
388
392
|
wheel: MapWheelEvent;
|
|
393
|
+
|
|
394
|
+
terrain: MapTerrainEvent;
|
|
389
395
|
};
|
|
390
396
|
|
|
391
397
|
export type MapEvent =
|
|
@@ -1394,8 +1400,17 @@ export type MapEvent =
|
|
|
1394
1400
|
*/
|
|
1395
1401
|
| 'style.load'
|
|
1396
1402
|
|
|
1403
|
+
/**
|
|
1404
|
+
* @event terrain
|
|
1405
|
+
* @memberof Map
|
|
1406
|
+
* @instance
|
|
1407
|
+
* @private
|
|
1408
|
+
*/
|
|
1409
|
+
| 'terrain'
|
|
1410
|
+
|
|
1397
1411
|
/**
|
|
1398
1412
|
* Fired when a request for one of the map's sources' tiles is aborted.
|
|
1413
|
+
* Fired when a request for one of the map's sources' data is aborted.
|
|
1399
1414
|
* See {@link MapDataEvent} for more information.
|
|
1400
1415
|
*
|
|
1401
1416
|
* @event dataabort
|
|
@@ -1406,7 +1421,7 @@ export type MapEvent =
|
|
|
1406
1421
|
* // Initialize the map
|
|
1407
1422
|
* var map = new maplibregl.Map({ // map options });
|
|
1408
1423
|
* // Set an event listener that fires
|
|
1409
|
-
* // when a request for one of the map's sources'
|
|
1424
|
+
* // when a request for one of the map's sources' data is aborted.
|
|
1410
1425
|
* map.on('dataabort', function() {
|
|
1411
1426
|
* console.log('A dataabort event occurred.');
|
|
1412
1427
|
* });
|
|
@@ -1414,7 +1429,7 @@ export type MapEvent =
|
|
|
1414
1429
|
| 'dataabort'
|
|
1415
1430
|
|
|
1416
1431
|
/**
|
|
1417
|
-
* Fired when a request for one of the map's sources'
|
|
1432
|
+
* Fired when a request for one of the map's sources' data is aborted.
|
|
1418
1433
|
* See {@link MapDataEvent} for more information.
|
|
1419
1434
|
*
|
|
1420
1435
|
* @event sourcedataabort
|
|
@@ -1425,7 +1440,7 @@ export type MapEvent =
|
|
|
1425
1440
|
* // Initialize the map
|
|
1426
1441
|
* var map = new maplibregl.Map({ // map options });
|
|
1427
1442
|
* // Set an event listener that fires
|
|
1428
|
-
* // when a request for one of the map's sources'
|
|
1443
|
+
* // when a request for one of the map's sources' data is aborted.
|
|
1429
1444
|
* map.on('sourcedataabort', function() {
|
|
1430
1445
|
* console.log('A sourcedataabort event occurred.');
|
|
1431
1446
|
* });
|
|
@@ -18,6 +18,7 @@ import DragRotateHandler from './handler/shim/drag_rotate';
|
|
|
18
18
|
import TouchZoomRotateHandler from './handler/shim/touch_zoom_rotate';
|
|
19
19
|
import {bindAll, extend} from '../util/util';
|
|
20
20
|
import Point from '@mapbox/point-geometry';
|
|
21
|
+
import LngLat from '../geo/lng_lat';
|
|
21
22
|
import assert from 'assert';
|
|
22
23
|
|
|
23
24
|
export type InputEvent = MouseEvent | TouchEvent | KeyboardEvent | WheelEvent;
|
|
@@ -100,6 +101,7 @@ class HandlerManager {
|
|
|
100
101
|
_handlersById: {[x: string]: Handler};
|
|
101
102
|
_updatingCamera: boolean;
|
|
102
103
|
_changes: Array<[HandlerResult, any, any]>;
|
|
104
|
+
_drag: {center: Point; lngLat: LngLat; point: Point; handlerName: string};
|
|
103
105
|
_previousActiveHandlers: {[x: string]: Handler};
|
|
104
106
|
_listeners: Array<[Window | Document | HTMLElement, string, {
|
|
105
107
|
passive?: boolean;
|
|
@@ -411,11 +413,11 @@ class HandlerManager {
|
|
|
411
413
|
}
|
|
412
414
|
|
|
413
415
|
_updateMapTransform(combinedResult: any, combinedEventsInProgress: any, deactivatedHandlers: any) {
|
|
414
|
-
|
|
415
416
|
const map = this._map;
|
|
416
417
|
const tr = map.transform;
|
|
418
|
+
const terrain = map.style && map.style.terrain;
|
|
417
419
|
|
|
418
|
-
if (!hasChange(combinedResult)) {
|
|
420
|
+
if (!hasChange(combinedResult) && !(terrain && this._drag)) {
|
|
419
421
|
return this._fireEvents(combinedEventsInProgress, deactivatedHandlers, true);
|
|
420
422
|
}
|
|
421
423
|
|
|
@@ -433,7 +435,35 @@ class HandlerManager {
|
|
|
433
435
|
if (bearingDelta) tr.bearing += bearingDelta;
|
|
434
436
|
if (pitchDelta) tr.pitch += pitchDelta;
|
|
435
437
|
if (zoomDelta) tr.zoom += zoomDelta;
|
|
436
|
-
|
|
438
|
+
|
|
439
|
+
if (!terrain) {
|
|
440
|
+
tr.setLocationAtPoint(loc, around);
|
|
441
|
+
} else {
|
|
442
|
+
// when 3d-terrain is enabled act a litte different:
|
|
443
|
+
// - draging do not drag the picked point itself, instead it drags the map by pixel-delta.
|
|
444
|
+
// With this approach it is no longer possible to pick a point from somewhere near
|
|
445
|
+
// the horizon to the center in one move.
|
|
446
|
+
// So this logic avoids the problem, that in such cases you easily loose orientation.
|
|
447
|
+
// - scrollzoom does not zoom into the mouse-point, instead it zooms into map-center
|
|
448
|
+
// this should be fixed in future-version
|
|
449
|
+
// when dragging starts, remember mousedown-location and panDelta from this point
|
|
450
|
+
if (combinedEventsInProgress.drag && !this._drag) {
|
|
451
|
+
this._drag = {
|
|
452
|
+
center: tr.centerPoint,
|
|
453
|
+
lngLat: tr.pointLocation(around),
|
|
454
|
+
point: around,
|
|
455
|
+
handlerName: combinedEventsInProgress.drag.handlerName
|
|
456
|
+
};
|
|
457
|
+
map.fire(new Event('freezeElevation', {freeze: true}));
|
|
458
|
+
// when dragging ends, recalcuate the zoomlevel for the new center coordinate
|
|
459
|
+
} else if (this._drag && deactivatedHandlers[this._drag.handlerName]) {
|
|
460
|
+
map.fire(new Event('freezeElevation', {freeze: false}));
|
|
461
|
+
this._drag = null;
|
|
462
|
+
// drag map
|
|
463
|
+
} else if (combinedEventsInProgress.drag && this._drag) {
|
|
464
|
+
tr.center = tr.pointLocation(tr.centerPoint.sub(panDelta));
|
|
465
|
+
}
|
|
466
|
+
}
|
|
437
467
|
|
|
438
468
|
this._map._update();
|
|
439
469
|
if (!combinedResult.noInertia) this._inertia.record(combinedResult);
|
package/src/ui/map.ts
CHANGED
|
@@ -51,7 +51,8 @@ import type {
|
|
|
51
51
|
FilterSpecification,
|
|
52
52
|
StyleSpecification,
|
|
53
53
|
LightSpecification,
|
|
54
|
-
SourceSpecification
|
|
54
|
+
SourceSpecification,
|
|
55
|
+
TerrainSpecification
|
|
55
56
|
} from '../style-spec/types.g';
|
|
56
57
|
import {Callback} from '../types/callback';
|
|
57
58
|
import type {ControlPosition, IControl} from './control/control';
|
|
@@ -432,6 +433,10 @@ class Map extends Camera {
|
|
|
432
433
|
this.on('move', () => this._update(false));
|
|
433
434
|
this.on('moveend', () => this._update(false));
|
|
434
435
|
this.on('zoom', () => this._update(true));
|
|
436
|
+
this.on('terrain', () => {
|
|
437
|
+
this.painter.terrainFacilitator.dirty = true;
|
|
438
|
+
this._update(true);
|
|
439
|
+
});
|
|
435
440
|
|
|
436
441
|
if (typeof window !== 'undefined') {
|
|
437
442
|
addEventListener('online', this._onWindowOnline, false);
|
|
@@ -873,7 +878,7 @@ class Map extends Camera {
|
|
|
873
878
|
* var point = map.project(coordinate);
|
|
874
879
|
*/
|
|
875
880
|
project(lnglat: LngLatLike) {
|
|
876
|
-
return this.transform.locationPoint(LngLat.convert(lnglat));
|
|
881
|
+
return this.transform.locationPoint(LngLat.convert(lnglat), this.style && this.style.terrain);
|
|
877
882
|
}
|
|
878
883
|
|
|
879
884
|
/**
|
|
@@ -889,7 +894,7 @@ class Map extends Camera {
|
|
|
889
894
|
* });
|
|
890
895
|
*/
|
|
891
896
|
unproject(point: PointLike) {
|
|
892
|
-
return this.transform.pointLocation(Point.convert(point));
|
|
897
|
+
return this.transform.pointLocation(Point.convert(point), this.style && this.style.terrain);
|
|
893
898
|
}
|
|
894
899
|
|
|
895
900
|
/**
|
|
@@ -1571,6 +1576,28 @@ class Map extends Camera {
|
|
|
1571
1576
|
return source.loaded();
|
|
1572
1577
|
}
|
|
1573
1578
|
|
|
1579
|
+
/**
|
|
1580
|
+
* Loads a 3D terrain mesh, based on a "raster-dem" source.
|
|
1581
|
+
* @param {TerrainSpecification} [options] Options object.
|
|
1582
|
+
* @returns {Map} `this`
|
|
1583
|
+
* @example
|
|
1584
|
+
* map.setTerrain({ source: 'terrain' });
|
|
1585
|
+
*/
|
|
1586
|
+
setTerrain(options: TerrainSpecification): Map {
|
|
1587
|
+
this.style.setTerrain(options);
|
|
1588
|
+
return this;
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
/**
|
|
1592
|
+
* Get the terrain-options if terrain is loaded
|
|
1593
|
+
* @returns {TerrainSpecification} the TerrainSpecification passed to setTerrain
|
|
1594
|
+
* @example
|
|
1595
|
+
* map.getTerrain(); // { source: 'terrain' };
|
|
1596
|
+
*/
|
|
1597
|
+
getTerrain(): TerrainSpecification {
|
|
1598
|
+
return this.style.terrain && this.style.terrain.options;
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1574
1601
|
/**
|
|
1575
1602
|
* Returns a Boolean indicating whether all tiles in the viewport from all sources on
|
|
1576
1603
|
* the style are loaded.
|
|
@@ -1579,8 +1606,7 @@ class Map extends Camera {
|
|
|
1579
1606
|
* @example
|
|
1580
1607
|
* var tilesLoaded = map.areTilesLoaded();
|
|
1581
1608
|
*/
|
|
1582
|
-
|
|
1583
|
-
areTilesLoaded() {
|
|
1609
|
+
areTilesLoaded(): boolean {
|
|
1584
1610
|
const sources = this.style && this.style.sourceCaches;
|
|
1585
1611
|
for (const id in sources) {
|
|
1586
1612
|
const source = sources[id];
|
|
@@ -1614,7 +1640,7 @@ class Map extends Camera {
|
|
|
1614
1640
|
* @example
|
|
1615
1641
|
* map.removeSource('bathymetry-data');
|
|
1616
1642
|
*/
|
|
1617
|
-
removeSource(id: string) {
|
|
1643
|
+
removeSource(id: string): Map {
|
|
1618
1644
|
this.style.removeSource(id);
|
|
1619
1645
|
return this._update(true);
|
|
1620
1646
|
}
|
|
@@ -2550,6 +2576,10 @@ class Map extends Camera {
|
|
|
2550
2576
|
this.style._updateSources(this.transform);
|
|
2551
2577
|
}
|
|
2552
2578
|
|
|
2579
|
+
// update terrain stuff
|
|
2580
|
+
if (this.style.terrain) this.style.terrain.sourceCache.update(this.transform, this.style.terrain);
|
|
2581
|
+
this.transform.updateElevation(this.style.terrain);
|
|
2582
|
+
|
|
2553
2583
|
this._placementDirty = this.style && this.style._updatePlacement(this.painter.transform, this.showCollisionBoxes, this._fadeDuration, this._crossSourceCollisions);
|
|
2554
2584
|
|
|
2555
2585
|
// Actually draw
|
package/src/ui/marker.test.ts
CHANGED
|
@@ -4,6 +4,7 @@ import Popup from './popup';
|
|
|
4
4
|
import LngLat from '../geo/lng_lat';
|
|
5
5
|
import Point from '@mapbox/point-geometry';
|
|
6
6
|
import simulate from '../../test/unit/lib/simulate_interaction';
|
|
7
|
+
import type Terrain from '../render/terrain';
|
|
7
8
|
|
|
8
9
|
function createMap(options = {}) {
|
|
9
10
|
const container = window.document.createElement('div');
|
|
@@ -771,4 +772,24 @@ describe('marker', () => {
|
|
|
771
772
|
|
|
772
773
|
map.remove();
|
|
773
774
|
});
|
|
775
|
+
|
|
776
|
+
test('Marker removed after update when terrain is on should clear timeout', () => {
|
|
777
|
+
jest.spyOn(global, 'setTimeout');
|
|
778
|
+
jest.spyOn(global, 'clearTimeout');
|
|
779
|
+
const map = createMap();
|
|
780
|
+
const marker = new Marker()
|
|
781
|
+
.setLngLat([0, 0])
|
|
782
|
+
.addTo(map);
|
|
783
|
+
map.style.terrain = {
|
|
784
|
+
getElevation: () => 0
|
|
785
|
+
} as any as Terrain;
|
|
786
|
+
|
|
787
|
+
marker.setOffset([10, 10]);
|
|
788
|
+
|
|
789
|
+
expect(setTimeout).toHaveBeenCalled();
|
|
790
|
+
marker.remove();
|
|
791
|
+
expect(clearTimeout).toHaveBeenCalled();
|
|
792
|
+
|
|
793
|
+
map.remove();
|
|
794
|
+
});
|
|
774
795
|
});
|
package/src/ui/marker.ts
CHANGED
|
@@ -74,6 +74,7 @@ export default class Marker extends Evented {
|
|
|
74
74
|
_pitchAlignment: string;
|
|
75
75
|
_rotationAlignment: string;
|
|
76
76
|
_originalTabIndex: string; // original tabindex of _element
|
|
77
|
+
_opacityTimeout: ReturnType<typeof setTimeout>;
|
|
77
78
|
|
|
78
79
|
constructor(options?: MarkerOptions, legacyOptions?: MarkerOptions) {
|
|
79
80
|
super();
|
|
@@ -264,6 +265,10 @@ export default class Marker extends Evented {
|
|
|
264
265
|
* @returns {Marker} `this`
|
|
265
266
|
*/
|
|
266
267
|
remove() {
|
|
268
|
+
if (this._opacityTimeout) {
|
|
269
|
+
clearTimeout(this._opacityTimeout);
|
|
270
|
+
delete this._opacityTimeout;
|
|
271
|
+
}
|
|
267
272
|
if (this._map) {
|
|
268
273
|
this._map.off('click', this._onMapClick);
|
|
269
274
|
this._map.off('move', this._update);
|
|
@@ -469,6 +474,15 @@ export default class Marker extends Evented {
|
|
|
469
474
|
}
|
|
470
475
|
|
|
471
476
|
DOM.setTransform(this._element, `${anchorTranslate[this._anchor]} translate(${this._pos.x}px, ${this._pos.y}px) ${pitch} ${rotation}`);
|
|
477
|
+
|
|
478
|
+
// in case of 3D, ask the terrain coords-framebuffer for this pos and check if the marker is visible
|
|
479
|
+
// call this logic in setTimeout with a timeout of 100ms to save performance in map-movement
|
|
480
|
+
if (this._map.style && this._map.style.terrain && !this._opacityTimeout) this._opacityTimeout = setTimeout(() => {
|
|
481
|
+
const lnglat = this._map.unproject(this._pos);
|
|
482
|
+
const metresPerPixel = 40075016.686 * Math.abs(Math.cos(this._lngLat.lat * Math.PI / 180)) / Math.pow(2, this._map.transform.tileZoom + 8);
|
|
483
|
+
this._element.style.opacity = lnglat.distanceTo(this._lngLat) > metresPerPixel * 20 ? '0.2' : '1.0';
|
|
484
|
+
this._opacityTimeout = null;
|
|
485
|
+
}, 100);
|
|
472
486
|
}
|
|
473
487
|
|
|
474
488
|
/**
|
package/src/util/primitives.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {mat4, vec3, vec4} from 'gl-matrix';
|
|
2
|
-
import assert from 'assert';
|
|
3
2
|
|
|
4
3
|
class Frustum {
|
|
5
4
|
|
|
@@ -19,10 +18,12 @@ class Frustum {
|
|
|
19
18
|
|
|
20
19
|
const scale = Math.pow(2, zoom);
|
|
21
20
|
|
|
22
|
-
// Transform frustum corner points from clip space to tile space
|
|
23
|
-
const frustumCoords = clipSpaceCorners
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
// Transform frustum corner points from clip space to tile space, Z to meters
|
|
22
|
+
const frustumCoords = clipSpaceCorners.map(v => {
|
|
23
|
+
v = vec4.transformMat4([] as any, v as any, invProj) as any;
|
|
24
|
+
const s = 1.0 / v[3] / worldSize * scale;
|
|
25
|
+
return vec4.mul(v as any, v as any, [s, s, 1.0 / v[3], s] as vec4);
|
|
26
|
+
});
|
|
26
27
|
|
|
27
28
|
const frustumPlanePointIndices = [
|
|
28
29
|
[0, 1, 2], // near
|
|
@@ -84,14 +85,16 @@ class Aabb {
|
|
|
84
85
|
intersects(frustum: Frustum): number {
|
|
85
86
|
// Execute separating axis test between two convex objects to find intersections
|
|
86
87
|
// Each frustum plane together with 3 major axes define the separating axes
|
|
87
|
-
// Note: test only 4 points as both min and max points have equal elevation
|
|
88
|
-
assert(this.min[2] === 0 && this.max[2] === 0);
|
|
89
88
|
|
|
90
89
|
const aabbPoints = [
|
|
91
|
-
[this.min[0], this.min[1],
|
|
92
|
-
[this.max[0], this.min[1],
|
|
93
|
-
[this.max[0], this.max[1],
|
|
94
|
-
[this.min[0], this.max[1],
|
|
90
|
+
[this.min[0], this.min[1], this.min[2], 1],
|
|
91
|
+
[this.max[0], this.min[1], this.min[2], 1],
|
|
92
|
+
[this.max[0], this.max[1], this.min[2], 1],
|
|
93
|
+
[this.min[0], this.max[1], this.min[2], 1],
|
|
94
|
+
[this.min[0], this.min[1], this.max[2], 1],
|
|
95
|
+
[this.max[0], this.min[1], this.max[2], 1],
|
|
96
|
+
[this.max[0], this.max[1], this.max[2], 1],
|
|
97
|
+
[this.min[0], this.max[1], this.max[2], 1]
|
|
95
98
|
];
|
|
96
99
|
|
|
97
100
|
let fullyInside = true;
|