tileserver-gl-light 4.6.6 → 4.7.0
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/.eslintrc.cjs +4 -0
- package/.github/workflows/ct.yml +1 -1
- package/.github/workflows/release.yml +1 -1
- package/README.md +2 -2
- package/docs/config.rst +20 -1
- package/docs/endpoints.rst +8 -8
- package/package.json +5 -5
- package/public/resources/images/marker-icon-2x.png +0 -0
- package/public/resources/images/marker-icon.png +0 -0
- package/public/resources/images/marker-shadow.png +0 -0
- package/public/resources/leaflet.css +6 -1
- package/public/resources/leaflet.js +3 -3
- package/public/resources/leaflet.js.map +1 -1
- package/public/resources/maplibre-gl.js +4 -4
- package/public/resources/maplibre-gl.js.map +1 -1
- package/public/templates/data.tmpl +0 -1
- package/public/templates/index.tmpl +1 -4
- package/public/templates/viewer.tmpl +0 -1
- package/src/healthcheck.js +3 -3
- package/src/main.js +30 -29
- package/src/pmtiles_adapter.js +9 -9
- package/src/render.js +303 -0
- package/src/serve_data.js +12 -12
- package/src/serve_font.js +25 -44
- package/src/serve_rendered.js +234 -613
- package/src/serve_style.js +5 -17
- package/src/server.js +24 -29
- package/src/utils.js +22 -3
- package/test/setup.js +0 -1
- package/public/resources/L.TileLayer.NoGap.js +0 -243
package/src/serve_style.js
CHANGED
|
@@ -11,12 +11,12 @@ import { getPublicUrl } from './utils.js';
|
|
|
11
11
|
|
|
12
12
|
const httpTester = /^(http(s)?:)?\/\//;
|
|
13
13
|
|
|
14
|
-
const fixUrl = (req, url, publicUrl
|
|
14
|
+
const fixUrl = (req, url, publicUrl) => {
|
|
15
15
|
if (!url || typeof url !== 'string' || url.indexOf('local://') !== 0) {
|
|
16
16
|
return url;
|
|
17
17
|
}
|
|
18
18
|
const queryParams = [];
|
|
19
|
-
if (
|
|
19
|
+
if (req.query.key) {
|
|
20
20
|
queryParams.unshift(`key=${encodeURIComponent(req.query.key)}`);
|
|
21
21
|
}
|
|
22
22
|
let query = '';
|
|
@@ -42,20 +42,10 @@ export const serve_style = {
|
|
|
42
42
|
}
|
|
43
43
|
// mapbox-gl-js viewer cannot handle sprite urls with query
|
|
44
44
|
if (styleJSON_.sprite) {
|
|
45
|
-
styleJSON_.sprite = fixUrl(
|
|
46
|
-
req,
|
|
47
|
-
styleJSON_.sprite,
|
|
48
|
-
item.publicUrl,
|
|
49
|
-
false,
|
|
50
|
-
);
|
|
45
|
+
styleJSON_.sprite = fixUrl(req, styleJSON_.sprite, item.publicUrl);
|
|
51
46
|
}
|
|
52
47
|
if (styleJSON_.glyphs) {
|
|
53
|
-
styleJSON_.glyphs = fixUrl(
|
|
54
|
-
req,
|
|
55
|
-
styleJSON_.glyphs,
|
|
56
|
-
item.publicUrl,
|
|
57
|
-
false,
|
|
58
|
-
);
|
|
48
|
+
styleJSON_.glyphs = fixUrl(req, styleJSON_.glyphs, item.publicUrl);
|
|
59
49
|
}
|
|
60
50
|
return res.send(styleJSON_);
|
|
61
51
|
});
|
|
@@ -98,9 +88,7 @@ export const serve_style = {
|
|
|
98
88
|
|
|
99
89
|
const validationErrors = validate(styleFileData);
|
|
100
90
|
if (validationErrors.length > 0) {
|
|
101
|
-
console.log(
|
|
102
|
-
`The file "${params.style}" is not valid a valid style file:`,
|
|
103
|
-
);
|
|
91
|
+
console.log(`The file "${params.style}" is not a valid style file:`);
|
|
104
92
|
for (const err of validationErrors) {
|
|
105
93
|
console.log(`${err.line}: ${err.message}`);
|
|
106
94
|
}
|
package/src/server.js
CHANGED
|
@@ -141,20 +141,16 @@ function start(opts) {
|
|
|
141
141
|
|
|
142
142
|
// Load all available icons into a settings object
|
|
143
143
|
startupPromises.push(
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
paths.availableIcons = files;
|
|
147
|
-
resolve();
|
|
148
|
-
});
|
|
144
|
+
getFiles(paths.icons).then((files) => {
|
|
145
|
+
paths.availableIcons = files;
|
|
149
146
|
}),
|
|
150
147
|
);
|
|
151
148
|
|
|
152
149
|
if (options.dataDecorator) {
|
|
153
150
|
try {
|
|
154
|
-
options.dataDecoratorFunc = require(
|
|
155
|
-
paths.root,
|
|
156
|
-
|
|
157
|
-
));
|
|
151
|
+
options.dataDecoratorFunc = require(
|
|
152
|
+
path.resolve(paths.root, options.dataDecorator),
|
|
153
|
+
);
|
|
158
154
|
} catch (e) {}
|
|
159
155
|
}
|
|
160
156
|
|
|
@@ -183,15 +179,15 @@ function start(opts) {
|
|
|
183
179
|
item,
|
|
184
180
|
id,
|
|
185
181
|
opts.publicUrl,
|
|
186
|
-
(
|
|
182
|
+
(styleSourceId, protocol) => {
|
|
187
183
|
let dataItemId;
|
|
188
184
|
for (const id of Object.keys(data)) {
|
|
189
|
-
if (id ===
|
|
185
|
+
if (id === styleSourceId) {
|
|
190
186
|
// Style id was found in data ids, return that id
|
|
191
187
|
dataItemId = id;
|
|
192
188
|
} else {
|
|
193
189
|
const fileType = Object.keys(data[id])[0];
|
|
194
|
-
if (data[id][fileType] ===
|
|
190
|
+
if (data[id][fileType] === styleSourceId) {
|
|
195
191
|
// Style id was found in data filename, return the id that filename belong to
|
|
196
192
|
dataItemId = id;
|
|
197
193
|
}
|
|
@@ -203,21 +199,21 @@ function start(opts) {
|
|
|
203
199
|
} else {
|
|
204
200
|
if (!allowMoreData) {
|
|
205
201
|
console.log(
|
|
206
|
-
`ERROR: style "${item.style}" using unknown file "${
|
|
202
|
+
`ERROR: style "${item.style}" using unknown file "${styleSourceId}"! Skipping...`,
|
|
207
203
|
);
|
|
208
204
|
return undefined;
|
|
209
205
|
} else {
|
|
210
206
|
let id =
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
if (isValidHttpUrl(
|
|
207
|
+
styleSourceId.substr(0, styleSourceId.lastIndexOf('.')) ||
|
|
208
|
+
styleSourceId;
|
|
209
|
+
if (isValidHttpUrl(styleSourceId)) {
|
|
214
210
|
id =
|
|
215
|
-
fnv1a(
|
|
211
|
+
fnv1a(styleSourceId) + '_' + id.replace(/^.*\/(.*)$/, '$1');
|
|
216
212
|
}
|
|
217
213
|
while (data[id]) id += '_'; //if the data source id already exists, add a "_" untill it doesn't
|
|
218
214
|
//Add the new data source to the data array.
|
|
219
215
|
data[id] = {
|
|
220
|
-
[protocol]:
|
|
216
|
+
[protocol]: styleSourceId,
|
|
221
217
|
};
|
|
222
218
|
|
|
223
219
|
return id;
|
|
@@ -240,15 +236,15 @@ function start(opts) {
|
|
|
240
236
|
item,
|
|
241
237
|
id,
|
|
242
238
|
opts.publicUrl,
|
|
243
|
-
(
|
|
239
|
+
function dataResolver(styleSourceId) {
|
|
244
240
|
let fileType;
|
|
245
241
|
let inputFile;
|
|
246
242
|
for (const id of Object.keys(data)) {
|
|
247
243
|
fileType = Object.keys(data[id])[0];
|
|
248
|
-
if (
|
|
244
|
+
if (styleSourceId == id) {
|
|
249
245
|
inputFile = data[id][fileType];
|
|
250
246
|
break;
|
|
251
|
-
} else if (data[id][fileType] ==
|
|
247
|
+
} else if (data[id][fileType] == styleSourceId) {
|
|
252
248
|
inputFile = data[id][fileType];
|
|
253
249
|
break;
|
|
254
250
|
}
|
|
@@ -257,7 +253,7 @@ function start(opts) {
|
|
|
257
253
|
inputFile = path.resolve(options.paths[fileType], inputFile);
|
|
258
254
|
}
|
|
259
255
|
|
|
260
|
-
return {
|
|
256
|
+
return { inputFile, fileType };
|
|
261
257
|
},
|
|
262
258
|
),
|
|
263
259
|
);
|
|
@@ -348,7 +344,7 @@ function start(opts) {
|
|
|
348
344
|
result.push({
|
|
349
345
|
version: styleJSON.version,
|
|
350
346
|
name: styleJSON.name,
|
|
351
|
-
id
|
|
347
|
+
id,
|
|
352
348
|
url: `${getPublicUrl(
|
|
353
349
|
opts.publicUrl,
|
|
354
350
|
req,
|
|
@@ -427,9 +423,8 @@ function start(opts) {
|
|
|
427
423
|
return res.status(404).send('Not found');
|
|
428
424
|
}
|
|
429
425
|
}
|
|
430
|
-
data[
|
|
431
|
-
|
|
432
|
-
] = `${packageJson.name} v${packageJson.version}`;
|
|
426
|
+
data['server_version'] =
|
|
427
|
+
`${packageJson.name} v${packageJson.version}`;
|
|
433
428
|
data['public_url'] = opts.publicUrl || '/';
|
|
434
429
|
data['is_light'] = isLight;
|
|
435
430
|
data['key_query_part'] = req.query.key
|
|
@@ -635,9 +630,9 @@ function start(opts) {
|
|
|
635
630
|
enableShutdown(server);
|
|
636
631
|
|
|
637
632
|
return {
|
|
638
|
-
app
|
|
639
|
-
server
|
|
640
|
-
startupPromise
|
|
633
|
+
app,
|
|
634
|
+
server,
|
|
635
|
+
startupPromise,
|
|
641
636
|
};
|
|
642
637
|
}
|
|
643
638
|
|
package/src/utils.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
import path from 'path';
|
|
4
|
-
import
|
|
4
|
+
import fsPromises from 'fs/promises';
|
|
5
|
+
import fs, { existsSync } from 'node:fs';
|
|
5
6
|
import clone from 'clone';
|
|
6
7
|
import glyphCompose from '@mapbox/glyph-pbf-composite';
|
|
7
8
|
|
|
@@ -139,7 +140,7 @@ const getFontPbf = (allowedFonts, fontPath, name, range, fallbacks) =>
|
|
|
139
140
|
}
|
|
140
141
|
});
|
|
141
142
|
|
|
142
|
-
export const getFontsPbf = (
|
|
143
|
+
export const getFontsPbf = async (
|
|
143
144
|
allowedFonts,
|
|
144
145
|
fontPath,
|
|
145
146
|
names,
|
|
@@ -160,7 +161,25 @@ export const getFontsPbf = (
|
|
|
160
161
|
);
|
|
161
162
|
}
|
|
162
163
|
|
|
163
|
-
|
|
164
|
+
const values = await Promise.all(queue);
|
|
165
|
+
return glyphCompose.combine(values);
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
export const listFonts = async (fontPath) => {
|
|
169
|
+
const existingFonts = {};
|
|
170
|
+
|
|
171
|
+
const files = await fsPromises.readdir(fontPath);
|
|
172
|
+
for (const file of files) {
|
|
173
|
+
const stats = await fsPromises.stat(path.join(fontPath, file));
|
|
174
|
+
if (
|
|
175
|
+
stats.isDirectory() &&
|
|
176
|
+
existsSync(path.join(fontPath, file, '0-255.pbf'))
|
|
177
|
+
) {
|
|
178
|
+
existingFonts[path.basename(file)] = true;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return existingFonts;
|
|
164
183
|
};
|
|
165
184
|
|
|
166
185
|
export const isValidHttpUrl = (string) => {
|
package/test/setup.js
CHANGED
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
// @class TileLayer
|
|
2
|
-
|
|
3
|
-
L.TileLayer.mergeOptions({
|
|
4
|
-
// @option keepBuffer
|
|
5
|
-
// The amount of tiles outside the visible map area to be kept in the stitched
|
|
6
|
-
// `TileLayer`.
|
|
7
|
-
|
|
8
|
-
// @option dumpToCanvas: Boolean = true
|
|
9
|
-
// Whether to dump loaded tiles to a `<canvas>` to prevent some rendering
|
|
10
|
-
// artifacts. (Disabled by default in IE)
|
|
11
|
-
dumpToCanvas: L.Browser.canvas && !L.Browser.ie,
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
L.TileLayer.include({
|
|
15
|
-
_onUpdateLevel: function(z, zoom) {
|
|
16
|
-
if (this.options.dumpToCanvas) {
|
|
17
|
-
this._levels[z].canvas.style.zIndex =
|
|
18
|
-
this.options.maxZoom - Math.abs(zoom - z);
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
|
|
22
|
-
_onRemoveLevel: function(z) {
|
|
23
|
-
if (this.options.dumpToCanvas) {
|
|
24
|
-
L.DomUtil.remove(this._levels[z].canvas);
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
_onCreateLevel: function(level) {
|
|
29
|
-
if (this.options.dumpToCanvas) {
|
|
30
|
-
level.canvas = L.DomUtil.create(
|
|
31
|
-
"canvas",
|
|
32
|
-
"leaflet-tile-container leaflet-zoom-animated",
|
|
33
|
-
this._container
|
|
34
|
-
);
|
|
35
|
-
level.ctx = level.canvas.getContext("2d");
|
|
36
|
-
this._resetCanvasSize(level);
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
|
|
40
|
-
_removeTile: function(key) {
|
|
41
|
-
if (this.options.dumpToCanvas) {
|
|
42
|
-
var tile = this._tiles[key];
|
|
43
|
-
var level = this._levels[tile.coords.z];
|
|
44
|
-
var tileSize = this.getTileSize();
|
|
45
|
-
|
|
46
|
-
if (level) {
|
|
47
|
-
// Where in the canvas should this tile go?
|
|
48
|
-
var offset = L.point(tile.coords.x, tile.coords.y)
|
|
49
|
-
.subtract(level.canvasRange.min)
|
|
50
|
-
.scaleBy(this.getTileSize());
|
|
51
|
-
|
|
52
|
-
level.ctx.clearRect(offset.x, offset.y, tileSize.x, tileSize.y);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
L.GridLayer.prototype._removeTile.call(this, key);
|
|
57
|
-
},
|
|
58
|
-
|
|
59
|
-
_resetCanvasSize: function(level) {
|
|
60
|
-
var buff = this.options.keepBuffer,
|
|
61
|
-
pixelBounds = this._getTiledPixelBounds(this._map.getCenter()),
|
|
62
|
-
tileRange = this._pxBoundsToTileRange(pixelBounds),
|
|
63
|
-
tileSize = this.getTileSize();
|
|
64
|
-
|
|
65
|
-
tileRange.min = tileRange.min.subtract([buff, buff]); // This adds the no-prune buffer
|
|
66
|
-
tileRange.max = tileRange.max.add([buff + 1, buff + 1]);
|
|
67
|
-
|
|
68
|
-
var pixelRange = L.bounds(
|
|
69
|
-
tileRange.min.scaleBy(tileSize),
|
|
70
|
-
tileRange.max.add([1, 1]).scaleBy(tileSize) // This prevents an off-by-one when checking if tiles are inside
|
|
71
|
-
),
|
|
72
|
-
mustRepositionCanvas = false,
|
|
73
|
-
neededSize = pixelRange.max.subtract(pixelRange.min);
|
|
74
|
-
|
|
75
|
-
// Resize the canvas, if needed, and only to make it bigger.
|
|
76
|
-
if (
|
|
77
|
-
neededSize.x > level.canvas.width ||
|
|
78
|
-
neededSize.y > level.canvas.height
|
|
79
|
-
) {
|
|
80
|
-
// Resizing canvases erases the currently drawn content, I'm afraid.
|
|
81
|
-
// To keep it, dump the pixels to another canvas, then display it on
|
|
82
|
-
// top. This could be done with getImageData/putImageData, but that
|
|
83
|
-
// would break for tainted canvases (in non-CORS tilesets)
|
|
84
|
-
var oldSize = { x: level.canvas.width, y: level.canvas.height };
|
|
85
|
-
// console.info('Resizing canvas from ', oldSize, 'to ', neededSize);
|
|
86
|
-
|
|
87
|
-
var tmpCanvas = L.DomUtil.create("canvas");
|
|
88
|
-
tmpCanvas.style.width = (tmpCanvas.width = oldSize.x) + "px";
|
|
89
|
-
tmpCanvas.style.height = (tmpCanvas.height = oldSize.y) + "px";
|
|
90
|
-
tmpCanvas.getContext("2d").drawImage(level.canvas, 0, 0);
|
|
91
|
-
// var data = level.ctx.getImageData(0, 0, oldSize.x, oldSize.y);
|
|
92
|
-
|
|
93
|
-
level.canvas.style.width = (level.canvas.width = neededSize.x) + "px";
|
|
94
|
-
level.canvas.style.height = (level.canvas.height = neededSize.y) + "px";
|
|
95
|
-
level.ctx.drawImage(tmpCanvas, 0, 0);
|
|
96
|
-
// level.ctx.putImageData(data, 0, 0, 0, 0, oldSize.x, oldSize.y);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Translate the canvas contents if it's moved around
|
|
100
|
-
if (level.canvasRange) {
|
|
101
|
-
var offset = level.canvasRange.min
|
|
102
|
-
.subtract(tileRange.min)
|
|
103
|
-
.scaleBy(this.getTileSize());
|
|
104
|
-
|
|
105
|
-
// console.info('Offsetting by ', offset);
|
|
106
|
-
|
|
107
|
-
if (!L.Browser.safari) {
|
|
108
|
-
// By default, canvases copy things "on top of" existing pixels, but we want
|
|
109
|
-
// this to *replace* the existing pixels when doing a drawImage() call.
|
|
110
|
-
// This will also clear the sides, so no clearRect() calls are needed to make room
|
|
111
|
-
// for the new tiles.
|
|
112
|
-
level.ctx.globalCompositeOperation = "copy";
|
|
113
|
-
level.ctx.drawImage(level.canvas, offset.x, offset.y);
|
|
114
|
-
level.ctx.globalCompositeOperation = "source-over";
|
|
115
|
-
} else {
|
|
116
|
-
// Safari clears the canvas when copying from itself :-(
|
|
117
|
-
if (!this._tmpCanvas) {
|
|
118
|
-
var t = (this._tmpCanvas = L.DomUtil.create("canvas"));
|
|
119
|
-
t.width = level.canvas.width;
|
|
120
|
-
t.height = level.canvas.height;
|
|
121
|
-
this._tmpContext = t.getContext("2d");
|
|
122
|
-
}
|
|
123
|
-
this._tmpContext.clearRect(
|
|
124
|
-
0,
|
|
125
|
-
0,
|
|
126
|
-
level.canvas.width,
|
|
127
|
-
level.canvas.height
|
|
128
|
-
);
|
|
129
|
-
this._tmpContext.drawImage(level.canvas, 0, 0);
|
|
130
|
-
level.ctx.clearRect(0, 0, level.canvas.width, level.canvas.height);
|
|
131
|
-
level.ctx.drawImage(this._tmpCanvas, offset.x, offset.y);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
mustRepositionCanvas = true; // Wait until new props are set
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
level.canvasRange = tileRange;
|
|
138
|
-
level.canvasPxRange = pixelRange;
|
|
139
|
-
level.canvasOrigin = pixelRange.min;
|
|
140
|
-
|
|
141
|
-
// console.log('Canvas tile range: ', level, tileRange.min, tileRange.max );
|
|
142
|
-
// console.log('Canvas pixel range: ', pixelRange.min, pixelRange.max );
|
|
143
|
-
// console.log('Level origin: ', level.origin );
|
|
144
|
-
|
|
145
|
-
if (mustRepositionCanvas) {
|
|
146
|
-
this._setCanvasZoomTransform(
|
|
147
|
-
level,
|
|
148
|
-
this._map.getCenter(),
|
|
149
|
-
this._map.getZoom()
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
},
|
|
153
|
-
|
|
154
|
-
/// set transform/position of canvas, in addition to the transform/position of the individual tile container
|
|
155
|
-
_setZoomTransform: function(level, center, zoom) {
|
|
156
|
-
L.GridLayer.prototype._setZoomTransform.call(this, level, center, zoom);
|
|
157
|
-
if (this.options.dumpToCanvas) {
|
|
158
|
-
this._setCanvasZoomTransform(level, center, zoom);
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
|
-
|
|
162
|
-
// This will get called twice:
|
|
163
|
-
// * From _setZoomTransform
|
|
164
|
-
// * When the canvas has shifted due to a new tile being loaded
|
|
165
|
-
_setCanvasZoomTransform: function(level, center, zoom) {
|
|
166
|
-
// console.log('_setCanvasZoomTransform', level, center, zoom);
|
|
167
|
-
if (!level.canvasOrigin) {
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
var scale = this._map.getZoomScale(zoom, level.zoom),
|
|
171
|
-
translate = level.canvasOrigin
|
|
172
|
-
.multiplyBy(scale)
|
|
173
|
-
.subtract(this._map._getNewPixelOrigin(center, zoom))
|
|
174
|
-
.round();
|
|
175
|
-
|
|
176
|
-
if (L.Browser.any3d) {
|
|
177
|
-
L.DomUtil.setTransform(level.canvas, translate, scale);
|
|
178
|
-
} else {
|
|
179
|
-
L.DomUtil.setPosition(level.canvas, translate);
|
|
180
|
-
}
|
|
181
|
-
},
|
|
182
|
-
|
|
183
|
-
_onOpaqueTile: function(tile) {
|
|
184
|
-
if (!this.options.dumpToCanvas) {
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Guard against an NS_ERROR_NOT_AVAILABLE (or similar) exception
|
|
189
|
-
// when a non-image-tile has been loaded (e.g. a WMS error).
|
|
190
|
-
// Checking for tile.el.complete is not enough, as it has been
|
|
191
|
-
// already marked as loaded and ready somehow.
|
|
192
|
-
try {
|
|
193
|
-
this.dumpPixels(tile.coords, tile.el);
|
|
194
|
-
} catch (ex) {
|
|
195
|
-
return this.fire("tileerror", {
|
|
196
|
-
error: "Could not copy tile pixels: " + ex,
|
|
197
|
-
tile: tile,
|
|
198
|
-
coods: tile.coords,
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// If dumping the pixels was successful, then hide the tile.
|
|
203
|
-
// Do not remove the tile itself, as it is needed to check if the whole
|
|
204
|
-
// level (and its canvas) should be removed (via level.el.children.length)
|
|
205
|
-
tile.el.style.display = "none";
|
|
206
|
-
},
|
|
207
|
-
|
|
208
|
-
// @section Extension methods
|
|
209
|
-
// @uninheritable
|
|
210
|
-
|
|
211
|
-
// @method dumpPixels(coords: Object, imageSource: CanvasImageSource): this
|
|
212
|
-
// Dumps pixels from the given `CanvasImageSource` into the layer, into
|
|
213
|
-
// the space for the tile represented by the `coords` tile coordinates (an object
|
|
214
|
-
// like `{x: Number, y: Number, z: Number}`; the image source must have the
|
|
215
|
-
// same size as the `tileSize` option for the layer. Has no effect if `dumpToCanvas`
|
|
216
|
-
// is `false`.
|
|
217
|
-
dumpPixels: function(coords, imageSource) {
|
|
218
|
-
var level = this._levels[coords.z],
|
|
219
|
-
tileSize = this.getTileSize();
|
|
220
|
-
|
|
221
|
-
if (!level.canvasRange || !this.options.dumpToCanvas) {
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Check if the tile is inside the currently visible map bounds
|
|
226
|
-
// There is a possible race condition when tiles are loaded after they
|
|
227
|
-
// have been panned outside of the map.
|
|
228
|
-
if (!level.canvasRange.contains(coords)) {
|
|
229
|
-
this._resetCanvasSize(level);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Where in the canvas should this tile go?
|
|
233
|
-
var offset = L.point(coords.x, coords.y)
|
|
234
|
-
.subtract(level.canvasRange.min)
|
|
235
|
-
.scaleBy(this.getTileSize());
|
|
236
|
-
|
|
237
|
-
level.ctx.drawImage(imageSource, offset.x, offset.y, tileSize.x, tileSize.y);
|
|
238
|
-
|
|
239
|
-
// TODO: Clear the pixels of other levels' canvases where they overlap
|
|
240
|
-
// this newly dumped tile.
|
|
241
|
-
return this;
|
|
242
|
-
},
|
|
243
|
-
});
|