forked from openlayers/openlayers
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTileQueue.js
162 lines (150 loc) · 4.61 KB
/
TileQueue.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/**
* @module ol/TileQueue
*/
import EventType from './events/EventType.js';
import PriorityQueue, {DROP} from './structs/PriorityQueue.js';
import TileState from './TileState.js';
/**
* @typedef {function(import("./Tile.js").default, string, import("./coordinate.js").Coordinate, number): number} PriorityFunction
*/
class TileQueue extends PriorityQueue {
/**
* @param {PriorityFunction} tilePriorityFunction Tile priority function.
* @param {function(): ?} tileChangeCallback Function called on each tile change event.
*/
constructor(tilePriorityFunction, tileChangeCallback) {
super(
/**
* @param {Array} element Element.
* @return {number} Priority.
*/
function (element) {
return tilePriorityFunction.apply(null, element);
},
/**
* @param {Array} element Element.
* @return {string} Key.
*/
function (element) {
return /** @type {import("./Tile.js").default} */ (element[0]).getKey();
}
);
/** @private */
this.boundHandleTileChange_ = this.handleTileChange.bind(this);
/**
* @private
* @type {function(): ?}
*/
this.tileChangeCallback_ = tileChangeCallback;
/**
* @private
* @type {number}
*/
this.tilesLoading_ = 0;
/**
* @private
* @type {!Object<string,boolean>}
*/
this.tilesLoadingKeys_ = {};
}
/**
* @param {Array} element Element.
* @return {boolean} The element was added to the queue.
*/
enqueue(element) {
const added = super.enqueue(element);
if (added) {
const tile = element[0];
tile.addEventListener(EventType.CHANGE, this.boundHandleTileChange_);
}
return added;
}
/**
* @return {number} Number of tiles loading.
*/
getTilesLoading() {
return this.tilesLoading_;
}
/**
* @param {import("./events/Event.js").default} event Event.
* @protected
*/
handleTileChange(event) {
const tile = /** @type {import("./Tile.js").default} */ (event.target);
const state = tile.getState();
if (
(tile.hifi && state === TileState.LOADED) ||
state === TileState.ERROR ||
state === TileState.EMPTY
) {
tile.removeEventListener(EventType.CHANGE, this.boundHandleTileChange_);
const tileKey = tile.getKey();
if (tileKey in this.tilesLoadingKeys_) {
delete this.tilesLoadingKeys_[tileKey];
--this.tilesLoading_;
}
this.tileChangeCallback_();
}
}
/**
* @param {number} maxTotalLoading Maximum number tiles to load simultaneously.
* @param {number} maxNewLoads Maximum number of new tiles to load.
*/
loadMoreTiles(maxTotalLoading, maxNewLoads) {
let newLoads = 0;
let state, tile, tileKey;
while (
this.tilesLoading_ < maxTotalLoading &&
newLoads < maxNewLoads &&
this.getCount() > 0
) {
tile = /** @type {import("./Tile.js").default} */ (this.dequeue()[0]);
tileKey = tile.getKey();
state = tile.getState();
if (state === TileState.IDLE && !(tileKey in this.tilesLoadingKeys_)) {
this.tilesLoadingKeys_[tileKey] = true;
++this.tilesLoading_;
++newLoads;
tile.load();
}
}
}
}
export default TileQueue;
/**
* @param {import('./PluggableMap.js').FrameState} frameState Frame state.
* @param {import("./Tile.js").default} tile Tile.
* @param {string} tileSourceKey Tile source key.
* @param {import("./coordinate.js").Coordinate} tileCenter Tile center.
* @param {number} tileResolution Tile resolution.
* @return {number} Tile priority.
*/
export function getTilePriority(
frameState,
tile,
tileSourceKey,
tileCenter,
tileResolution
) {
// Filter out tiles at higher zoom levels than the current zoom level, or that
// are outside the visible extent.
if (!frameState || !(tileSourceKey in frameState.wantedTiles)) {
return DROP;
}
if (!frameState.wantedTiles[tileSourceKey][tile.getKey()]) {
return DROP;
}
// Prioritize the highest zoom level tiles closest to the focus.
// Tiles at higher zoom levels are prioritized using Math.log(tileResolution).
// Within a zoom level, tiles are prioritized by the distance in pixels between
// the center of the tile and the center of the viewport. The factor of 65536
// means that the prioritization should behave as desired for tiles up to
// 65536 * Math.log(2) = 45426 pixels from the focus.
const center = frameState.viewState.center;
const deltaX = tileCenter[0] - center[0];
const deltaY = tileCenter[1] - center[1];
return (
65536 * Math.log(tileResolution) +
Math.sqrt(deltaX * deltaX + deltaY * deltaY) / tileResolution
);
}