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
|
@@ -21,6 +21,8 @@ import type Transform from '../geo/transform';
|
|
|
21
21
|
import type {TileState} from './tile';
|
|
22
22
|
import type {Callback} from '../types/callback';
|
|
23
23
|
import type {SourceSpecification} from '../style-spec/types.g';
|
|
24
|
+
import type {MapSourceDataEvent} from '../ui/events';
|
|
25
|
+
import Terrain from '../render/terrain';
|
|
24
26
|
|
|
25
27
|
/**
|
|
26
28
|
* `SourceCache` is responsible for
|
|
@@ -56,7 +58,10 @@ class SourceCache extends Evented {
|
|
|
56
58
|
_shouldReloadOnResume: boolean;
|
|
57
59
|
_coveredTiles: {[_: string]: boolean};
|
|
58
60
|
transform: Transform;
|
|
61
|
+
terrain: Terrain;
|
|
59
62
|
used: boolean;
|
|
63
|
+
usedForTerrain: boolean;
|
|
64
|
+
tileSize: number;
|
|
60
65
|
_state: SourceFeatureState;
|
|
61
66
|
_loadedParentTiles: {[_: string]: Tile};
|
|
62
67
|
|
|
@@ -68,7 +73,7 @@ class SourceCache extends Evented {
|
|
|
68
73
|
this.id = id;
|
|
69
74
|
this.dispatcher = dispatcher;
|
|
70
75
|
|
|
71
|
-
this.on('data', (e) => {
|
|
76
|
+
this.on('data', (e: MapSourceDataEvent) => {
|
|
72
77
|
// this._sourceLoaded signifies that the TileJSON is loaded if applicable.
|
|
73
78
|
// if the source type does not come with a TileJSON, the flag signifies the
|
|
74
79
|
// source data has loaded (i.e geojson has been tiled on the worker and is ready)
|
|
@@ -79,7 +84,7 @@ class SourceCache extends Evented {
|
|
|
79
84
|
if (this._sourceLoaded && !this._paused && e.dataType === 'source' && e.sourceDataType === 'content') {
|
|
80
85
|
this.reload();
|
|
81
86
|
if (this.transform) {
|
|
82
|
-
this.update(this.transform);
|
|
87
|
+
this.update(this.transform, this.terrain);
|
|
83
88
|
}
|
|
84
89
|
}
|
|
85
90
|
});
|
|
@@ -152,7 +157,7 @@ class SourceCache extends Evented {
|
|
|
152
157
|
this._paused = false;
|
|
153
158
|
this._shouldReloadOnResume = false;
|
|
154
159
|
if (shouldReload) this.reload();
|
|
155
|
-
if (this.transform) this.update(this.transform);
|
|
160
|
+
if (this.transform) this.update(this.transform, this.terrain);
|
|
156
161
|
}
|
|
157
162
|
|
|
158
163
|
_loadTile(tile: Tile, callback: Callback<void>) {
|
|
@@ -263,7 +268,7 @@ class SourceCache extends Evented {
|
|
|
263
268
|
tile.state = 'errored';
|
|
264
269
|
if ((err as any).status !== 404) this._source.fire(new ErrorEvent(err, {tile}));
|
|
265
270
|
// continue to try loading parent/children tiles if a tile doesn't exist (404)
|
|
266
|
-
else this.update(this.transform);
|
|
271
|
+
else this.update(this.transform, this.terrain);
|
|
267
272
|
return;
|
|
268
273
|
}
|
|
269
274
|
|
|
@@ -295,6 +300,7 @@ class SourceCache extends Evented {
|
|
|
295
300
|
|
|
296
301
|
function fillBorder(tile, borderTile) {
|
|
297
302
|
tile.needsHillshadePrepare = true;
|
|
303
|
+
tile.needsTerrainPrepare = true;
|
|
298
304
|
let dx = borderTile.tileID.canonical.x - tile.tileID.canonical.x;
|
|
299
305
|
const dy = borderTile.tileID.canonical.y - tile.tileID.canonical.y;
|
|
300
306
|
const dim = Math.pow(2, tile.tileID.canonical.z);
|
|
@@ -486,8 +492,9 @@ class SourceCache extends Evented {
|
|
|
486
492
|
* are inside the viewport.
|
|
487
493
|
* @private
|
|
488
494
|
*/
|
|
489
|
-
update(transform: Transform) {
|
|
495
|
+
update(transform: Transform, terrain: Terrain) {
|
|
490
496
|
this.transform = transform;
|
|
497
|
+
this.terrain = terrain;
|
|
491
498
|
if (!this._sourceLoaded || this._paused) { return; }
|
|
492
499
|
|
|
493
500
|
this.updateCacheSize(transform);
|
|
@@ -498,18 +505,19 @@ class SourceCache extends Evented {
|
|
|
498
505
|
this._coveredTiles = {};
|
|
499
506
|
|
|
500
507
|
let idealTileIDs;
|
|
501
|
-
if (!this.used) {
|
|
508
|
+
if (!this.used && !this.usedForTerrain) {
|
|
502
509
|
idealTileIDs = [];
|
|
503
510
|
} else if (this._source.tileID) {
|
|
504
511
|
idealTileIDs = transform.getVisibleUnwrappedCoordinates(this._source.tileID)
|
|
505
512
|
.map((unwrapped) => new OverscaledTileID(unwrapped.canonical.z, unwrapped.wrap, unwrapped.canonical.z, unwrapped.canonical.x, unwrapped.canonical.y));
|
|
506
513
|
} else {
|
|
507
514
|
idealTileIDs = transform.coveringTiles({
|
|
508
|
-
tileSize: this._source.tileSize,
|
|
515
|
+
tileSize: this.usedForTerrain ? this.tileSize : this._source.tileSize,
|
|
509
516
|
minzoom: this._source.minzoom,
|
|
510
517
|
maxzoom: this._source.maxzoom,
|
|
511
|
-
roundZoom: this._source.roundZoom,
|
|
512
|
-
reparseOverscaled: this._source.reparseOverscaled
|
|
518
|
+
roundZoom: this.usedForTerrain ? false : this._source.roundZoom,
|
|
519
|
+
reparseOverscaled: this._source.reparseOverscaled,
|
|
520
|
+
terrain
|
|
513
521
|
});
|
|
514
522
|
|
|
515
523
|
if (this._source.hasTile) {
|
|
@@ -522,6 +530,21 @@ class SourceCache extends Evented {
|
|
|
522
530
|
const minCoveringZoom = Math.max(zoom - SourceCache.maxOverzooming, this._source.minzoom);
|
|
523
531
|
const maxCoveringZoom = Math.max(zoom + SourceCache.maxUnderzooming, this._source.minzoom);
|
|
524
532
|
|
|
533
|
+
// When sourcecache is used for terrain also load parent tiles to avoid flickering when zooming out
|
|
534
|
+
if (this.usedForTerrain) {
|
|
535
|
+
const parents = {};
|
|
536
|
+
for (const tileID of idealTileIDs) {
|
|
537
|
+
if (tileID.canonical.z > this._source.minzoom) {
|
|
538
|
+
const parent = tileID.scaledTo(tileID.canonical.z - 1);
|
|
539
|
+
parents[parent.key] = parent;
|
|
540
|
+
// load very low zoom to calculate tile visability in transform.coveringTiles and high zoomlevels correct
|
|
541
|
+
const parent2 = tileID.scaledTo(Math.max(this._source.minzoom, Math.min(tileID.canonical.z, 5)));
|
|
542
|
+
parents[parent2.key] = parent2;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
idealTileIDs = idealTileIDs.concat(Object.values(parents));
|
|
546
|
+
}
|
|
547
|
+
|
|
525
548
|
// Retain is a list of tiles that we shouldn't delete, even if they are not
|
|
526
549
|
// the most ideal tile for the current viewport. This may include tiles like
|
|
527
550
|
// parent or child tiles that are *already* loaded.
|
|
@@ -558,6 +581,44 @@ class SourceCache extends Evented {
|
|
|
558
581
|
retain[id] = parentsForFading[id];
|
|
559
582
|
}
|
|
560
583
|
}
|
|
584
|
+
|
|
585
|
+
// disable fading logic in terrain3D mode to avoid rendering two tiles on the same place
|
|
586
|
+
if (terrain) {
|
|
587
|
+
const idealRasterTileIDs: {[_: string]: OverscaledTileID} = {};
|
|
588
|
+
const missingTileIDs: {[_: string]: OverscaledTileID} = {};
|
|
589
|
+
for (const tileID of idealTileIDs) {
|
|
590
|
+
if (this._tiles[tileID.key].hasData())
|
|
591
|
+
idealRasterTileIDs[tileID.key] = tileID;
|
|
592
|
+
else
|
|
593
|
+
missingTileIDs[tileID.key] = tileID;
|
|
594
|
+
}
|
|
595
|
+
// search for a complete set of children for each missing tile
|
|
596
|
+
for (const key in missingTileIDs) {
|
|
597
|
+
const children = missingTileIDs[key].children(this._source.maxzoom);
|
|
598
|
+
if (this._tiles[children[0].key] && this._tiles[children[1].key] && this._tiles[children[2].key] && this._tiles[children[3].key]) {
|
|
599
|
+
idealRasterTileIDs[children[0].key] = retain[children[0].key] = children[0];
|
|
600
|
+
idealRasterTileIDs[children[1].key] = retain[children[1].key] = children[1];
|
|
601
|
+
idealRasterTileIDs[children[2].key] = retain[children[2].key] = children[2];
|
|
602
|
+
idealRasterTileIDs[children[3].key] = retain[children[3].key] = children[3];
|
|
603
|
+
delete missingTileIDs[key];
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
// search for parent for each missing tile
|
|
607
|
+
for (const key in missingTileIDs) {
|
|
608
|
+
const parent = this.findLoadedParent(missingTileIDs[key], this._source.minzoom);
|
|
609
|
+
if (parent) {
|
|
610
|
+
idealRasterTileIDs[parent.tileID.key] = retain[parent.tileID.key] = parent.tileID;
|
|
611
|
+
// remove idealTiles which would be rendered twice
|
|
612
|
+
for (const key in idealRasterTileIDs) {
|
|
613
|
+
if (idealRasterTileIDs[key].isChildOf(parent.tileID)) delete idealRasterTileIDs[key];
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
// cover all tiles which are not needed
|
|
618
|
+
for (const key in this._tiles) {
|
|
619
|
+
if (!idealRasterTileIDs[key]) this._coveredTiles[key] = true;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
561
622
|
}
|
|
562
623
|
|
|
563
624
|
for (const retainedId in retain) {
|
|
@@ -819,8 +880,8 @@ class SourceCache extends Evented {
|
|
|
819
880
|
transform.getCameraQueryGeometry(pointQueryGeometry) :
|
|
820
881
|
pointQueryGeometry;
|
|
821
882
|
|
|
822
|
-
const queryGeometry = pointQueryGeometry.map((p) => transform.pointCoordinate(p));
|
|
823
|
-
const cameraQueryGeometry = cameraPointQueryGeometry.map((p) => transform.pointCoordinate(p));
|
|
883
|
+
const queryGeometry = pointQueryGeometry.map((p: Point) => transform.pointCoordinate(p, this.terrain));
|
|
884
|
+
const cameraQueryGeometry = cameraPointQueryGeometry.map((p: Point) => transform.pointCoordinate(p, this.terrain));
|
|
824
885
|
|
|
825
886
|
const ids = this.getIds();
|
|
826
887
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import TerrainSourceCache from './terrain_source_cache';
|
|
2
|
+
import Style from '../style/style';
|
|
3
|
+
import {RequestManager} from '../util/request_manager';
|
|
4
|
+
import Dispatcher from '../util/dispatcher';
|
|
5
|
+
import {fakeServer, FakeServer} from 'nise';
|
|
6
|
+
import Transform from '../geo/transform';
|
|
7
|
+
import {Evented} from '../util/evented';
|
|
8
|
+
import Painter from '../render/painter';
|
|
9
|
+
import Context from '../gl/context';
|
|
10
|
+
import gl from 'gl';
|
|
11
|
+
import RasterDEMTileSource from './raster_dem_tile_source';
|
|
12
|
+
|
|
13
|
+
const context = new Context(gl(10, 10));
|
|
14
|
+
const transform = new Transform();
|
|
15
|
+
|
|
16
|
+
class StubMap extends Evented {
|
|
17
|
+
transform: Transform;
|
|
18
|
+
painter: Painter;
|
|
19
|
+
_requestManager: RequestManager;
|
|
20
|
+
|
|
21
|
+
constructor() {
|
|
22
|
+
super();
|
|
23
|
+
this.transform = transform;
|
|
24
|
+
this._requestManager = {
|
|
25
|
+
transformRequest: (url) => {
|
|
26
|
+
return {url};
|
|
27
|
+
}
|
|
28
|
+
} as any as RequestManager;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function createSource(options, transformCallback?) {
|
|
33
|
+
const source = new RasterDEMTileSource('id', options, {send() {}} as any as Dispatcher, null);
|
|
34
|
+
source.onAdd({
|
|
35
|
+
transform,
|
|
36
|
+
_getMapId: () => 1,
|
|
37
|
+
_requestManager: new RequestManager(transformCallback),
|
|
38
|
+
getPixelRatio() { return 1; }
|
|
39
|
+
} as any);
|
|
40
|
+
|
|
41
|
+
source.on('error', (e) => {
|
|
42
|
+
throw e.error;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
return source;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
describe('TerrainSourceCache', () => {
|
|
49
|
+
let server: FakeServer;
|
|
50
|
+
let style: Style;
|
|
51
|
+
let tsc: TerrainSourceCache;
|
|
52
|
+
|
|
53
|
+
beforeAll(done => {
|
|
54
|
+
global.fetch = null;
|
|
55
|
+
server = fakeServer.create();
|
|
56
|
+
server.respondWith('/source.json', JSON.stringify({
|
|
57
|
+
minzoom: 0,
|
|
58
|
+
maxzoom: 22,
|
|
59
|
+
attribution: 'MapLibre',
|
|
60
|
+
tiles: ['http://example.com/{z}/{x}/{y}.pngraw'],
|
|
61
|
+
bounds: [-47, -7, -45, -5]
|
|
62
|
+
}));
|
|
63
|
+
const map = new StubMap();
|
|
64
|
+
style = new Style(map as any);
|
|
65
|
+
style.map.painter = {style, context} as any;
|
|
66
|
+
style.on('style.load', () => {
|
|
67
|
+
const source = createSource({url: '/source.json'});
|
|
68
|
+
server.respond();
|
|
69
|
+
style.addSource('terrain', source as any);
|
|
70
|
+
tsc = new TerrainSourceCache(style.sourceCaches.terrain);
|
|
71
|
+
done();
|
|
72
|
+
});
|
|
73
|
+
style.loadJSON({
|
|
74
|
+
'version': 8,
|
|
75
|
+
'sources': {},
|
|
76
|
+
'layers': []
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
afterAll(() => {
|
|
81
|
+
server.restore();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('#constructor', () => {
|
|
85
|
+
expect(tsc.sourceCache.usedForTerrain).toBeTruthy();
|
|
86
|
+
expect(tsc.sourceCache.tileSize).toBe(tsc.tileSize * 2 ** tsc.deltaZoom);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
});
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import {OverscaledTileID} from './tile_id';
|
|
2
|
+
import Tile from './tile';
|
|
3
|
+
import EXTENT from '../data/extent';
|
|
4
|
+
import {mat4} from 'gl-matrix';
|
|
5
|
+
import {Evented} from '../util/evented';
|
|
6
|
+
import type Transform from '../geo/transform';
|
|
7
|
+
import type SourceCache from '../source/source_cache';
|
|
8
|
+
import Painter from '../render/painter';
|
|
9
|
+
import Terrain from '../render/terrain';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* This class is a helper for the Terrain-class, it:
|
|
13
|
+
* - loads raster-dem tiles
|
|
14
|
+
* - manages all renderToTexture tiles.
|
|
15
|
+
* - caches previous rendered tiles.
|
|
16
|
+
* - finds all necessary renderToTexture tiles for a OverscaledTileID area
|
|
17
|
+
* - finds the corresponding raster-dem tile for OverscaledTileID
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export default class TerrainSourceCache extends Evented {
|
|
21
|
+
// source-cache for the raster-dem source.
|
|
22
|
+
sourceCache: SourceCache;
|
|
23
|
+
// stores all render-to-texture tiles.
|
|
24
|
+
_tiles: {[_: string]: Tile};
|
|
25
|
+
// contains a list of tileID-keys for the current scene. (only for performance)
|
|
26
|
+
_renderableTilesKeys: Array<string>;
|
|
27
|
+
// raster-dem-tile for a TileID cache.
|
|
28
|
+
_sourceTileCache: {[_: string]: string};
|
|
29
|
+
// minimum zoomlevel to render the terrain.
|
|
30
|
+
minzoom: number;
|
|
31
|
+
// maximum zoomlevel to render the terrain.
|
|
32
|
+
maxzoom: number;
|
|
33
|
+
// render-to-texture tileSize in scene.
|
|
34
|
+
tileSize: number;
|
|
35
|
+
// raster-dem tiles will load for performance the actualZoom - deltaZoom zoom-level.
|
|
36
|
+
deltaZoom: number;
|
|
37
|
+
// each time a render-to-texture tile is rendered, its tileID.key is stored into this array
|
|
38
|
+
renderHistory: Array<string>;
|
|
39
|
+
// maximal size of render-history
|
|
40
|
+
renderHistorySize: number;
|
|
41
|
+
|
|
42
|
+
constructor(sourceCache: SourceCache) {
|
|
43
|
+
super();
|
|
44
|
+
this.sourceCache = sourceCache;
|
|
45
|
+
this._tiles = {};
|
|
46
|
+
this._renderableTilesKeys = [];
|
|
47
|
+
this._sourceTileCache = {};
|
|
48
|
+
this.renderHistory = [];
|
|
49
|
+
this.minzoom = 0;
|
|
50
|
+
this.maxzoom = 22;
|
|
51
|
+
this.tileSize = 512;
|
|
52
|
+
this.deltaZoom = 1;
|
|
53
|
+
this.renderHistorySize = 150;
|
|
54
|
+
sourceCache.usedForTerrain = true;
|
|
55
|
+
sourceCache.tileSize = this.tileSize * 2 ** this.deltaZoom;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
destruct() {
|
|
59
|
+
this.sourceCache.usedForTerrain = false;
|
|
60
|
+
this.sourceCache.tileSize = null;
|
|
61
|
+
for (const key in this._tiles) {
|
|
62
|
+
const tile = this._tiles[key];
|
|
63
|
+
tile.textures.forEach(t => t.destroy());
|
|
64
|
+
tile.textures = [];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Load Terrain Tiles, create internal render-to-texture tiles, free GPU memory.
|
|
70
|
+
* @param {Transform} transform - the operation to do
|
|
71
|
+
* @param {Terrain} terrain - the terrain
|
|
72
|
+
*/
|
|
73
|
+
update(transform: Transform, terrain: Terrain): void {
|
|
74
|
+
// load raster-dem tiles for the current scene.
|
|
75
|
+
this.sourceCache.update(transform, terrain);
|
|
76
|
+
// create internal render-to-texture tiles for the current scene.
|
|
77
|
+
this._renderableTilesKeys = [];
|
|
78
|
+
for (const tileID of transform.coveringTiles({
|
|
79
|
+
tileSize: this.tileSize,
|
|
80
|
+
minzoom: this.minzoom,
|
|
81
|
+
maxzoom: this.maxzoom,
|
|
82
|
+
reparseOverscaled: false,
|
|
83
|
+
terrain
|
|
84
|
+
})) {
|
|
85
|
+
this._renderableTilesKeys.push(tileID.key);
|
|
86
|
+
if (!this._tiles[tileID.key]) {
|
|
87
|
+
tileID.posMatrix = new Float64Array(16) as any;
|
|
88
|
+
mat4.ortho(tileID.posMatrix, 0, EXTENT, 0, EXTENT, 0, 1);
|
|
89
|
+
this._tiles[tileID.key] = new Tile(tileID, this.tileSize);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* This method should called before each render-to-texture step to free old cached tiles
|
|
96
|
+
* @param {Painter} painter - the painter
|
|
97
|
+
*/
|
|
98
|
+
removeOutdated(painter: Painter) {
|
|
99
|
+
// create lookuptable for actual needed tiles
|
|
100
|
+
const tileIDs = {};
|
|
101
|
+
for (const key of this._renderableTilesKeys) tileIDs[key] = true;
|
|
102
|
+
// remove duplicates from renderHistory
|
|
103
|
+
this.renderHistory = this.renderHistory.filter((i, p) => this.renderHistory.indexOf(i) === p);
|
|
104
|
+
// free (GPU) memory from previously rendered not needed tiles
|
|
105
|
+
while (this.renderHistory.length > this.renderHistorySize) {
|
|
106
|
+
const tile = this.sourceCache._tiles[this.renderHistory.shift()];
|
|
107
|
+
if (tile && !tileIDs[tile.tileID.key]) {
|
|
108
|
+
tile.clearTextures(painter);
|
|
109
|
+
delete this.sourceCache._tiles[tile.tileID.key];
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* get a list of tiles, which are loaded and should be rendered in the current scene
|
|
116
|
+
* @returns {Array<Tile>} the renderable tiles
|
|
117
|
+
*/
|
|
118
|
+
getRenderableTiles(): Array<Tile> {
|
|
119
|
+
return this._renderableTilesKeys.map(key => this.getTileByID(key));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* get terrain tile by the TileID key
|
|
124
|
+
* @param id - the tile id
|
|
125
|
+
* @returns {Tile} - the tile
|
|
126
|
+
*/
|
|
127
|
+
getTileByID(id: string): Tile {
|
|
128
|
+
return this._tiles[id];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* searches for the corresponding current renderable terrain-tiles
|
|
133
|
+
* @param {OverscaledTileID} tileID - the tile to look for
|
|
134
|
+
* @returns {[_:string]: Tile} - the tiles that were found
|
|
135
|
+
*/
|
|
136
|
+
getTerrainCoords(tileID: OverscaledTileID): {[_: string]: OverscaledTileID} {
|
|
137
|
+
const coords = {};
|
|
138
|
+
for (const key of this._renderableTilesKeys) {
|
|
139
|
+
const _tileID = this._tiles[key].tileID;
|
|
140
|
+
if (_tileID.canonical.equals(tileID.canonical)) {
|
|
141
|
+
const coord = tileID.clone();
|
|
142
|
+
coord.posMatrix = new Float64Array(16) as any;
|
|
143
|
+
mat4.ortho(coord.posMatrix, 0, EXTENT, 0, EXTENT, 0, 1);
|
|
144
|
+
coords[key] = coord;
|
|
145
|
+
} else if (_tileID.canonical.isChildOf(tileID.canonical)) {
|
|
146
|
+
const coord = tileID.clone();
|
|
147
|
+
coord.posMatrix = new Float64Array(16) as any;
|
|
148
|
+
const dz = _tileID.canonical.z - tileID.canonical.z;
|
|
149
|
+
const dx = _tileID.canonical.x - (_tileID.canonical.x >> dz << dz);
|
|
150
|
+
const dy = _tileID.canonical.y - (_tileID.canonical.y >> dz << dz);
|
|
151
|
+
const size = EXTENT >> dz;
|
|
152
|
+
mat4.ortho(coord.posMatrix, 0, size, 0, size, 0, 1);
|
|
153
|
+
mat4.translate(coord.posMatrix, coord.posMatrix, [-dx * size, -dy * size, 0]);
|
|
154
|
+
coords[key] = coord;
|
|
155
|
+
} else if (tileID.canonical.isChildOf(_tileID.canonical)) {
|
|
156
|
+
const coord = tileID.clone();
|
|
157
|
+
coord.posMatrix = new Float64Array(16) as any;
|
|
158
|
+
const dz = tileID.canonical.z - _tileID.canonical.z;
|
|
159
|
+
const dx = tileID.canonical.x - (tileID.canonical.x >> dz << dz);
|
|
160
|
+
const dy = tileID.canonical.y - (tileID.canonical.y >> dz << dz);
|
|
161
|
+
const size = EXTENT >> dz;
|
|
162
|
+
mat4.ortho(coord.posMatrix, 0, EXTENT, 0, EXTENT, 0, 1);
|
|
163
|
+
mat4.translate(coord.posMatrix, coord.posMatrix, [dx * size, dy * size, 0]);
|
|
164
|
+
mat4.scale(coord.posMatrix, coord.posMatrix, [1 / (2 ** dz), 1 / (2 ** dz), 0]);
|
|
165
|
+
coords[key] = coord;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return coords;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* find the covering raster-dem tile
|
|
173
|
+
* @param {OverscaledTileID} tileID - the tile to look for
|
|
174
|
+
* @param {boolean} searchForDEM Optinal parameter to search for (parent) souretiles with loaded dem.
|
|
175
|
+
* @returns {Tile} - the tile
|
|
176
|
+
*/
|
|
177
|
+
getSourceTile(tileID: OverscaledTileID, searchForDEM?: boolean): Tile {
|
|
178
|
+
const source = this.sourceCache._source;
|
|
179
|
+
let z = tileID.overscaledZ - this.deltaZoom;
|
|
180
|
+
if (z > source.maxzoom) z = source.maxzoom;
|
|
181
|
+
if (z < source.minzoom) return null;
|
|
182
|
+
// cache for tileID to terrain-tileID
|
|
183
|
+
if (!this._sourceTileCache[tileID.key])
|
|
184
|
+
this._sourceTileCache[tileID.key] = tileID.scaledTo(z).key;
|
|
185
|
+
let tile = this.sourceCache.getTileByID(this._sourceTileCache[tileID.key]);
|
|
186
|
+
// during tile-loading phase look if parent tiles (with loaded dem) are available.
|
|
187
|
+
if (!(tile && tile.dem) && searchForDEM)
|
|
188
|
+
while (z > source.minzoom && !(tile && tile.dem))
|
|
189
|
+
tile = this.sourceCache.getTileByID(tileID.scaledTo(z--).key);
|
|
190
|
+
return tile;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* get a list of tiles, loaded after a spezific time. This is used to update depth & coords framebuffers.
|
|
195
|
+
* @param {Date} time - the time
|
|
196
|
+
* @returns {Array<Tile>} - the relevant tiles
|
|
197
|
+
*/
|
|
198
|
+
tilesAfterTime(time = Date.now()): Array<Tile> {
|
|
199
|
+
return Object.values(this._tiles).filter(t => t.timeLoaded >= time);
|
|
200
|
+
}
|
|
201
|
+
}
|
package/src/source/tile.ts
CHANGED
|
@@ -65,6 +65,7 @@ class Tile {
|
|
|
65
65
|
expiredRequestCount: number;
|
|
66
66
|
state: TileState;
|
|
67
67
|
timeAdded: any;
|
|
68
|
+
timeLoaded: any;
|
|
68
69
|
fadeEndTime: any;
|
|
69
70
|
collisionBoxArray: CollisionBoxArray;
|
|
70
71
|
redoWhenDone: boolean;
|
|
@@ -75,8 +76,10 @@ class Tile {
|
|
|
75
76
|
|
|
76
77
|
neighboringTiles: any;
|
|
77
78
|
dem: DEMData;
|
|
79
|
+
demMatrix: mat4;
|
|
78
80
|
aborted: boolean;
|
|
79
81
|
needsHillshadePrepare: boolean;
|
|
82
|
+
needsTerrainPrepare: boolean;
|
|
80
83
|
request: Cancelable;
|
|
81
84
|
texture: any;
|
|
82
85
|
fbo: Framebuffer;
|
|
@@ -90,6 +93,8 @@ class Tile {
|
|
|
90
93
|
hasSymbolBuckets: boolean;
|
|
91
94
|
hasRTLText: boolean;
|
|
92
95
|
dependencies: any;
|
|
96
|
+
textures: Array<Texture>;
|
|
97
|
+
textureCoords: {[_: string]: string}; // remeber all coords rendered to textures
|
|
93
98
|
|
|
94
99
|
/**
|
|
95
100
|
* @param {OverscaledTileID} tileID
|
|
@@ -107,6 +112,8 @@ class Tile {
|
|
|
107
112
|
this.hasSymbolBuckets = false;
|
|
108
113
|
this.hasRTLText = false;
|
|
109
114
|
this.dependencies = {};
|
|
115
|
+
this.textures = [];
|
|
116
|
+
this.textureCoords = {};
|
|
110
117
|
|
|
111
118
|
// Counts the number of times a response was already expired when
|
|
112
119
|
// received. We're using this to add a delay when making a new request
|
|
@@ -129,6 +136,14 @@ class Tile {
|
|
|
129
136
|
return this.state === 'errored' || this.state === 'loaded' || this.state === 'reloading';
|
|
130
137
|
}
|
|
131
138
|
|
|
139
|
+
clearTextures(painter: any) {
|
|
140
|
+
if (this.demTexture) painter.saveTileTexture(this.demTexture);
|
|
141
|
+
this.textures.forEach(t => painter.saveTileTexture(t));
|
|
142
|
+
this.demTexture = null;
|
|
143
|
+
this.textures = [];
|
|
144
|
+
this.textureCoords = {};
|
|
145
|
+
}
|
|
146
|
+
|
|
132
147
|
/**
|
|
133
148
|
* Given a data object with a 'buffers' property, load it into
|
|
134
149
|
* this tile's elementGroups and buffers properties and set loaded
|
package/src/source/tile_id.ts
CHANGED
|
@@ -42,6 +42,11 @@ export class CanonicalTileID {
|
|
|
42
42
|
.replace(/{bbox-epsg-3857}/g, bbox);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
isChildOf(parent: CanonicalTileID) {
|
|
46
|
+
const dz = this.z - parent.z;
|
|
47
|
+
return dz > 0 && parent.x === (this.x >> dz) && parent.y === (this.y >> dz);
|
|
48
|
+
}
|
|
49
|
+
|
|
45
50
|
getTilePoint(coord: MercatorCoordinate) {
|
|
46
51
|
const tilesAtZoom = Math.pow(2, this.z);
|
|
47
52
|
return new Point(
|
|
@@ -81,6 +86,10 @@ export class OverscaledTileID {
|
|
|
81
86
|
this.key = calculateKey(wrap, overscaledZ, z, x, y);
|
|
82
87
|
}
|
|
83
88
|
|
|
89
|
+
clone() {
|
|
90
|
+
return new OverscaledTileID(this.overscaledZ, this.wrap, this.canonical.z, this.canonical.x, this.canonical.y);
|
|
91
|
+
}
|
|
92
|
+
|
|
84
93
|
equals(id: OverscaledTileID) {
|
|
85
94
|
return this.overscaledZ === id.overscaledZ && this.wrap === id.wrap && this.canonical.equals(id.canonical);
|
|
86
95
|
}
|
|
@@ -7,6 +7,7 @@ import type StyleLayer from './style_layer';
|
|
|
7
7
|
import type SymbolStyleLayer from './style_layer/symbol_style_layer';
|
|
8
8
|
import type Tile from '../source/tile';
|
|
9
9
|
import type {BucketPart} from '../symbol/placement';
|
|
10
|
+
import Terrain from '../render/terrain';
|
|
10
11
|
|
|
11
12
|
class LayerPlacement {
|
|
12
13
|
_sortAcrossTiles: boolean;
|
|
@@ -69,6 +70,7 @@ class PauseablePlacement {
|
|
|
69
70
|
|
|
70
71
|
constructor(
|
|
71
72
|
transform: Transform,
|
|
73
|
+
terrain: Terrain,
|
|
72
74
|
order: Array<string>,
|
|
73
75
|
forceFullPlacement: boolean,
|
|
74
76
|
showCollisionBoxes: boolean,
|
|
@@ -76,7 +78,7 @@ class PauseablePlacement {
|
|
|
76
78
|
crossSourceCollisions: boolean,
|
|
77
79
|
prevPlacement?: Placement
|
|
78
80
|
) {
|
|
79
|
-
this.placement = new Placement(transform, fadeDuration, crossSourceCollisions, prevPlacement);
|
|
81
|
+
this.placement = new Placement(transform, terrain, fadeDuration, crossSourceCollisions, prevPlacement);
|
|
80
82
|
this._currentPlacementIndex = order.length - 1;
|
|
81
83
|
this._forceFullPlacement = forceFullPlacement;
|
|
82
84
|
this._showCollisionBoxes = showCollisionBoxes;
|
package/src/style/style.test.ts
CHANGED
|
@@ -379,6 +379,22 @@ describe('Style#loadJSON', () => {
|
|
|
379
379
|
style._layers.background.fire(new Event('error', {mapLibre: true}));
|
|
380
380
|
});
|
|
381
381
|
});
|
|
382
|
+
|
|
383
|
+
test('sets terrain if defined', (done) => {
|
|
384
|
+
const map = getStubMap();
|
|
385
|
+
const style = new Style(map);
|
|
386
|
+
map.transform.updateElevation = jest.fn();
|
|
387
|
+
style.loadJSON(createStyleJSON({
|
|
388
|
+
sources: {'source-id': createGeoJSONSource()},
|
|
389
|
+
terrain: {source: 'source-id', exaggeration: 0.33}
|
|
390
|
+
}));
|
|
391
|
+
|
|
392
|
+
style.on('style.load', () => {
|
|
393
|
+
expect(style.terrain).toBeDefined();
|
|
394
|
+
expect(map.transform.updateElevation).toHaveBeenCalled();
|
|
395
|
+
done();
|
|
396
|
+
});
|
|
397
|
+
});
|
|
382
398
|
});
|
|
383
399
|
|
|
384
400
|
describe('Style#_remove', () => {
|