tileserver-gl 3.1.1 → 4.0.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.
Files changed (35) hide show
  1. package/Dockerfile +13 -5
  2. package/Dockerfile_light +3 -2
  3. package/Dockerfile_test +2 -1
  4. package/README.md +56 -15
  5. package/docker-entrypoint.sh +13 -1
  6. package/docker-entrypoint_light.sh +35 -0
  7. package/docs/conf.py +4 -4
  8. package/docs/installation.rst +23 -4
  9. package/docs/usage.rst +4 -2
  10. package/package.json +17 -17
  11. package/public/resources/L.TileLayer.NoGap.js +243 -0
  12. package/public/resources/leaflet.css +657 -0
  13. package/public/resources/leaflet.js +6 -0
  14. package/public/resources/leaflet.js.map +1 -0
  15. package/public/resources/maplibre-gl-compat.css +1 -0
  16. package/public/resources/maplibre-gl-compat.js +45 -0
  17. package/public/resources/maplibre-gl-compat.js.map +1 -0
  18. package/public/resources/maplibre-gl-inspect-compat.min.js +1 -0
  19. package/public/resources/maplibre-gl-inspect.css +39 -0
  20. package/public/resources/maplibre-gl-inspect.min.js +1 -0
  21. package/public/resources/maplibre-gl.css +1 -0
  22. package/public/resources/maplibre-gl.js +44 -0
  23. package/public/resources/maplibre-gl.js.map +1 -0
  24. package/public/templates/data.tmpl +54 -15
  25. package/public/templates/viewer.tmpl +68 -14
  26. package/publish.js +2 -1
  27. package/src/main.js +4 -2
  28. package/src/serve_rendered.js +23 -22
  29. package/src/serve_style.js +1 -1
  30. package/public/resources/mapbox-gl-inspect.css +0 -40
  31. package/public/resources/mapbox-gl-inspect.min.js +0 -1
  32. package/public/resources/mapbox-gl.css +0 -1
  33. package/public/resources/mapbox-gl.js +0 -40
  34. package/public/resources/mapbox.css +0 -1
  35. package/public/resources/mapbox.js +0 -67
package/Dockerfile CHANGED
@@ -1,4 +1,4 @@
1
- FROM node:10-buster AS builder
1
+ FROM node:16-bullseye AS builder
2
2
 
3
3
  RUN export DEBIAN_FRONTEND=noninteractive \
4
4
  && apt-get -qq update \
@@ -11,7 +11,6 @@ RUN export DEBIAN_FRONTEND=noninteractive \
11
11
  libcairo2-dev \
12
12
  libgles2-mesa-dev \
13
13
  libgbm-dev \
14
- libllvm7 \
15
14
  libprotobuf-dev \
16
15
  && apt-get -y --purge autoremove \
17
16
  && apt-get clean \
@@ -24,7 +23,7 @@ ENV NODE_ENV="production"
24
23
  RUN cd /usr/src/app && npm install --production
25
24
 
26
25
 
27
- FROM node:10-buster-slim AS final
26
+ FROM node:16-bullseye-slim AS final
28
27
 
29
28
  RUN export DEBIAN_FRONTEND=noninteractive \
30
29
  && apt-get -qq update \
@@ -33,9 +32,20 @@ RUN export DEBIAN_FRONTEND=noninteractive \
33
32
  libegl1 \
34
33
  xvfb \
35
34
  xauth \
35
+ libopengl0 \
36
+ libcurl4 \
37
+ curl \
38
+ libuv1-dev \
39
+ libc6-dev \
40
+ libcap2-bin \
36
41
  && apt-get -y --purge autoremove \
37
42
  && apt-get clean \
38
43
  && rm -rf /var/lib/apt/lists/*
44
+
45
+ RUN curl http://archive.ubuntu.com/ubuntu/pool/main/libj/libjpeg-turbo/libjpeg-turbo8_2.0.3-0ubuntu1_amd64.deb --output libjpeg-turbo8_2.0.3-0ubuntu1_amd64.deb
46
+ RUN apt install ./libjpeg-turbo8_2.0.3-0ubuntu1_amd64.deb
47
+ RUN curl http://archive.ubuntu.com/ubuntu/pool/main/i/icu/libicu66_66.1-2ubuntu2_amd64.deb --output libicu66_66.1-2ubuntu2_amd64.deb
48
+ RUN apt install ./libicu66_66.1-2ubuntu2_amd64.deb
39
49
 
40
50
  COPY --from=builder /usr/src/app /app
41
51
 
@@ -51,5 +61,3 @@ EXPOSE 80
51
61
  USER node:node
52
62
 
53
63
  ENTRYPOINT ["/app/docker-entrypoint.sh"]
54
-
55
- CMD ["-p", "80"]
package/Dockerfile_light CHANGED
@@ -1,4 +1,4 @@
1
- FROM node:10-stretch
1
+ FROM node:16-bullseye
2
2
 
3
3
  ENV NODE_ENV="production"
4
4
  ENV CHOKIDAR_USEPOLLING=1
@@ -6,8 +6,9 @@ ENV CHOKIDAR_INTERVAL=500
6
6
  EXPOSE 80
7
7
  VOLUME /data
8
8
  WORKDIR /data
9
- ENTRYPOINT ["node", "/usr/src/app/", "-p", "80"]
9
+ ENTRYPOINT ["/usr/src/app/docker-entrypoint.sh"]
10
10
 
11
11
  RUN mkdir -p /usr/src/app
12
12
  COPY / /usr/src/app
13
13
  RUN cd /usr/src/app && npm install --production
14
+ RUN ["chmod", "+x", "/usr/src/app/docker-entrypoint.sh"]
package/Dockerfile_test CHANGED
@@ -2,7 +2,7 @@
2
2
  # Simply run "docker build -f Dockerfile_test ."
3
3
  # WARNING: sometimes it fails with a core dumped exception
4
4
 
5
- FROM node:10-stretch
5
+ FROM node:16-bullseye
6
6
 
7
7
  RUN apt-get -qq update \
8
8
  && DEBIAN_FRONTEND=noninteractive apt-get -y install \
@@ -17,6 +17,7 @@ RUN apt-get -qq update \
17
17
  libllvm3.9 \
18
18
  libprotobuf-dev \
19
19
  libxxf86vm-dev \
20
+ libopengl0 \
20
21
  xvfb \
21
22
  && apt-get clean
22
23
 
package/README.md CHANGED
@@ -5,44 +5,85 @@
5
5
  [![Build Status](https://travis-ci.org/maptiler/tileserver-gl.svg?branch=master)](https://travis-ci.org/maptiler/tileserver-gl)
6
6
  [![Docker Hub](https://img.shields.io/badge/docker-hub-blue.svg)](https://hub.docker.com/r/maptiler/tileserver-gl/)
7
7
 
8
- Vector and raster maps with GL styles. Server side rendering by Mapbox GL Native. Map tile server for Mapbox GL JS, Android, iOS, Leaflet, OpenLayers, GIS via WMTS, etc.
8
+ Vector and raster maps with GL styles. Server-side rendering by MapLibre GL Native. Map tile server for MapLibre GL JS, Android, iOS, Leaflet, OpenLayers, GIS via WMTS, etc.
9
9
 
10
- ## Get Started
10
+ Download vector tiles from [OpenMapTiles](https://data.maptiler.com/downloads/planet/).
11
+ ## Getting Started with Node
11
12
 
12
- Make sure you have Node.js version **10** installed (running `node -v` it should output something like `v10.17.0`).
13
+ Make sure you have Node.js version **14.20.0** or above installed. Node 16 is recommended. (running `node -v` it should output something like `v16.x.x`). Running without docker requires [Native dependencies](https://tileserver.readthedocs.io/en/latest/installation.html#npm) to be installed first.
13
14
 
14
- Install `tileserver-gl` with server-side raster rendering of vector tiles with npm
15
+ Install `tileserver-gl` with server-side raster rendering of vector tiles with npm.
15
16
 
16
17
  ```bash
17
18
  npm install -g tileserver-gl
18
19
  ```
19
20
 
20
- Now download vector tiles from [OpenMapTiles](https://openmaptiles.org/downloads/).
21
+ Once installed, you can use it like the following examples.
21
22
 
23
+ using a mbtiles file
22
24
  ```bash
23
- curl -o zurich_switzerland.mbtiles https://[GET-YOUR-LINK]/extracts/zurich_switzerland.mbtiles
25
+ wget https://github.com/maptiler/tileserver-gl/releases/download/v1.3.0/zurich_switzerland.mbtiles
26
+ tileserver-gl --mbtiles zurich_switzerland.mbtiles
27
+ [in your browser, visit http://[server ip]:8080]
24
28
  ```
25
29
 
26
- Start `tileserver-gl` with the downloaded vector tiles.
27
-
30
+ using a config.json + style + mbtiles file
28
31
  ```bash
29
- tileserver-gl zurich_switzerland.mbtiles
32
+ wget https://github.com/maptiler/tileserver-gl/releases/download/v1.3.0/test_data.zip
33
+ unzip test_data.zip
34
+ tileserver-gl
35
+ [in your browser, visit http://[server ip]:8080]
30
36
  ```
31
37
 
32
- Alternatively, you can use the `tileserver-gl-light` package instead, which is pure javascript (does not have any native dependencies) and can run anywhere, but does not contain rasterization on the server side made with MapBox GL Native.
38
+ Alternatively, you can use the `tileserver-gl-light` npm package instead, which is pure javascript, does not have any native dependencies, and can run anywhere, but does not contain rasterization on the server side made with Maplibre GL Native.
33
39
 
34
- ## Using Docker
40
+ ## Getting Started with Docker
35
41
 
36
- An alternative to npm to start the packed software easier is to install [Docker](https://www.docker.com/) on your computer and then run in the directory with the downloaded MBTiles the command:
42
+ An alternative to npm to start the packed software easier is to install [Docker](https://www.docker.com/) on your computer and then run from the tileserver-gl directory
37
43
 
44
+ Example using a mbtiles file
38
45
  ```bash
46
+ wget https://github.com/maptiler/tileserver-gl/releases/download/v1.3.0/zurich_switzerland.mbtiles
47
+ docker run --rm -it -v $(pwd):/data -p 8080:80 maptiler/tileserver-gl --mbtiles zurich_switzerland.mbtiles
48
+ [in your browser, visit http://[server ip]:8080]
49
+ ```
50
+
51
+ Example using a config.json + style + mbtiles file
52
+ ```bash
53
+ wget https://github.com/maptiler/tileserver-gl/releases/download/v1.3.0/test_data.zip
54
+ unzip test_data.zip
39
55
  docker run --rm -it -v $(pwd):/data -p 8080:80 maptiler/tileserver-gl
56
+ [in your browser, visit http://[server ip]:8080]
40
57
  ```
41
58
 
42
- This will download and start a ready to use container on your computer and the maps are going to be available in webbrowser on localhost:8080.
59
+ Example using a different path
60
+ ```bash
61
+ docker run --rm -it -v /your/local/config/path:/data -p 8080:80 maptiler/tileserver-gl
62
+ ```
63
+ replace '/your/local/config/path' with the path to your config file
64
+
43
65
 
44
- On laptop you can use [Docker Kitematic](https://kitematic.com/) and search "tileserver-gl" and run it, then drop in the 'data' folder the MBTiles.
66
+ Alternatively, you can use the `maptiler/tileserver-gl-light` docker image instead, which is pure javascript, does not have any native dependencies, and can run anywhere, but does not contain rasterization on the server side made with Maplibre GL Native.
67
+
68
+ ## Getting Started with Linux cli
69
+
70
+ Test from command line
71
+ ```bash
72
+ wget https://github.com/maptiler/tileserver-gl/releases/download/v1.3.0/test_data.zip
73
+ unzip -q test_data.zip -d test_data
74
+ xvfb-run --server-args="-screen 0 1024x768x24" npm test
75
+ ```
76
+
77
+ Run from command line
78
+ ```bash
79
+ xvfb-run --server-args="-screen 0 1024x768x24" node .
80
+ ```
45
81
 
46
82
  ## Documentation
47
83
 
48
- You can read full documentation of this project at https://tileserver.readthedocs.io/.
84
+ You can read the full documentation of this project at https://tileserver.readthedocs.io/.
85
+
86
+ ## Alternative
87
+
88
+ Discover MapTiler Server if you need a [map server with easy setup and user-friendly interface](https://www.maptiler.com/server/).
89
+
@@ -10,12 +10,24 @@ handle() {
10
10
 
11
11
  trap handle INT TERM
12
12
 
13
+ refresh() {
14
+ SIGNAL=$(( $? - 128 ))
15
+ echo "Caught signal ${SIGNAL}, refreshing"
16
+ kill -s ${SIGNAL} $(pidof node) 2>/dev/null
17
+ }
18
+
19
+ trap refresh HUP
20
+
13
21
  if ! which -- "${1}"; then
14
22
  # first arg is not an executable
15
- xvfb-run -a --server-args="-screen 0 1024x768x24" -- node /app/ "$@" &
23
+ xvfb-run -a --server-args="-screen 0 1024x768x24" -- node /app/ -p 80 "$@" &
16
24
  # Wait exits immediately on signals which have traps set. Store return value and wait
17
25
  # again for all jobs to actually complete before continuing.
18
26
  wait $! || RETVAL=$?
27
+ while [ ${RETVAL} = 129 ] ; do
28
+ # Refressh signal HUP received. Continue waiting for signals.
29
+ wait $! || RETVAL=$?
30
+ done
19
31
  wait
20
32
  exit ${RETVAL}
21
33
  fi
@@ -0,0 +1,35 @@
1
+ #!/bin/sh
2
+
3
+ set -e
4
+
5
+ handle() {
6
+ SIGNAL=$(( $? - 128 ))
7
+ echo "Caught signal ${SIGNAL}, stopping gracefully"
8
+ kill -s ${SIGNAL} $(pidof node) 2>/dev/null
9
+ }
10
+
11
+ trap handle INT TERM
12
+
13
+ refresh() {
14
+ SIGNAL=$(( $? - 128 ))
15
+ echo "Caught signal ${SIGNAL}, refreshing"
16
+ kill -s ${SIGNAL} $(pidof node) 2>/dev/null
17
+ }
18
+
19
+ trap refresh HUP
20
+
21
+ if ! which -- "${1}"; then
22
+ # first arg is not an executable
23
+ node /usr/src/app/ -p 80 "$@" &
24
+ # Wait exits immediately on signals which have traps set. Store return value and wait
25
+ # again for all jobs to actually complete before continuing.
26
+ wait $! || RETVAL=$?
27
+ while [ ${RETVAL} = 129 ] ; do
28
+ # Refressh signal HUP received. Continue waiting for signals.
29
+ wait $! || RETVAL=$?
30
+ done
31
+ wait
32
+ exit ${RETVAL}
33
+ fi
34
+
35
+ exec "$@"
package/docs/conf.py CHANGED
@@ -44,7 +44,7 @@ master_doc = 'index'
44
44
 
45
45
  # General information about the project.
46
46
  project = u'TileServer GL'
47
- copyright = u'2016, Klokan Technologies GmbH'
47
+ copyright = u'2022, MapTiler.com'
48
48
 
49
49
  # The version info for the project you're documenting, acts as replacement for
50
50
  # |version| and |release|, also used in various other places throughout the
@@ -197,7 +197,7 @@ latex_elements = {
197
197
  # author, documentclass [howto, manual, or own class]).
198
198
  latex_documents = [
199
199
  ('index', 'TileServerGL.tex', u'TileServer GL Documentation',
200
- u'Klokan Technologies GmbH', 'manual'),
200
+ u'MapTiler.com', 'manual'),
201
201
  ]
202
202
 
203
203
  # The name of an image file (relative to this directory) to place at the top of
@@ -227,7 +227,7 @@ latex_documents = [
227
227
  # (source start file, name, description, authors, manual section).
228
228
  man_pages = [
229
229
  ('index', 'tileservergl', u'TileServer GL Documentation',
230
- [u'Klokan Technologies GmbH'], 1)
230
+ [u'MapTiler.com'], 1)
231
231
  ]
232
232
 
233
233
  # If true, show URL addresses after external links.
@@ -241,7 +241,7 @@ man_pages = [
241
241
  # dir menu entry, description, category)
242
242
  texinfo_documents = [
243
243
  ('index', 'TileServerGL', u'TileServer GL Documentation',
244
- u'Klokan Technologies GmbH', 'TileServerGL', 'One line description of project.',
244
+ u'MapTiler.com', 'TileServerGL', 'One line description of project.',
245
245
  'Miscellaneous'),
246
246
  ]
247
247
 
@@ -26,11 +26,30 @@ Native dependencies
26
26
  There are some native dependencies that you need to make sure are installed if you plan to run the TileServer GL natively without docker.
27
27
  The precise package names you need to install may differ on various platforms.
28
28
 
29
- These are required on Debian 9:
30
- * ``build-essential``
29
+ These are required on Debian 11:
30
+ * ``libgles2-mesa``
31
+ * ``libegl1``
32
+ * ``xvfb``
33
+ * ``xauth``
34
+ * ``libopengl0``
35
+ * ``libcurl4``
36
+ * ``curl``
37
+ * ``libuv1-dev``
38
+ * ``libc6-dev``
39
+ * ``http://archive.ubuntu.com/ubuntu/pool/main/libj/libjpeg-turbo/libjpeg-turbo8_2.0.3-0ubuntu1_amd64.deb``
40
+ * ``http://archive.ubuntu.com/ubuntu/pool/main/i/icu/libicu66_66.1-2ubuntu2_amd64.deb``
41
+
42
+ These are required on Ubuntu 20.04:
31
43
  * ``libcairo2-dev``
32
- * ``libprotobuf-dev``
33
-
44
+ * ``libjpeg8-dev``
45
+ * ``libpango1.0-dev``
46
+ * ``libgif-dev``
47
+ * ``build-essential``
48
+ * ``g++``
49
+ * ``xvfb``
50
+ * ``libgles2-mesa-dev``
51
+ * ``libgbm-dev``
52
+ * ``libxxf86vm-dev``
34
53
 
35
54
  ``tileserver-gl-light`` on npm
36
55
  ==============================
package/docs/usage.rst CHANGED
@@ -29,8 +29,10 @@ Default preview style and configuration
29
29
  - If no configuration file is specified, a default preview style (compatible with openmaptiles) is used.
30
30
  - If no mbtiles file is specified (and is not found in the current working directory), a sample file is downloaded (showing the Zurich area)
31
31
 
32
- Reloading configuration
32
+ Reloading the configuration
33
33
  ======
34
34
 
35
35
  It is possible to reload the configuration file without restarting the whole process by sending a SIGHUP signal to the node process.
36
- However, this does not currently work when running the tileserver-gl docker container (the signal is not passed to the subprocess, see https://github.com/maptiler/tileserver-gl/issues/420#issuecomment-597507663).
36
+
37
+ - The `docker kill -s HUP tileserver-gl` command can be used when running the tileserver-gl docker container.
38
+ - The `docker-compose kill -s HUP tileserver-gl-service-name` can be used when tileserver-gl is run as a docker-compose service.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tileserver-gl",
3
- "version": "3.1.1",
3
+ "version": "4.0.0",
4
4
  "description": "Map tile server for JSON GL styles - vector and server side generated raster tiles",
5
5
  "main": "src/main.js",
6
6
  "bin": "src/main.js",
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "license": "BSD-2-Clause",
12
12
  "engines": {
13
- "node": ">=10 <11"
13
+ "node": ">=14.15.0 <17"
14
14
  },
15
15
  "scripts": {
16
16
  "test": "mocha test/**.js --timeout 10000",
@@ -18,32 +18,32 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "@mapbox/glyph-pbf-composite": "0.0.3",
21
- "@mapbox/mapbox-gl-native": "5.0.2",
22
- "@mapbox/mapbox-gl-style-spec": "13.12.0",
23
- "@mapbox/mbtiles": "0.11.0",
24
- "@mapbox/sphericalmercator": "1.1.0",
21
+ "@maplibre/maplibre-gl-native": "5.0.1-pre.0",
22
+ "@maplibre/maplibre-gl-style-spec": "17.0.1",
23
+ "@mapbox/mbtiles": "0.12.1",
24
+ "@mapbox/sphericalmercator": "1.2.0",
25
25
  "@mapbox/vector-tile": "1.3.1",
26
26
  "advanced-pool": "0.3.3",
27
- "canvas": "2.6.1",
28
- "chokidar": "3.3.1",
27
+ "canvas": "2.10.1",
28
+ "chokidar": "3.5.3",
29
29
  "clone": "2.1.2",
30
- "color": "3.1.2",
31
- "commander": "4.1.1",
30
+ "color": "4.2.3",
31
+ "commander": "9.4.0",
32
32
  "cors": "2.8.5",
33
33
  "esm": "3.2.25",
34
- "express": "4.17.1",
35
- "handlebars": "4.7.3",
34
+ "express": "4.18.1",
35
+ "handlebars": "4.7.7",
36
36
  "http-shutdown": "1.2.2",
37
- "morgan": "1.9.1",
37
+ "morgan": "1.10.0",
38
38
  "pbf": "3.2.1",
39
- "proj4": "2.6.0",
39
+ "proj4": "2.8.0",
40
40
  "request": "2.88.2",
41
- "sharp": "0.26.2",
41
+ "sharp": "0.31.0",
42
42
  "tileserver-gl-styles": "2.0.0"
43
43
  },
44
44
  "devDependencies": {
45
- "mocha": "^7.1.0",
45
+ "mocha": "^10.0.0",
46
46
  "should": "^13.2.3",
47
- "supertest": "^4.0.2"
47
+ "supertest": "^6.2.4"
48
48
  }
49
49
  }
@@ -0,0 +1,243 @@
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
+ });