@deck.gl/geo-layers 8.6.0-alpha.3 → 8.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/dist.dev.js +4574 -622
  2. package/dist/es5/h3-layers/h3-hexagon-layer.js +61 -46
  3. package/dist/es5/h3-layers/h3-hexagon-layer.js.map +1 -1
  4. package/dist/es5/mvt-layer/mvt-layer.js +2 -2
  5. package/dist/es5/mvt-layer/mvt-layer.js.map +1 -1
  6. package/dist/es5/tile-3d-layer/tile-3d-layer.js +1 -1
  7. package/dist/es5/tile-3d-layer/tile-3d-layer.js.map +1 -1
  8. package/dist/es5/tile-layer/tile-2d-header.js +70 -55
  9. package/dist/es5/tile-layer/tile-2d-header.js.map +1 -1
  10. package/dist/es5/tile-layer/tile-2d-traversal.js +42 -4
  11. package/dist/es5/tile-layer/tile-2d-traversal.js.map +1 -1
  12. package/dist/es5/tile-layer/tile-layer.js +31 -33
  13. package/dist/es5/tile-layer/tile-layer.js.map +1 -1
  14. package/dist/es5/tile-layer/tileset-2d.js +102 -67
  15. package/dist/es5/tile-layer/tileset-2d.js.map +1 -1
  16. package/dist/es5/tile-layer/utils.js +1 -1
  17. package/dist/es5/tile-layer/utils.js.map +1 -1
  18. package/dist/esm/h3-layers/h3-hexagon-layer.js +52 -38
  19. package/dist/esm/h3-layers/h3-hexagon-layer.js.map +1 -1
  20. package/dist/esm/mvt-layer/mvt-layer.js +2 -4
  21. package/dist/esm/mvt-layer/mvt-layer.js.map +1 -1
  22. package/dist/esm/tile-3d-layer/tile-3d-layer.js +1 -1
  23. package/dist/esm/tile-3d-layer/tile-3d-layer.js.map +1 -1
  24. package/dist/esm/tile-layer/tile-2d-header.js +43 -31
  25. package/dist/esm/tile-layer/tile-2d-header.js.map +1 -1
  26. package/dist/esm/tile-layer/tile-2d-traversal.js +23 -1
  27. package/dist/esm/tile-layer/tile-2d-traversal.js.map +1 -1
  28. package/dist/esm/tile-layer/tile-layer.js +29 -33
  29. package/dist/esm/tile-layer/tile-layer.js.map +1 -1
  30. package/dist/esm/tile-layer/tileset-2d.js +34 -11
  31. package/dist/esm/tile-layer/tileset-2d.js.map +1 -1
  32. package/dist/esm/tile-layer/utils.js +1 -1
  33. package/dist/esm/tile-layer/utils.js.map +1 -1
  34. package/dist.min.js +2 -2
  35. package/package.json +3 -3
  36. package/src/h3-layers/h3-hexagon-layer.js +47 -37
  37. package/src/mvt-layer/mvt-layer.js +2 -2
  38. package/src/tile-3d-layer/tile-3d-layer.js +1 -1
  39. package/src/tile-layer/tile-2d-header.js +43 -32
  40. package/src/tile-layer/tile-2d-traversal.js +28 -2
  41. package/src/tile-layer/tile-layer.js +33 -34
  42. package/src/tile-layer/tileset-2d.js +29 -15
  43. package/src/tile-layer/utils.js +1 -1
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@deck.gl/geo-layers",
3
3
  "description": "deck.gl layers supporting geospatial use cases and GIS formats",
4
4
  "license": "MIT",
5
- "version": "8.6.0-alpha.3",
5
+ "version": "8.6.1",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
@@ -35,7 +35,7 @@
35
35
  "@loaders.gl/mvt": "^3.0.8",
36
36
  "@loaders.gl/terrain": "^3.0.8",
37
37
  "@loaders.gl/tiles": "^3.0.8",
38
- "@luma.gl/experimental": "^8.5.5",
38
+ "@luma.gl/experimental": "^8.5.10",
39
39
  "@math.gl/culling": "^3.5.4",
40
40
  "@math.gl/web-mercator": "^3.5.4",
41
41
  "h3-js": "^3.6.0",
@@ -49,5 +49,5 @@
49
49
  "@deck.gl/mesh-layers": "^8.0.0",
50
50
  "@loaders.gl/core": "^3.0.0"
51
51
  },
52
- "gitHead": "b43ac51af23cb186e3cbb2ed9c69fddd16483745"
52
+ "gitHead": "9611fa7fc206cf2970ac4dfdb35a50677b0afa3c"
53
53
  }
@@ -91,7 +91,7 @@ function mergeTriggers(getHexagon, coverage) {
91
91
 
92
92
  const defaultProps = {
93
93
  ...PolygonLayer.defaultProps,
94
- highPrecision: false,
94
+ highPrecision: 'auto',
95
95
  coverage: {type: 'number', min: 0, max: 1, value: 1},
96
96
  centerHexagon: null,
97
97
  getHexagon: {type: 'accessor', value: x => x.hexagon},
@@ -121,49 +121,59 @@ export default class H3HexagonLayer extends CompositeLayer {
121
121
 
122
122
  updateState({props, oldProps, changeFlags}) {
123
123
  if (
124
- changeFlags.dataChanged ||
125
- (changeFlags.updateTriggers && changeFlags.updateTriggers.getHexagon)
124
+ props.highPrecision !== true &&
125
+ (changeFlags.dataChanged ||
126
+ (changeFlags.updateTriggers && changeFlags.updateTriggers.getHexagon))
126
127
  ) {
127
- let resolution = -1;
128
- let hasPentagon = false;
129
- let hasMultipleRes = false;
130
- const {iterable, objectInfo} = createIterable(props.data);
131
- for (const object of iterable) {
132
- objectInfo.index++;
133
- const hexId = props.getHexagon(object, objectInfo);
134
- // Take the resolution of the first hex
135
- const hexResolution = h3GetResolution(hexId);
136
- if (resolution < 0) resolution = hexResolution;
137
- else if (resolution !== hexResolution) {
138
- hasMultipleRes = true;
139
- break;
140
- }
141
- if (h3IsPentagon(hexId)) {
142
- hasPentagon = true;
143
- break;
144
- }
145
- }
146
- this.setState({
147
- resolution,
148
- edgeLengthKM: resolution >= 0 ? edgeLength(resolution, UNITS.km) : 0,
149
- hasMultipleRes,
150
- hasPentagon
151
- });
128
+ const dataProps = this._calculateH3DataProps(props);
129
+ this.setState(dataProps);
152
130
  }
153
131
 
154
132
  this._updateVertices(this.context.viewport);
155
133
  }
156
134
 
135
+ _calculateH3DataProps(props) {
136
+ let resolution = -1;
137
+ let hasPentagon = false;
138
+ let hasMultipleRes = false;
139
+
140
+ const {iterable, objectInfo} = createIterable(props.data);
141
+ for (const object of iterable) {
142
+ objectInfo.index++;
143
+ const hexId = props.getHexagon(object, objectInfo);
144
+ // Take the resolution of the first hex
145
+ const hexResolution = h3GetResolution(hexId);
146
+ if (resolution < 0) {
147
+ resolution = hexResolution;
148
+ if (!props.highPrecision) break;
149
+ } else if (resolution !== hexResolution) {
150
+ hasMultipleRes = true;
151
+ break;
152
+ }
153
+ if (h3IsPentagon(hexId)) {
154
+ hasPentagon = true;
155
+ break;
156
+ }
157
+ }
158
+
159
+ return {
160
+ resolution,
161
+ edgeLengthKM: resolution >= 0 ? edgeLength(resolution, UNITS.km) : 0,
162
+ hasMultipleRes,
163
+ hasPentagon
164
+ };
165
+ }
166
+
157
167
  _shouldUseHighPrecision() {
158
- const {resolution, hasPentagon, hasMultipleRes} = this.state;
159
- const {viewport} = this.context;
160
- return (
161
- this.props.highPrecision ||
162
- viewport.resolution ||
163
- hasMultipleRes ||
164
- hasPentagon ||
165
- (resolution >= 0 && resolution <= 5)
166
- );
168
+ if (this.props.highPrecision === 'auto') {
169
+ const {resolution, hasPentagon, hasMultipleRes} = this.state;
170
+ const {viewport} = this.context;
171
+ return (
172
+ viewport.resolution || hasMultipleRes || hasPentagon || (resolution >= 0 && resolution <= 5)
173
+ );
174
+ }
175
+
176
+ return this.props.highPrecision;
167
177
  }
168
178
 
169
179
  _updateVertices(viewport) {
@@ -18,7 +18,7 @@ const defaultProps = {
18
18
  uniqueIdProperty: {type: 'string', value: ''},
19
19
  highlightedFeatureId: null,
20
20
  loaders: [MVTWorkerLoader],
21
- binary: false
21
+ binary: true
22
22
  };
23
23
 
24
24
  export default class MVTLayer extends TileLayer {
@@ -198,7 +198,7 @@ export default class MVTLayer extends TileLayer {
198
198
  getHighlightedObjectIndex(tile) {
199
199
  const {hoveredFeatureId, hoveredFeatureLayerName} = this.state;
200
200
  const {uniqueIdProperty, highlightedFeatureId, binary} = this.props;
201
- const {data} = tile;
201
+ const data = tile.content;
202
202
 
203
203
  const isHighlighted = isFeatureIdDefined(highlightedFeatureId);
204
204
  const isFeatureIdPresent = isFeatureIdDefined(hoveredFeatureId) || isHighlighted;
@@ -99,7 +99,7 @@ export default class Tile3DLayer extends CompositeLayer {
99
99
  filterSubLayer({layer, viewport}) {
100
100
  const {tile} = layer.props;
101
101
  const {id: viewportId} = viewport;
102
- return this.props.visible && tile.selected && tile.viewportIds.includes(viewportId);
102
+ return tile.selected && tile.viewportIds.includes(viewportId);
103
103
  }
104
104
 
105
105
  _updateAutoHighlight(info) {
@@ -2,7 +2,7 @@
2
2
  import {log} from '@deck.gl/core';
3
3
 
4
4
  export default class Tile2DHeader {
5
- constructor({x, y, z, onTileLoad, onTileError}) {
5
+ constructor({x, y, z}) {
6
6
  this.x = x;
7
7
  this.y = y;
8
8
  this.z = z;
@@ -12,27 +12,27 @@ export default class Tile2DHeader {
12
12
  this.children = [];
13
13
 
14
14
  this.content = null;
15
+
16
+ this._loaderId = 0;
15
17
  this._isLoaded = false;
16
18
  this._isCancelled = false;
17
-
18
- this.onTileLoad = onTileLoad;
19
- this.onTileError = onTileError;
19
+ this._needsReload = false;
20
20
  }
21
21
 
22
22
  get data() {
23
- return this._isLoaded ? this.content : this._loader;
23
+ return this.isLoading ? this._loader.then(() => this.data) : this.content;
24
24
  }
25
25
 
26
26
  get isLoaded() {
27
- return this._isLoaded;
27
+ return this._isLoaded && !this._needsReload;
28
28
  }
29
29
 
30
30
  get isLoading() {
31
- return Boolean(this._loader);
31
+ return Boolean(this._loader) && !this._isCancelled;
32
32
  }
33
33
 
34
- get isCancelled() {
35
- return this._isCancelled;
34
+ get needsReload() {
35
+ return this._needsReload || this._isCancelled;
36
36
  }
37
37
 
38
38
  get byteLength() {
@@ -44,8 +44,9 @@ export default class Tile2DHeader {
44
44
  }
45
45
 
46
46
  /* eslint-disable max-statements */
47
- async _loadData(getTileData, requestScheduler) {
47
+ async _loadData({getData, requestScheduler, onLoad, onError}) {
48
48
  const {x, y, z, bbox} = this;
49
+ const loaderId = this._loaderId;
49
50
 
50
51
  this._abortController = new AbortController(); // eslint-disable-line no-undef
51
52
  const {signal} = this._abortController;
@@ -64,47 +65,57 @@ export default class Tile2DHeader {
64
65
  return;
65
66
  }
66
67
 
67
- let tileData;
68
+ let tileData = null;
68
69
  let error;
69
70
  try {
70
- tileData = await getTileData({x, y, z, bbox, signal});
71
+ tileData = await getData({x, y, z, bbox, signal});
71
72
  } catch (err) {
72
73
  error = err || true;
73
74
  } finally {
74
75
  requestToken.done();
75
-
76
- if (this._isCancelled && !tileData) {
77
- this._isLoaded = false;
78
- } else {
79
- // Consider it loaded if we tried to cancel but `getTileData` still returned data
80
- this._isLoaded = true;
81
- this._isCancelled = false;
82
- }
83
76
  }
84
77
 
85
- if (!this._isLoaded) {
78
+ // If loadData has been called with a newer version, discard the result from this operation
79
+ if (loaderId !== this._loaderId) {
86
80
  return;
87
81
  }
82
+ // Clear the `isLoading` flag
83
+ this._loader = undefined;
84
+ // Rewrite tile content with the result of getTileData if successful, or `null` in case of
85
+ // error or cancellation
86
+ this.content = tileData;
87
+ // If cancelled, do not invoke the callbacks
88
+ // Consider it loaded if we tried to cancel but `getTileData` still returned data
89
+ if (this._isCancelled && !tileData) {
90
+ this._isLoaded = false;
91
+ return;
92
+ }
93
+ this._isLoaded = true;
94
+ this._isCancelled = false;
88
95
 
89
96
  if (error) {
90
- this.onTileError(error, this);
97
+ onError(error, this);
91
98
  } else {
92
- this.content = tileData;
93
- this.onTileLoad(this);
99
+ onLoad(this);
94
100
  }
95
101
  }
96
102
  /* eslint-enable max-statements */
97
103
 
98
- loadData(getTileData, requestScheduler) {
99
- if (!getTileData) {
100
- return;
101
- }
102
-
104
+ loadData(opts) {
105
+ this._isLoaded = false;
103
106
  this._isCancelled = false;
104
- this._loader = this._loadData(getTileData, requestScheduler);
105
- this._loader.finally(() => {
107
+ this._needsReload = false;
108
+ this._loaderId++;
109
+ this._loader = this._loadData(opts);
110
+ return this._loader;
111
+ }
112
+
113
+ setNeedsReload() {
114
+ if (this.isLoading) {
115
+ this.abort();
106
116
  this._loader = undefined;
107
- });
117
+ }
118
+ this._needsReload = true;
108
119
  }
109
120
 
110
121
  abort() {
@@ -5,6 +5,7 @@ import {
5
5
  AxisAlignedBoundingBox,
6
6
  makeOrientedBoundingBoxFromPoints
7
7
  } from '@math.gl/culling';
8
+ import {lngLatToWorld} from '@math.gl/web-mercator';
8
9
  import {osmTile2lngLat} from './utils';
9
10
 
10
11
  const TILE_SIZE = 512;
@@ -38,10 +39,14 @@ class OSMNode {
38
39
  }
39
40
 
40
41
  update(params) {
41
- const {viewport, cullingVolume, elevationBounds, minZ, maxZ, offset, project} = params;
42
+ const {viewport, cullingVolume, elevationBounds, minZ, maxZ, bounds, offset, project} = params;
42
43
  const boundingVolume = this.getBoundingVolume(elevationBounds, offset, project);
43
44
 
44
45
  // First, check if this tile is visible
46
+ if (bounds && !this.insideBounds(bounds)) {
47
+ return false;
48
+ }
49
+
45
50
  const isInside = cullingVolume.computeVisibility(boundingVolume);
46
51
  if (isInside < 0) {
47
52
  return false;
@@ -85,6 +90,18 @@ class OSMNode {
85
90
  return result;
86
91
  }
87
92
 
93
+ insideBounds([minX, minY, maxX, maxY]) {
94
+ const scale = Math.pow(2, this.z);
95
+ const extent = TILE_SIZE / scale;
96
+
97
+ return (
98
+ this.x * extent < maxX &&
99
+ this.y * extent < maxY &&
100
+ (this.x + 1) * extent > minX &&
101
+ (this.y + 1) * extent > minY
102
+ );
103
+ }
104
+
88
105
  getBoundingVolume(zRange, worldOffset, project) {
89
106
  if (project) {
90
107
  // Custom projection
@@ -123,7 +140,7 @@ class OSMNode {
123
140
  }
124
141
  }
125
142
 
126
- export function getOSMTileIndices(viewport, maxZ, zRange) {
143
+ export function getOSMTileIndices(viewport, maxZ, zRange, bounds) {
127
144
  const project = viewport.resolution ? viewport.projectPosition : null;
128
145
 
129
146
  // Get the culling volume of the current camera
@@ -140,6 +157,14 @@ export function getOSMTileIndices(viewport, maxZ, zRange) {
140
157
  // Always load at the current zoom level if pitch is small
141
158
  const minZ = viewport.pitch <= 60 ? maxZ : 0;
142
159
 
160
+ // Map extent to OSM position
161
+ if (bounds) {
162
+ const [minLng, minLat, maxLng, maxLat] = bounds;
163
+ const topLeft = lngLatToWorld([minLng, maxLat]);
164
+ const bottomRight = lngLatToWorld([maxLng, minLat]);
165
+ bounds = [topLeft[0], TILE_SIZE - topLeft[1], bottomRight[0], TILE_SIZE - bottomRight[1]];
166
+ }
167
+
143
168
  const root = new OSMNode(0, 0, 0);
144
169
  const traversalParams = {
145
170
  viewport,
@@ -148,6 +173,7 @@ export function getOSMTileIndices(viewport, maxZ, zRange) {
148
173
  elevationBounds: [elevationMin, elevationMax],
149
174
  minZ,
150
175
  maxZ,
176
+ bounds,
151
177
  // num. of worlds from the center. For repeated maps
152
178
  offset: 0
153
179
  };
@@ -42,7 +42,7 @@ export default class TileLayer extends CompositeLayer {
42
42
  get isLoaded() {
43
43
  const {tileset} = this.state;
44
44
  return tileset.selectedTiles.every(
45
- tile => tile.layers && tile.layers.every(layer => layer.isLoaded)
45
+ tile => tile.isLoaded && tile.layers && tile.layers.every(layer => layer.isLoaded)
46
46
  );
47
47
  }
48
48
 
@@ -52,30 +52,28 @@ export default class TileLayer extends CompositeLayer {
52
52
 
53
53
  updateState({props, changeFlags}) {
54
54
  let {tileset} = this.state;
55
- const createTileCache =
56
- !tileset ||
55
+ const propsChanged = changeFlags.propsOrDataChanged || changeFlags.updateTriggersChanged;
56
+ const dataChanged =
57
57
  changeFlags.dataChanged ||
58
58
  (changeFlags.updateTriggersChanged &&
59
59
  (changeFlags.updateTriggersChanged.all || changeFlags.updateTriggersChanged.getTileData));
60
60
 
61
- if (createTileCache) {
62
- if (tileset) {
63
- tileset.finalize();
64
- }
65
- tileset = new Tileset2D({
66
- ...this._getTilesetOptions(props),
67
- getTileData: this.getTileData.bind(this),
68
- onTileLoad: this._onTileLoad.bind(this),
69
- onTileError: this._onTileError.bind(this),
70
- onTileUnload: this._onTileUnload.bind(this)
71
- });
61
+ if (!tileset) {
62
+ tileset = new Tileset2D(this._getTilesetOptions(props));
72
63
  this.setState({tileset});
73
- } else if (changeFlags.propsChanged || changeFlags.updateTriggersChanged) {
64
+ } else if (propsChanged) {
74
65
  tileset.setOptions(this._getTilesetOptions(props));
75
- // if any props changed, delete the cached layers
76
- this.state.tileset.tiles.forEach(tile => {
77
- tile.layers = null;
78
- });
66
+
67
+ if (dataChanged) {
68
+ // reload all tiles
69
+ // use cached layers until new content is loaded
70
+ tileset.reloadAll();
71
+ } else {
72
+ // some render options changed, regenerate sub layers now
73
+ this.state.tileset.tiles.forEach(tile => {
74
+ tile.layers = null;
75
+ });
76
+ }
79
77
  }
80
78
 
81
79
  this._updateTileset();
@@ -103,7 +101,12 @@ export default class TileLayer extends CompositeLayer {
103
101
  refinementStrategy,
104
102
  extent,
105
103
  maxRequests,
106
- zoomOffset
104
+ zoomOffset,
105
+
106
+ getTileData: this.getTileData.bind(this),
107
+ onTileLoad: this._onTileLoad.bind(this),
108
+ onTileError: this._onTileError.bind(this),
109
+ onTileUnload: this._onTileUnload.bind(this)
107
110
  };
108
111
  }
109
112
 
@@ -138,8 +141,8 @@ export default class TileLayer extends CompositeLayer {
138
141
  }
139
142
 
140
143
  _onTileLoad(tile) {
141
- const layer = this.getCurrentLayer();
142
- layer.props.onTileLoad(tile);
144
+ this.props.onTileLoad(tile);
145
+ tile.layers = null;
143
146
 
144
147
  if (tile.isVisible) {
145
148
  this.setNeedsUpdate();
@@ -147,10 +150,8 @@ export default class TileLayer extends CompositeLayer {
147
150
  }
148
151
 
149
152
  _onTileError(error, tile) {
150
- const layer = this.getCurrentLayer();
151
- layer.props.onTileError(error);
152
- // errorred tiles should not block rendering, are considered "loaded" with empty data
153
- layer._updateTileset();
153
+ this.props.onTileError(error);
154
+ tile.layers = null;
154
155
 
155
156
  if (tile.isVisible) {
156
157
  this.setNeedsUpdate();
@@ -158,15 +159,13 @@ export default class TileLayer extends CompositeLayer {
158
159
  }
159
160
 
160
161
  _onTileUnload(tile) {
161
- const layer = this.getCurrentLayer();
162
- layer.props.onTileUnload(tile);
162
+ this.props.onTileUnload(tile);
163
163
  }
164
164
 
165
165
  // Methods for subclass to override
166
166
 
167
167
  getTileData(tile) {
168
- const {data} = this.props;
169
- const {getTileData, fetch} = this.getCurrentLayer().props;
168
+ const {data, getTileData, fetch} = this.props;
170
169
  const {signal} = tile;
171
170
 
172
171
  tile.url = getURLFromTemplate(data, tile);
@@ -203,13 +202,13 @@ export default class TileLayer extends CompositeLayer {
203
202
  return this.state.tileset.tiles.map(tile => {
204
203
  const highlightedObjectIndex = this.getHighlightedObjectIndex(tile);
205
204
  // cache the rendered layer in the tile
206
- if (!tile.isLoaded) {
207
- // no op
205
+ if (!tile.isLoaded && !tile.content) {
206
+ // nothing to show
208
207
  } else if (!tile.layers) {
209
208
  const layers = this.renderSubLayers({
210
209
  ...this.props,
211
210
  id: `${this.id}-${tile.x}-${tile.y}-${tile.z}`,
212
- data: tile.data,
211
+ data: tile.content,
213
212
  _offset: 0,
214
213
  tile
215
214
  });
@@ -230,7 +229,7 @@ export default class TileLayer extends CompositeLayer {
230
229
  }
231
230
 
232
231
  filterSubLayer({layer}) {
233
- return this.props.visible && layer.props.tile.isVisible;
232
+ return layer.props.tile.isVisible;
234
233
  }
235
234
  }
236
235
 
@@ -46,17 +46,14 @@ export default class Tileset2D {
46
46
  */
47
47
  constructor(opts) {
48
48
  this.opts = opts;
49
- this._getTileData = opts.getTileData;
50
49
 
51
- this.onTileError = opts.onTileError;
52
50
  this.onTileLoad = tile => {
53
- opts.onTileLoad(tile);
51
+ this.opts.onTileLoad(tile);
54
52
  if (this.opts.maxCacheByteSize) {
55
53
  this._cacheByteSize += tile.byteLength;
56
54
  this._resizeCache();
57
55
  }
58
56
  };
59
- this.onTileUnload = opts.onTileUnload;
60
57
 
61
58
  this._requestScheduler = new RequestScheduler({
62
59
  maxRequests: opts.maxRequests,
@@ -107,6 +104,20 @@ export default class Tileset2D {
107
104
  tile.abort();
108
105
  }
109
106
  }
107
+ this._cache.clear();
108
+ this._tiles = [];
109
+ this._selectedTiles = null;
110
+ }
111
+
112
+ reloadAll() {
113
+ for (const tileId of this._cache.keys()) {
114
+ const tile = this._cache.get(tileId);
115
+ if (!this._selectedTiles.includes(tile)) {
116
+ this._cache.delete(tileId);
117
+ } else {
118
+ tile.setNeedsReload();
119
+ }
120
+ }
110
121
  }
111
122
 
112
123
  /**
@@ -304,7 +315,7 @@ export default class Tileset2D {
304
315
  // delete tile
305
316
  this._cacheByteSize -= opts.maxCacheByteSize ? tile.byteLength : 0;
306
317
  _cache.delete(tileId);
307
- this.onTileUnload(tile);
318
+ this.opts.onTileUnload(tile);
308
319
  }
309
320
  if (_cache.size <= maxCacheSize && this._cacheByteSize <= maxCacheByteSize) {
310
321
  break;
@@ -326,21 +337,24 @@ export default class Tileset2D {
326
337
  _getTile({x, y, z}, create) {
327
338
  const tileId = `${x},${y},${z}`;
328
339
  let tile = this._cache.get(tileId);
340
+ let needsReload = false;
329
341
 
330
342
  if (!tile && create) {
331
- tile = new Tile2DHeader({
332
- x,
333
- y,
334
- z,
335
- onTileLoad: this.onTileLoad,
336
- onTileError: this.onTileError
337
- });
343
+ tile = new Tile2DHeader({x, y, z});
338
344
  Object.assign(tile, this.getTileMetadata(tile));
339
- tile.loadData(this._getTileData, this._requestScheduler);
345
+ needsReload = true;
340
346
  this._cache.set(tileId, tile);
341
347
  this._dirty = true;
342
- } else if (tile && tile.isCancelled && !tile.isLoading) {
343
- tile.loadData(this._getTileData, this._requestScheduler);
348
+ } else if (tile && tile.needsReload) {
349
+ needsReload = true;
350
+ }
351
+ if (needsReload) {
352
+ tile.loadData({
353
+ getData: this.opts.getTileData,
354
+ requestScheduler: this._requestScheduler,
355
+ onLoad: this.onTileLoad,
356
+ onError: this.opts.onTileError
357
+ });
344
358
  }
345
359
 
346
360
  return tile;
@@ -197,7 +197,7 @@ export function getTileIndices({
197
197
  transformedExtent = transformBox(extent, modelMatrix);
198
198
  }
199
199
  return viewport.isGeospatial
200
- ? getOSMTileIndices(viewport, z, zRange, extent || DEFAULT_EXTENT)
200
+ ? getOSMTileIndices(viewport, z, zRange, extent)
201
201
  : getIdentityTileIndices(
202
202
  viewport,
203
203
  z,