Skip to content

Commit fa20b96

Browse files
committed
Merge branch 'tile-throttle'
2 parents 8fe5ce7 + 3450431 commit fa20b96

File tree

5 files changed

+132
-52
lines changed

5 files changed

+132
-52
lines changed

src/core/util/LRUCache.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
const isMapSupported = typeof Map === 'function';
2+
3+
const nullOnRemove = () => {};
24
/**
35
* from mapbox-gl-js
46
* A [least-recently-used cache](http://en.wikipedia.org/wiki/Cache_algorithms)
@@ -14,7 +16,7 @@ class ArrayLRUCache {
1416
*/
1517
constructor(max, onRemove) {
1618
this.max = max;
17-
this.onRemove = onRemove;
19+
this.onRemove = onRemove || nullOnRemove;
1820
this.reset();
1921
}
2022

@@ -160,7 +162,7 @@ if (isMapSupported) {
160162
MapLRUCache = class {
161163
constructor(max, onRemove) {
162164
this.max = max;
163-
this.onRemove = onRemove;
165+
this.onRemove = onRemove || nullOnRemove;
164166
this.reset();
165167
}
166168

src/layer/tile/TileLayer.js

Lines changed: 63 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
isString,
1010
extend
1111
} from '../../core/util';
12+
import LRUCache from '../../core/util/LRUCache';
1213
import Browser from '../../core/Browser';
1314
import Size from '../../geo/Size';
1415
import Point from '../../geo/Point';
@@ -137,7 +138,11 @@ const options = {
137138

138139
'pyramidMode': 1,
139140

140-
'decodeImageInWorker': false
141+
'decodeImageInWorker': false,
142+
143+
'tileLimitPerFrame': 0,
144+
145+
'backZoomOffset': 0
141146
};
142147

143148
const URL_PATTERN = /\{ *([\w_]+) *\}/g;
@@ -289,7 +294,8 @@ class TileLayer extends Layer {
289294
id: this._getTileId(i, y, z),
290295
url: this.getTileUrl(i, y, z + this.options['zoomOffset']),
291296
offset: [0, 0],
292-
error: error
297+
error: error,
298+
children: []
293299
});
294300
}
295301
}
@@ -374,6 +380,7 @@ class TileLayer extends Layer {
374380
const offsets = {
375381
0: offset0
376382
};
383+
const preservedZoom = this.options['backZoomOffset'] + z;
377384
const extent = new PointExtent();
378385
const tiles = [];
379386
while (queue.length > 0) {
@@ -387,6 +394,10 @@ class TileLayer extends Layer {
387394
offsets[node.z + 1] = this._getTileOffset(node.z + 1);
388395
}
389396
this._splitNode(node, projectionView, queue, tiles, extent, maxZoom, offsets[node.z + 1], layer && layer.getRenderer(), glRes);
397+
if (preservedZoom < z && tiles[tiles.length - 1] !== node && preservedZoom === node.z) {
398+
// extent._combine(node.extent2d);
399+
tiles.push(node);
400+
}
390401
}
391402
return {
392403
tileGrids: [
@@ -429,37 +440,52 @@ class TileLayer extends Layer {
429440
const childIdx = (idx << 1) + dx;
430441
const childIdy = (idy << 1) + dy;
431442

432-
const tileId = this._getTileId(childIdx, childIdy, z);
443+
// const tileId = this._getTileId(childIdx, childIdy, z);
444+
if (!node.children) {
445+
node.children = [];
446+
}
447+
let tileId = node.children[i];
448+
if (!tileId) {
449+
tileId = this._getTileId(childIdx, childIdy, z);
450+
node.children[i] = tileId;
451+
}
433452
const cached = renderer.isTileCachedOrLoading(tileId);
434453
let extent;
435-
if (!cached) {
436-
if (scaleY < 0) {
437-
const nwx = minx + dx * width;
438-
const nwy = maxy - dy * height;
439-
// extent2d 是 node.z 级别上的 extent
440-
extent = new PointExtent(nwx, nwy - height, nwx + width, nwy);
441-
442-
} else {
443-
const swx = minx + dx * width;
444-
const swy = miny + dy * height;
445-
extent = new PointExtent(swx, swy, swx + width, swy + height);
446-
}
447-
}
448454
let childNode = cached && cached.info;
449455
if (!childNode) {
450-
childNode = {
451-
x: childX,
452-
y: childY,
453-
idx: childIdx,
454-
idy: childIdy,
455-
z,
456-
extent2d: extent,
457-
error: node.error / 2,
458-
res,
459-
id: tileId,
460-
url: this.getTileUrl(childX, childY, z + this.options['zoomOffset']),
461-
offset
462-
};
456+
if (!this.tileInfoCache) {
457+
this.tileInfoCache = new LRUCache(this.options['maxCacheSize'] * 4);
458+
}
459+
childNode = this.tileInfoCache.get(tileId);
460+
if (!childNode) {
461+
if (scaleY < 0) {
462+
const nwx = minx + dx * width;
463+
const nwy = maxy - dy * height;
464+
// extent2d 是 node.z 级别上的 extent
465+
extent = new PointExtent(nwx, nwy - height, nwx + width, nwy);
466+
467+
} else {
468+
const swx = minx + dx * width;
469+
const swy = miny + dy * height;
470+
extent = new PointExtent(swx, swy, swx + width, swy + height);
471+
}
472+
childNode = {
473+
x: childX,
474+
y: childY,
475+
idx: childIdx,
476+
idy: childIdy,
477+
z,
478+
extent2d: extent,
479+
error: node.error / 2,
480+
res,
481+
id: tileId,
482+
parentNodeId: node.id,
483+
children: [],
484+
url: this.getTileUrl(childX, childY, z + this.options['zoomOffset']),
485+
offset
486+
};
487+
this.tileInfoCache.add(tileId, childNode);
488+
}
463489
if (parentRenderer) {
464490
childNode['layer'] = this.getId();
465491
}
@@ -473,6 +499,7 @@ class TileLayer extends Layer {
473499
} else if (visible === -1) {
474500
continue;
475501
} else if (visible === 0 && z !== maxZoom) {
502+
// 任意子瓦片的error低于maxError,则添加父级瓦片,不再遍历子瓦片
476503
tiles.push(node);
477504
gridExtent._combine(node.extent2d);
478505
return;
@@ -547,11 +574,18 @@ class TileLayer extends Layer {
547574
// }
548575

549576
_isTileInFrustum(node, projectionView, glScale, offset) {
577+
if (!this._zScale) {
578+
const map = this.getMap();
579+
const glRes = map.getGLRes();
580+
this._zScale = map.altitudeToPoint(100, glRes) / 100;
581+
}
550582
const { xmin, ymin, xmax, ymax } = node.extent2d;
551583
TILE_BOX[0][0] = (xmin - offset[0]) * glScale;
552584
TILE_BOX[0][1] = (ymin - offset[1]) * glScale;
585+
TILE_BOX[0][2] = (node.minAltitude || 0) * this._zScale;
553586
TILE_BOX[1][0] = (xmax - offset[0]) * glScale;
554587
TILE_BOX[1][1] = (ymax - offset[1]) * glScale;
588+
TILE_BOX[1][2] = (node.maxAltitude || 0) * this._zScale;
555589
return intersectsBox(projectionView, TILE_BOX);
556590
}
557591

src/map/handler/Map.Drag.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,8 @@ class MapDragHandler extends Handler {
139139
t = 5 * t;
140140
const dscale = isTouch ? 5 : 2.8;
141141
const targetPrjCoord = currentCenter.add(dxy._multi(dscale));
142-
map._fixPrjOnWorldWide(targetPrjCoord);
143-
const targetCoord = map.getProjection().unproject(targetPrjCoord);
144-
map.panTo(targetCoord, { 'duration': isTouch ? t * 3 : t * 2, 'easing': 'outExpo' });
142+
// map._fixPrjOnWorldWide(targetPrjCoord);
143+
map._panTo(targetPrjCoord, { 'duration': isTouch ? t * 3 : t * 2, 'easing': 'outExpo' });
145144
} else {
146145
map.onMoveEnd(param);
147146
}

src/renderer/layer/tilelayer/TileLayerCanvasRenderer.js

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class TileLayerCanvasRenderer extends CanvasRenderer {
7171
this.tilesLoading = {};
7272
this._parentTiles = [];
7373
this._childTiles = [];
74+
this._tileQueue = [];
7475
this.tileCache = new LRUCache(layer.options['maxCacheSize'], this.deleteTile.bind(this));
7576
if (Browser.decodeImageInWorker && this.layer.options['decodeImageInWorker'] && (layer.options['renderer'] === 'gl' || !Browser.safari)) {
7677
this._tileImageWorkerConn = new TileWorkerConnection();
@@ -94,29 +95,36 @@ class TileLayerCanvasRenderer extends CanvasRenderer {
9495
return;
9596
}
9697
}
97-
let tileGrids;
98+
if (!context || context && context.isFinalRender) {
99+
// maptalks/issues#10
100+
// 如果consumeTileQueue方法在每个renderMode都会调用,但多边形只在fxaa mode下才会绘制。
101+
// 导致可能出现consumeTileQueue在fxaa阶段后调用,之后的阶段就不再绘制。
102+
// 改为consumeTileQueue只在finalRender时调用即解决问题
103+
this._consumeTileQueue();
104+
}
105+
let currentTiles;
98106
let hasFreshTiles = false;
99-
const frameTileGrids = this._frameTileGrids;
100-
if (frameTileGrids && timestamp === frameTileGrids.timestamp) {
101-
if (frameTileGrids.empty) {
107+
const frameTiles = this._frameTiles;
108+
if (frameTiles && timestamp === frameTiles.timestamp) {
109+
if (frameTiles.empty) {
102110
return;
103111
}
104-
tileGrids = frameTileGrids;
112+
currentTiles = frameTiles;
105113
} else {
106-
tileGrids = this._getTilesInCurrentFrame();
107-
if (!tileGrids) {
108-
this._frameTileGrids = { empty: true, timestamp };
114+
currentTiles = this._getTilesInCurrentFrame();
115+
if (!currentTiles) {
116+
this._frameTiles = { empty: true, timestamp };
109117
this.completeRender();
110118
return;
111119
}
112120
hasFreshTiles = true;
113-
this._frameTileGrids = tileGrids;
114-
this._frameTileGrids.timestamp = timestamp;
115-
if (tileGrids.loadingCount) {
116-
this.loadTileQueue(tileGrids.tileQueue);
121+
this._frameTiles = currentTiles;
122+
this._frameTiles.timestamp = timestamp;
123+
if (currentTiles.loadingCount) {
124+
this.loadTileQueue(currentTiles.tileQueue);
117125
}
118126
}
119-
const { tiles, childTiles, parentTiles, placeholders, loading, loadingCount } = tileGrids;
127+
const { tiles, childTiles, parentTiles, placeholders, loading, loadingCount } = currentTiles;
120128

121129
this._drawTiles(tiles, parentTiles, childTiles, placeholders, context);
122130
if (!loadingCount) {
@@ -135,10 +143,16 @@ class TileLayerCanvasRenderer extends CanvasRenderer {
135143
}
136144
}
137145

146+
getTileGridsInCurrentFrame() {
147+
return this._frameTileGrids;
148+
}
149+
138150
_getTilesInCurrentFrame() {
139151
const map = this.getMap();
140152
const layer = this.layer;
141-
const tileGrids = layer.getTiles().tileGrids;
153+
let tileGrids = layer.getTiles();
154+
this._frameTileGrids = tileGrids;
155+
tileGrids = tileGrids.tileGrids;
142156
if (!tileGrids || !tileGrids.length) {
143157
return null;
144158
}
@@ -167,7 +181,10 @@ class TileLayerCanvasRenderer extends CanvasRenderer {
167181
const tileGrid = tileGrids[i];
168182
const allTiles = tileGrid['tiles'];
169183

170-
const placeholder = this._generatePlaceHolder(tileGrid.zoom);
184+
let placeholder;
185+
if (allTiles.length) {
186+
placeholder = this._generatePlaceHolder(allTiles[0].res);
187+
}
171188

172189
for (let j = 0, l = allTiles.length; j < l; j++) {
173190
const tile = allTiles[j],
@@ -305,6 +322,9 @@ class TileLayerCanvasRenderer extends CanvasRenderer {
305322

306323
needToRedraw() {
307324
const map = this.getMap();
325+
if (this._tileQueue.length) {
326+
return true;
327+
}
308328
if (map.getPitch()) {
309329
return super.needToRedraw();
310330
}
@@ -456,6 +476,30 @@ class TileLayerCanvasRenderer extends CanvasRenderer {
456476
}
457477

458478
onTileLoad(tileImage, tileInfo) {
479+
this._tileQueue.push({ tileInfo: tileInfo, tileData: tileImage });
480+
}
481+
482+
_consumeTileQueue() {
483+
let count = 0;
484+
const limit = this.layer.options['tileLimitPerFrame'];
485+
const queue = this._tileQueue;
486+
/* eslint-disable no-unmodified-loop-condition */
487+
while (queue.length && (limit <= 0 || count < limit)) {
488+
const { tileData, tileInfo } = queue.shift();
489+
if (!this.checkTileInQueue(tileData, tileInfo)) {
490+
continue;
491+
}
492+
this.consumeTile(tileData, tileInfo);
493+
count++;
494+
}
495+
/* eslint-enable no-unmodified-loop-condition */
496+
}
497+
498+
checkTileInQueue() {
499+
return true;
500+
}
501+
502+
consumeTile(tileImage, tileInfo) {
459503
if (!this.layer) {
460504
return;
461505
}
@@ -535,7 +579,7 @@ class TileLayerCanvasRenderer extends CanvasRenderer {
535579
const map = this.getMap(),
536580
zoom = map.getZoom(),
537581
ctx = this.context,
538-
cp = map._pointToContainerPoint(point, tileZoom, 0, TEMP_POINT),
582+
cp = map._pointAtResToContainerPoint(point, tileInfo.res, 0, TEMP_POINT),
539583
bearing = map.getBearing(),
540584
transformed = bearing || zoom !== tileZoom;
541585
const opacity = this.getTileOpacity(tileImage);
@@ -763,14 +807,14 @@ class TileLayerCanvasRenderer extends CanvasRenderer {
763807
tile.image.onerror = null;
764808
}
765809

766-
_generatePlaceHolder(z) {
810+
_generatePlaceHolder(res) {
767811
const map = this.getMap();
768812
const placeholder = this.layer.options['placeholder'];
769813
if (!placeholder || map.getPitch()) {
770814
return null;
771815
}
772816
const tileSize = this.layer.getTileSize(),
773-
scale = map._getResolution(z) / map._getResolution(),
817+
scale = res / map._getResolution(),
774818
canvas = this._tilePlaceHolder = this._tilePlaceHolder || Canvas.createCanvas(1, 1, map.CanvasClass);
775819
canvas.width = tileSize.width * scale;
776820
canvas.height = tileSize.height * scale;

src/ui/UIMarker.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import UIComponent from './UIComponent';
1010

1111
/**
1212
* @property {Object} options - construct options
13+
* @property {String} [options.containerClass=null] - css class name applied to UIMarker's DOM container
1314
* @property {Boolean} [options.draggable=false] - if the marker can be dragged.
1415
* @property {Number} [options.single=false] - if the marker is a global single one.
1516
* @property {String|HTMLElement} options.content - content of the marker, can be a string type HTML code or a HTMLElement.

0 commit comments

Comments
 (0)