Merge remote-tracking branch 'origin/master' into pr/605
This commit is contained in:
commit
4f49151375
36 changed files with 1356 additions and 262 deletions
18
Dockerfile
18
Dockerfile
|
|
@ -1,4 +1,4 @@
|
|||
FROM node:10-buster AS builder
|
||||
FROM node:16-bullseye AS builder
|
||||
|
||||
RUN export DEBIAN_FRONTEND=noninteractive \
|
||||
&& apt-get -qq update \
|
||||
|
|
@ -11,7 +11,6 @@ RUN export DEBIAN_FRONTEND=noninteractive \
|
|||
libcairo2-dev \
|
||||
libgles2-mesa-dev \
|
||||
libgbm-dev \
|
||||
libllvm7 \
|
||||
libprotobuf-dev \
|
||||
&& apt-get -y --purge autoremove \
|
||||
&& apt-get clean \
|
||||
|
|
@ -24,7 +23,7 @@ ENV NODE_ENV="production"
|
|||
RUN cd /usr/src/app && npm install --production
|
||||
|
||||
|
||||
FROM node:10-buster-slim AS final
|
||||
FROM node:16-bullseye-slim AS final
|
||||
|
||||
RUN export DEBIAN_FRONTEND=noninteractive \
|
||||
&& apt-get -qq update \
|
||||
|
|
@ -33,10 +32,21 @@ RUN export DEBIAN_FRONTEND=noninteractive \
|
|||
libegl1 \
|
||||
xvfb \
|
||||
xauth \
|
||||
libopengl0 \
|
||||
libcurl4 \
|
||||
curl \
|
||||
libuv1-dev \
|
||||
libc6-dev \
|
||||
libcap2-bin \
|
||||
&& apt-get -y --purge autoremove \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
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
|
||||
RUN apt install ./libjpeg-turbo8_2.0.3-0ubuntu1_amd64.deb
|
||||
RUN curl http://archive.ubuntu.com/ubuntu/pool/main/i/icu/libicu66_66.1-2ubuntu2_amd64.deb --output libicu66_66.1-2ubuntu2_amd64.deb
|
||||
RUN apt install ./libicu66_66.1-2ubuntu2_amd64.deb
|
||||
|
||||
COPY --from=builder /usr/src/app /app
|
||||
|
||||
ENV NODE_ENV="production"
|
||||
|
|
@ -51,5 +61,3 @@ EXPOSE 80
|
|||
USER node:node
|
||||
|
||||
ENTRYPOINT ["/app/docker-entrypoint.sh"]
|
||||
|
||||
CMD ["-p", "80"]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
FROM node:10-stretch
|
||||
FROM node:16-bullseye
|
||||
|
||||
ENV NODE_ENV="production"
|
||||
ENV CHOKIDAR_USEPOLLING=1
|
||||
|
|
@ -6,8 +6,9 @@ ENV CHOKIDAR_INTERVAL=500
|
|||
EXPOSE 80
|
||||
VOLUME /data
|
||||
WORKDIR /data
|
||||
ENTRYPOINT ["node", "/usr/src/app/", "-p", "80"]
|
||||
ENTRYPOINT ["/usr/src/app/docker-entrypoint.sh"]
|
||||
|
||||
RUN mkdir -p /usr/src/app
|
||||
COPY / /usr/src/app
|
||||
RUN cd /usr/src/app && npm install --production
|
||||
RUN ["chmod", "+x", "/usr/src/app/docker-entrypoint.sh"]
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# Simply run "docker build -f Dockerfile_test ."
|
||||
# WARNING: sometimes it fails with a core dumped exception
|
||||
|
||||
FROM node:10-stretch
|
||||
FROM node:16-bullseye
|
||||
|
||||
RUN apt-get -qq update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get -y install \
|
||||
|
|
@ -17,6 +17,7 @@ RUN apt-get -qq update \
|
|||
libllvm3.9 \
|
||||
libprotobuf-dev \
|
||||
libxxf86vm-dev \
|
||||
libopengl0 \
|
||||
xvfb \
|
||||
&& apt-get clean
|
||||
|
||||
|
|
|
|||
65
README.md
65
README.md
|
|
@ -5,43 +5,79 @@
|
|||
[](https://travis-ci.org/maptiler/tileserver-gl)
|
||||
[](https://hub.docker.com/r/maptiler/tileserver-gl/)
|
||||
|
||||
Vector and raster maps with GL styles. Server-side rendering by Mapbox GL Native. Map tile server for MapLibre GL JS, Android, iOS, Leaflet, OpenLayers, GIS via WMTS, etc.
|
||||
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.
|
||||
|
||||
## Get Started
|
||||
Download vector tiles from [OpenMapTiles](https://data.maptiler.com/downloads/planet/).
|
||||
## Getting Started with Node
|
||||
|
||||
Make sure you have Node.js version **10** installed (running `node -v` it should output something like `v10.17.0`).
|
||||
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.
|
||||
|
||||
Install `tileserver-gl` with server-side raster rendering of vector tiles with npm
|
||||
Install `tileserver-gl` with server-side raster rendering of vector tiles with npm.
|
||||
|
||||
```bash
|
||||
npm install -g tileserver-gl
|
||||
```
|
||||
|
||||
Now download vector tiles from [OpenMapTiles](https://data.maptiler.com/downloads/planet/).
|
||||
Once installed, you can use it like the following examples.
|
||||
|
||||
using a mbtiles file
|
||||
```bash
|
||||
curl -o zurich_switzerland.mbtiles https://[GET-YOUR-LINK]/extracts/zurich_switzerland.mbtiles
|
||||
wget https://github.com/maptiler/tileserver-gl/releases/download/v1.3.0/zurich_switzerland.mbtiles
|
||||
tileserver-gl --mbtiles zurich_switzerland.mbtiles
|
||||
[in your browser, visit http://[server ip]:8080]
|
||||
```
|
||||
|
||||
Start `tileserver-gl` with the downloaded vector tiles.
|
||||
|
||||
using a config.json + style + mbtiles file
|
||||
```bash
|
||||
tileserver-gl zurich_switzerland.mbtiles
|
||||
wget https://github.com/maptiler/tileserver-gl/releases/download/v1.3.0/test_data.zip
|
||||
unzip test_data.zip
|
||||
tileserver-gl
|
||||
[in your browser, visit http://[server ip]:8080]
|
||||
```
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
## Using Docker
|
||||
## Getting Started with Docker
|
||||
|
||||
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:
|
||||
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
|
||||
|
||||
Example using a mbtiles file
|
||||
```bash
|
||||
wget https://github.com/maptiler/tileserver-gl/releases/download/v1.3.0/zurich_switzerland.mbtiles
|
||||
docker run --rm -it -v $(pwd):/data -p 8080:80 maptiler/tileserver-gl --mbtiles zurich_switzerland.mbtiles
|
||||
[in your browser, visit http://[server ip]:8080]
|
||||
```
|
||||
|
||||
Example using a config.json + style + mbtiles file
|
||||
```bash
|
||||
wget https://github.com/maptiler/tileserver-gl/releases/download/v1.3.0/test_data.zip
|
||||
unzip test_data.zip
|
||||
docker run --rm -it -v $(pwd):/data -p 8080:80 maptiler/tileserver-gl
|
||||
[in your browser, visit http://[server ip]:8080]
|
||||
```
|
||||
|
||||
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.
|
||||
Example using a different path
|
||||
```bash
|
||||
docker run --rm -it -v /your/local/config/path:/data -p 8080:80 maptiler/tileserver-gl
|
||||
```
|
||||
replace '/your/local/config/path' with the path to your config file
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
## Getting Started with Linux cli
|
||||
|
||||
Test from command line
|
||||
```bash
|
||||
wget https://github.com/maptiler/tileserver-gl/releases/download/v1.3.0/test_data.zip
|
||||
unzip -q test_data.zip -d test_data
|
||||
xvfb-run --server-args="-screen 0 1024x768x24" npm test
|
||||
```
|
||||
|
||||
Run from command line
|
||||
```bash
|
||||
xvfb-run --server-args="-screen 0 1024x768x24" node .
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
|
|
@ -50,3 +86,4 @@ You can read the full documentation of this project at https://tileserver.readth
|
|||
## Alternative
|
||||
|
||||
Discover MapTiler Server if you need a [map server with easy setup and user-friendly interface](https://www.maptiler.com/server/).
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ trap refresh HUP
|
|||
|
||||
if ! which -- "${1}"; then
|
||||
# first arg is not an executable
|
||||
xvfb-run -a --server-args="-screen 0 1024x768x24" -- node /app/ "$@" &
|
||||
xvfb-run -a --server-args="-screen 0 1024x768x24" -- node /app/ -p 80 "$@" &
|
||||
# Wait exits immediately on signals which have traps set. Store return value and wait
|
||||
# again for all jobs to actually complete before continuing.
|
||||
wait $! || RETVAL=$?
|
||||
|
|
|
|||
35
docker-entrypoint_light.sh
Normal file
35
docker-entrypoint_light.sh
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
handle() {
|
||||
SIGNAL=$(( $? - 128 ))
|
||||
echo "Caught signal ${SIGNAL}, stopping gracefully"
|
||||
kill -s ${SIGNAL} $(pidof node) 2>/dev/null
|
||||
}
|
||||
|
||||
trap handle INT TERM
|
||||
|
||||
refresh() {
|
||||
SIGNAL=$(( $? - 128 ))
|
||||
echo "Caught signal ${SIGNAL}, refreshing"
|
||||
kill -s ${SIGNAL} $(pidof node) 2>/dev/null
|
||||
}
|
||||
|
||||
trap refresh HUP
|
||||
|
||||
if ! which -- "${1}"; then
|
||||
# first arg is not an executable
|
||||
node /usr/src/app/ -p 80 "$@" &
|
||||
# Wait exits immediately on signals which have traps set. Store return value and wait
|
||||
# again for all jobs to actually complete before continuing.
|
||||
wait $! || RETVAL=$?
|
||||
while [ ${RETVAL} = 129 ] ; do
|
||||
# Refressh signal HUP received. Continue waiting for signals.
|
||||
wait $! || RETVAL=$?
|
||||
done
|
||||
wait
|
||||
exit ${RETVAL}
|
||||
fi
|
||||
|
||||
exec "$@"
|
||||
|
|
@ -44,7 +44,7 @@ master_doc = 'index'
|
|||
|
||||
# General information about the project.
|
||||
project = u'TileServer GL'
|
||||
copyright = u'2016, Klokan Technologies GmbH'
|
||||
copyright = u'2022, MapTiler.com'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
|
|
@ -197,7 +197,7 @@ latex_elements = {
|
|||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
('index', 'TileServerGL.tex', u'TileServer GL Documentation',
|
||||
u'Klokan Technologies GmbH', 'manual'),
|
||||
u'MapTiler.com', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
|
|
@ -227,7 +227,7 @@ latex_documents = [
|
|||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'tileservergl', u'TileServer GL Documentation',
|
||||
[u'Klokan Technologies GmbH'], 1)
|
||||
[u'MapTiler.com'], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
|
|
@ -241,7 +241,7 @@ man_pages = [
|
|||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'TileServerGL', u'TileServer GL Documentation',
|
||||
u'Klokan Technologies GmbH', 'TileServerGL', 'One line description of project.',
|
||||
u'MapTiler.com', 'TileServerGL', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
Deployment
|
||||
==========
|
||||
|
||||
Typically - you should use nginx/lighttpd/apache on the frontend - and the tileserver-gl server is hidden behind it in production deployment.
|
||||
Typically, you should use nginx, lighttpd or apache on the frontend. The tileserver-gl server is hidden behind it in production deployment.
|
||||
|
||||
Caching
|
||||
=======
|
||||
|
|
@ -17,4 +17,4 @@ Nginx can be used to add protection via https, password, referrer, IP address re
|
|||
Running behind a proxy or a load-balancer
|
||||
=========================================
|
||||
|
||||
If you need to run TileServer GL behind a proxy, make sure the proxy sends ``X-Forwarded-*`` headers to the server (most importantly ``X-Forwarded-Host`` and ``X-Forwarded-Proto``) to ensures the URLs generated inside TileJSON etc. are using the desired domain and protocol.
|
||||
If you need to run TileServer GL behind a proxy, make sure the proxy sends ``X-Forwarded-*`` headers to the server (most importantly ``X-Forwarded-Host`` and ``X-Forwarded-Proto``) to ensure the URLs generated inside TileJSON, etc. are using the desired domain and protocol.
|
||||
|
|
|
|||
|
|
@ -26,11 +26,30 @@ Native dependencies
|
|||
There are some native dependencies that you need to make sure are installed if you plan to run the TileServer GL natively without docker.
|
||||
The precise package names you need to install may differ on various platforms.
|
||||
|
||||
These are required on Debian 9:
|
||||
* ``build-essential``
|
||||
* ``libcairo2-dev``
|
||||
* ``libprotobuf-dev``
|
||||
These are required on Debian 11:
|
||||
* ``libgles2-mesa``
|
||||
* ``libegl1``
|
||||
* ``xvfb``
|
||||
* ``xauth``
|
||||
* ``libopengl0``
|
||||
* ``libcurl4``
|
||||
* ``curl``
|
||||
* ``libuv1-dev``
|
||||
* ``libc6-dev``
|
||||
* ``http://archive.ubuntu.com/ubuntu/pool/main/libj/libjpeg-turbo/libjpeg-turbo8_2.0.3-0ubuntu1_amd64.deb``
|
||||
* ``http://archive.ubuntu.com/ubuntu/pool/main/i/icu/libicu66_66.1-2ubuntu2_amd64.deb``
|
||||
|
||||
These are required on Ubuntu 20.04:
|
||||
* ``libcairo2-dev``
|
||||
* ``libjpeg8-dev``
|
||||
* ``libpango1.0-dev``
|
||||
* ``libgif-dev``
|
||||
* ``build-essential``
|
||||
* ``g++``
|
||||
* ``xvfb``
|
||||
* ``libgles2-mesa-dev``
|
||||
* ``libgbm-dev``
|
||||
* ``libxxf86vm-dev``
|
||||
|
||||
``tileserver-gl-light`` on npm
|
||||
==============================
|
||||
|
|
|
|||
34
package.json
34
package.json
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "tileserver-gl",
|
||||
"version": "3.1.1",
|
||||
"version": "4.0.0",
|
||||
"description": "Map tile server for JSON GL styles - vector and server side generated raster tiles",
|
||||
"main": "src/main.js",
|
||||
"bin": "src/main.js",
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
},
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=10 <11"
|
||||
"node": ">=14.15.0 <17"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha test/**.js --timeout 10000",
|
||||
|
|
@ -18,32 +18,32 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@mapbox/glyph-pbf-composite": "0.0.3",
|
||||
"@mapbox/mapbox-gl-native": "5.0.2",
|
||||
"@mapbox/mapbox-gl-style-spec": "13.12.0",
|
||||
"@mapbox/mbtiles": "0.11.0",
|
||||
"@mapbox/sphericalmercator": "1.1.0",
|
||||
"@maplibre/maplibre-gl-native": "5.0.1-pre.0",
|
||||
"@maplibre/maplibre-gl-style-spec": "17.0.1",
|
||||
"@mapbox/mbtiles": "0.12.1",
|
||||
"@mapbox/sphericalmercator": "1.2.0",
|
||||
"@mapbox/vector-tile": "1.3.1",
|
||||
"advanced-pool": "0.3.3",
|
||||
"canvas": "2.6.1",
|
||||
"chokidar": "3.3.1",
|
||||
"canvas": "2.10.1",
|
||||
"chokidar": "3.5.3",
|
||||
"clone": "2.1.2",
|
||||
"color": "3.1.2",
|
||||
"commander": "4.1.1",
|
||||
"color": "4.2.3",
|
||||
"commander": "9.4.0",
|
||||
"cors": "2.8.5",
|
||||
"esm": "3.2.25",
|
||||
"express": "4.17.1",
|
||||
"handlebars": "4.7.3",
|
||||
"express": "4.18.1",
|
||||
"handlebars": "4.7.7",
|
||||
"http-shutdown": "1.2.2",
|
||||
"morgan": "1.9.1",
|
||||
"morgan": "1.10.0",
|
||||
"pbf": "3.2.1",
|
||||
"proj4": "2.6.0",
|
||||
"proj4": "2.8.0",
|
||||
"request": "2.88.2",
|
||||
"sharp": "0.26.2",
|
||||
"sharp": "0.31.0",
|
||||
"tileserver-gl-styles": "2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "^7.1.0",
|
||||
"mocha": "^10.0.0",
|
||||
"should": "^13.2.3",
|
||||
"supertest": "^4.0.2"
|
||||
"supertest": "^6.2.4"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
243
public/resources/L.TileLayer.NoGap.js
Normal file
243
public/resources/L.TileLayer.NoGap.js
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
// @class TileLayer
|
||||
|
||||
L.TileLayer.mergeOptions({
|
||||
// @option keepBuffer
|
||||
// The amount of tiles outside the visible map area to be kept in the stitched
|
||||
// `TileLayer`.
|
||||
|
||||
// @option dumpToCanvas: Boolean = true
|
||||
// Whether to dump loaded tiles to a `<canvas>` to prevent some rendering
|
||||
// artifacts. (Disabled by default in IE)
|
||||
dumpToCanvas: L.Browser.canvas && !L.Browser.ie,
|
||||
});
|
||||
|
||||
L.TileLayer.include({
|
||||
_onUpdateLevel: function(z, zoom) {
|
||||
if (this.options.dumpToCanvas) {
|
||||
this._levels[z].canvas.style.zIndex =
|
||||
this.options.maxZoom - Math.abs(zoom - z);
|
||||
}
|
||||
},
|
||||
|
||||
_onRemoveLevel: function(z) {
|
||||
if (this.options.dumpToCanvas) {
|
||||
L.DomUtil.remove(this._levels[z].canvas);
|
||||
}
|
||||
},
|
||||
|
||||
_onCreateLevel: function(level) {
|
||||
if (this.options.dumpToCanvas) {
|
||||
level.canvas = L.DomUtil.create(
|
||||
"canvas",
|
||||
"leaflet-tile-container leaflet-zoom-animated",
|
||||
this._container
|
||||
);
|
||||
level.ctx = level.canvas.getContext("2d");
|
||||
this._resetCanvasSize(level);
|
||||
}
|
||||
},
|
||||
|
||||
_removeTile: function(key) {
|
||||
if (this.options.dumpToCanvas) {
|
||||
var tile = this._tiles[key];
|
||||
var level = this._levels[tile.coords.z];
|
||||
var tileSize = this.getTileSize();
|
||||
|
||||
if (level) {
|
||||
// Where in the canvas should this tile go?
|
||||
var offset = L.point(tile.coords.x, tile.coords.y)
|
||||
.subtract(level.canvasRange.min)
|
||||
.scaleBy(this.getTileSize());
|
||||
|
||||
level.ctx.clearRect(offset.x, offset.y, tileSize.x, tileSize.y);
|
||||
}
|
||||
}
|
||||
|
||||
L.GridLayer.prototype._removeTile.call(this, key);
|
||||
},
|
||||
|
||||
_resetCanvasSize: function(level) {
|
||||
var buff = this.options.keepBuffer,
|
||||
pixelBounds = this._getTiledPixelBounds(this._map.getCenter()),
|
||||
tileRange = this._pxBoundsToTileRange(pixelBounds),
|
||||
tileSize = this.getTileSize();
|
||||
|
||||
tileRange.min = tileRange.min.subtract([buff, buff]); // This adds the no-prune buffer
|
||||
tileRange.max = tileRange.max.add([buff + 1, buff + 1]);
|
||||
|
||||
var pixelRange = L.bounds(
|
||||
tileRange.min.scaleBy(tileSize),
|
||||
tileRange.max.add([1, 1]).scaleBy(tileSize) // This prevents an off-by-one when checking if tiles are inside
|
||||
),
|
||||
mustRepositionCanvas = false,
|
||||
neededSize = pixelRange.max.subtract(pixelRange.min);
|
||||
|
||||
// Resize the canvas, if needed, and only to make it bigger.
|
||||
if (
|
||||
neededSize.x > level.canvas.width ||
|
||||
neededSize.y > level.canvas.height
|
||||
) {
|
||||
// Resizing canvases erases the currently drawn content, I'm afraid.
|
||||
// To keep it, dump the pixels to another canvas, then display it on
|
||||
// top. This could be done with getImageData/putImageData, but that
|
||||
// would break for tainted canvases (in non-CORS tilesets)
|
||||
var oldSize = { x: level.canvas.width, y: level.canvas.height };
|
||||
// console.info('Resizing canvas from ', oldSize, 'to ', neededSize);
|
||||
|
||||
var tmpCanvas = L.DomUtil.create("canvas");
|
||||
tmpCanvas.style.width = (tmpCanvas.width = oldSize.x) + "px";
|
||||
tmpCanvas.style.height = (tmpCanvas.height = oldSize.y) + "px";
|
||||
tmpCanvas.getContext("2d").drawImage(level.canvas, 0, 0);
|
||||
// var data = level.ctx.getImageData(0, 0, oldSize.x, oldSize.y);
|
||||
|
||||
level.canvas.style.width = (level.canvas.width = neededSize.x) + "px";
|
||||
level.canvas.style.height = (level.canvas.height = neededSize.y) + "px";
|
||||
level.ctx.drawImage(tmpCanvas, 0, 0);
|
||||
// level.ctx.putImageData(data, 0, 0, 0, 0, oldSize.x, oldSize.y);
|
||||
}
|
||||
|
||||
// Translate the canvas contents if it's moved around
|
||||
if (level.canvasRange) {
|
||||
var offset = level.canvasRange.min
|
||||
.subtract(tileRange.min)
|
||||
.scaleBy(this.getTileSize());
|
||||
|
||||
// console.info('Offsetting by ', offset);
|
||||
|
||||
if (!L.Browser.safari) {
|
||||
// By default, canvases copy things "on top of" existing pixels, but we want
|
||||
// this to *replace* the existing pixels when doing a drawImage() call.
|
||||
// This will also clear the sides, so no clearRect() calls are needed to make room
|
||||
// for the new tiles.
|
||||
level.ctx.globalCompositeOperation = "copy";
|
||||
level.ctx.drawImage(level.canvas, offset.x, offset.y);
|
||||
level.ctx.globalCompositeOperation = "source-over";
|
||||
} else {
|
||||
// Safari clears the canvas when copying from itself :-(
|
||||
if (!this._tmpCanvas) {
|
||||
var t = (this._tmpCanvas = L.DomUtil.create("canvas"));
|
||||
t.width = level.canvas.width;
|
||||
t.height = level.canvas.height;
|
||||
this._tmpContext = t.getContext("2d");
|
||||
}
|
||||
this._tmpContext.clearRect(
|
||||
0,
|
||||
0,
|
||||
level.canvas.width,
|
||||
level.canvas.height
|
||||
);
|
||||
this._tmpContext.drawImage(level.canvas, 0, 0);
|
||||
level.ctx.clearRect(0, 0, level.canvas.width, level.canvas.height);
|
||||
level.ctx.drawImage(this._tmpCanvas, offset.x, offset.y);
|
||||
}
|
||||
|
||||
mustRepositionCanvas = true; // Wait until new props are set
|
||||
}
|
||||
|
||||
level.canvasRange = tileRange;
|
||||
level.canvasPxRange = pixelRange;
|
||||
level.canvasOrigin = pixelRange.min;
|
||||
|
||||
// console.log('Canvas tile range: ', level, tileRange.min, tileRange.max );
|
||||
// console.log('Canvas pixel range: ', pixelRange.min, pixelRange.max );
|
||||
// console.log('Level origin: ', level.origin );
|
||||
|
||||
if (mustRepositionCanvas) {
|
||||
this._setCanvasZoomTransform(
|
||||
level,
|
||||
this._map.getCenter(),
|
||||
this._map.getZoom()
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
/// set transform/position of canvas, in addition to the transform/position of the individual tile container
|
||||
_setZoomTransform: function(level, center, zoom) {
|
||||
L.GridLayer.prototype._setZoomTransform.call(this, level, center, zoom);
|
||||
if (this.options.dumpToCanvas) {
|
||||
this._setCanvasZoomTransform(level, center, zoom);
|
||||
}
|
||||
},
|
||||
|
||||
// This will get called twice:
|
||||
// * From _setZoomTransform
|
||||
// * When the canvas has shifted due to a new tile being loaded
|
||||
_setCanvasZoomTransform: function(level, center, zoom) {
|
||||
// console.log('_setCanvasZoomTransform', level, center, zoom);
|
||||
if (!level.canvasOrigin) {
|
||||
return;
|
||||
}
|
||||
var scale = this._map.getZoomScale(zoom, level.zoom),
|
||||
translate = level.canvasOrigin
|
||||
.multiplyBy(scale)
|
||||
.subtract(this._map._getNewPixelOrigin(center, zoom))
|
||||
.round();
|
||||
|
||||
if (L.Browser.any3d) {
|
||||
L.DomUtil.setTransform(level.canvas, translate, scale);
|
||||
} else {
|
||||
L.DomUtil.setPosition(level.canvas, translate);
|
||||
}
|
||||
},
|
||||
|
||||
_onOpaqueTile: function(tile) {
|
||||
if (!this.options.dumpToCanvas) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Guard against an NS_ERROR_NOT_AVAILABLE (or similar) exception
|
||||
// when a non-image-tile has been loaded (e.g. a WMS error).
|
||||
// Checking for tile.el.complete is not enough, as it has been
|
||||
// already marked as loaded and ready somehow.
|
||||
try {
|
||||
this.dumpPixels(tile.coords, tile.el);
|
||||
} catch (ex) {
|
||||
return this.fire("tileerror", {
|
||||
error: "Could not copy tile pixels: " + ex,
|
||||
tile: tile,
|
||||
coods: tile.coords,
|
||||
});
|
||||
}
|
||||
|
||||
// If dumping the pixels was successful, then hide the tile.
|
||||
// Do not remove the tile itself, as it is needed to check if the whole
|
||||
// level (and its canvas) should be removed (via level.el.children.length)
|
||||
tile.el.style.display = "none";
|
||||
},
|
||||
|
||||
// @section Extension methods
|
||||
// @uninheritable
|
||||
|
||||
// @method dumpPixels(coords: Object, imageSource: CanvasImageSource): this
|
||||
// Dumps pixels from the given `CanvasImageSource` into the layer, into
|
||||
// the space for the tile represented by the `coords` tile coordinates (an object
|
||||
// like `{x: Number, y: Number, z: Number}`; the image source must have the
|
||||
// same size as the `tileSize` option for the layer. Has no effect if `dumpToCanvas`
|
||||
// is `false`.
|
||||
dumpPixels: function(coords, imageSource) {
|
||||
var level = this._levels[coords.z],
|
||||
tileSize = this.getTileSize();
|
||||
|
||||
if (!level.canvasRange || !this.options.dumpToCanvas) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the tile is inside the currently visible map bounds
|
||||
// There is a possible race condition when tiles are loaded after they
|
||||
// have been panned outside of the map.
|
||||
if (!level.canvasRange.contains(coords)) {
|
||||
this._resetCanvasSize(level);
|
||||
}
|
||||
|
||||
// Where in the canvas should this tile go?
|
||||
var offset = L.point(coords.x, coords.y)
|
||||
.subtract(level.canvasRange.min)
|
||||
.scaleBy(this.getTileSize());
|
||||
|
||||
level.ctx.drawImage(imageSource, offset.x, offset.y, tileSize.x, tileSize.y);
|
||||
|
||||
// TODO: Clear the pixels of other levels' canvases where they overlap
|
||||
// this newly dumped tile.
|
||||
return this;
|
||||
},
|
||||
});
|
||||
657
public/resources/leaflet.css
Normal file
657
public/resources/leaflet.css
Normal file
|
|
@ -0,0 +1,657 @@
|
|||
/* required styles */
|
||||
|
||||
.leaflet-pane,
|
||||
.leaflet-tile,
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow,
|
||||
.leaflet-tile-container,
|
||||
.leaflet-pane > svg,
|
||||
.leaflet-pane > canvas,
|
||||
.leaflet-zoom-box,
|
||||
.leaflet-image-layer,
|
||||
.leaflet-layer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
.leaflet-container {
|
||||
overflow: hidden;
|
||||
}
|
||||
.leaflet-tile,
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-user-drag: none;
|
||||
}
|
||||
/* Prevents IE11 from highlighting tiles in blue */
|
||||
.leaflet-tile::selection {
|
||||
background: transparent;
|
||||
}
|
||||
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
|
||||
.leaflet-safari .leaflet-tile {
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
}
|
||||
/* hack that prevents hw layers "stretching" when loading new tiles */
|
||||
.leaflet-safari .leaflet-tile-container {
|
||||
width: 1600px;
|
||||
height: 1600px;
|
||||
-webkit-transform-origin: 0 0;
|
||||
}
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow {
|
||||
display: block;
|
||||
}
|
||||
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
|
||||
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
|
||||
.leaflet-container .leaflet-overlay-pane svg {
|
||||
max-width: none !important;
|
||||
max-height: none !important;
|
||||
}
|
||||
.leaflet-container .leaflet-marker-pane img,
|
||||
.leaflet-container .leaflet-shadow-pane img,
|
||||
.leaflet-container .leaflet-tile-pane img,
|
||||
.leaflet-container img.leaflet-image-layer,
|
||||
.leaflet-container .leaflet-tile {
|
||||
max-width: none !important;
|
||||
max-height: none !important;
|
||||
width: auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.leaflet-container.leaflet-touch-zoom {
|
||||
-ms-touch-action: pan-x pan-y;
|
||||
touch-action: pan-x pan-y;
|
||||
}
|
||||
.leaflet-container.leaflet-touch-drag {
|
||||
-ms-touch-action: pinch-zoom;
|
||||
/* Fallback for FF which doesn't support pinch-zoom */
|
||||
touch-action: none;
|
||||
touch-action: pinch-zoom;
|
||||
}
|
||||
.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.leaflet-container {
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.leaflet-container a {
|
||||
-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
|
||||
}
|
||||
.leaflet-tile {
|
||||
filter: inherit;
|
||||
visibility: hidden;
|
||||
}
|
||||
.leaflet-tile-loaded {
|
||||
visibility: inherit;
|
||||
}
|
||||
.leaflet-zoom-box {
|
||||
width: 0;
|
||||
height: 0;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
z-index: 800;
|
||||
}
|
||||
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
|
||||
.leaflet-overlay-pane svg {
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.leaflet-pane { z-index: 400; }
|
||||
|
||||
.leaflet-tile-pane { z-index: 200; }
|
||||
.leaflet-overlay-pane { z-index: 400; }
|
||||
.leaflet-shadow-pane { z-index: 500; }
|
||||
.leaflet-marker-pane { z-index: 600; }
|
||||
.leaflet-tooltip-pane { z-index: 650; }
|
||||
.leaflet-popup-pane { z-index: 700; }
|
||||
|
||||
.leaflet-map-pane canvas { z-index: 100; }
|
||||
.leaflet-map-pane svg { z-index: 200; }
|
||||
|
||||
.leaflet-vml-shape {
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
}
|
||||
.lvml {
|
||||
behavior: url(#default#VML);
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
|
||||
/* control positioning */
|
||||
|
||||
.leaflet-control {
|
||||
position: relative;
|
||||
z-index: 800;
|
||||
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
|
||||
pointer-events: auto;
|
||||
}
|
||||
.leaflet-top,
|
||||
.leaflet-bottom {
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
pointer-events: none;
|
||||
}
|
||||
.leaflet-top {
|
||||
top: 0;
|
||||
}
|
||||
.leaflet-right {
|
||||
right: 0;
|
||||
}
|
||||
.leaflet-bottom {
|
||||
bottom: 0;
|
||||
}
|
||||
.leaflet-left {
|
||||
left: 0;
|
||||
}
|
||||
.leaflet-control {
|
||||
float: left;
|
||||
clear: both;
|
||||
}
|
||||
.leaflet-right .leaflet-control {
|
||||
float: right;
|
||||
}
|
||||
.leaflet-top .leaflet-control {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.leaflet-bottom .leaflet-control {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.leaflet-left .leaflet-control {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.leaflet-right .leaflet-control {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
|
||||
/* zoom and fade animations */
|
||||
|
||||
.leaflet-fade-anim .leaflet-popup {
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity 0.2s linear;
|
||||
-moz-transition: opacity 0.2s linear;
|
||||
transition: opacity 0.2s linear;
|
||||
}
|
||||
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
|
||||
opacity: 1;
|
||||
}
|
||||
.leaflet-zoom-animated {
|
||||
-webkit-transform-origin: 0 0;
|
||||
-ms-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
svg.leaflet-zoom-animated {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.leaflet-zoom-anim .leaflet-zoom-animated {
|
||||
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||
-moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||
transition: transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||
}
|
||||
.leaflet-zoom-anim .leaflet-tile,
|
||||
.leaflet-pan-anim .leaflet-tile {
|
||||
-webkit-transition: none;
|
||||
-moz-transition: none;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.leaflet-zoom-anim .leaflet-zoom-hide {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
|
||||
/* cursors */
|
||||
|
||||
.leaflet-interactive {
|
||||
cursor: pointer;
|
||||
}
|
||||
.leaflet-grab {
|
||||
cursor: -webkit-grab;
|
||||
cursor: -moz-grab;
|
||||
cursor: grab;
|
||||
}
|
||||
.leaflet-crosshair,
|
||||
.leaflet-crosshair .leaflet-interactive {
|
||||
cursor: crosshair;
|
||||
}
|
||||
.leaflet-popup-pane,
|
||||
.leaflet-control {
|
||||
cursor: auto;
|
||||
}
|
||||
.leaflet-dragging .leaflet-grab,
|
||||
.leaflet-dragging .leaflet-grab .leaflet-interactive,
|
||||
.leaflet-dragging .leaflet-marker-draggable {
|
||||
cursor: move;
|
||||
cursor: -webkit-grabbing;
|
||||
cursor: -moz-grabbing;
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
/* marker & overlays interactivity */
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow,
|
||||
.leaflet-image-layer,
|
||||
.leaflet-pane > svg path,
|
||||
.leaflet-tile-container {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.leaflet-marker-icon.leaflet-interactive,
|
||||
.leaflet-image-layer.leaflet-interactive,
|
||||
.leaflet-pane > svg path.leaflet-interactive,
|
||||
svg.leaflet-image-layer.leaflet-interactive path {
|
||||
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
/* visual tweaks */
|
||||
|
||||
.leaflet-container {
|
||||
background: #ddd;
|
||||
outline-offset: 1px;
|
||||
}
|
||||
.leaflet-container a {
|
||||
color: #0078A8;
|
||||
}
|
||||
.leaflet-zoom-box {
|
||||
border: 2px dotted #38f;
|
||||
background: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
|
||||
/* general typography */
|
||||
.leaflet-container {
|
||||
font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
|
||||
font-size: 12px;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
|
||||
/* general toolbar styles */
|
||||
|
||||
.leaflet-bar {
|
||||
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
|
||||
border-radius: 4px;
|
||||
}
|
||||
.leaflet-bar a {
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #ccc;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
display: block;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
}
|
||||
.leaflet-bar a,
|
||||
.leaflet-control-layers-toggle {
|
||||
background-position: 50% 50%;
|
||||
background-repeat: no-repeat;
|
||||
display: block;
|
||||
}
|
||||
.leaflet-bar a:hover,
|
||||
.leaflet-bar a:focus {
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
.leaflet-bar a:first-child {
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
.leaflet-bar a:last-child {
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
border-bottom: none;
|
||||
}
|
||||
.leaflet-bar a.leaflet-disabled {
|
||||
cursor: default;
|
||||
background-color: #f4f4f4;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-bar a {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
.leaflet-touch .leaflet-bar a:first-child {
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 2px;
|
||||
}
|
||||
.leaflet-touch .leaflet-bar a:last-child {
|
||||
border-bottom-left-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
}
|
||||
|
||||
/* zoom control */
|
||||
|
||||
.leaflet-control-zoom-in,
|
||||
.leaflet-control-zoom-out {
|
||||
font: bold 18px 'Lucida Console', Monaco, monospace;
|
||||
text-indent: 1px;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
|
||||
/* layers control */
|
||||
|
||||
.leaflet-control-layers {
|
||||
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
|
||||
background: #fff;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.leaflet-control-layers-toggle {
|
||||
background-image: url(images/layers.png);
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
.leaflet-retina .leaflet-control-layers-toggle {
|
||||
background-image: url(images/layers-2x.png);
|
||||
background-size: 26px 26px;
|
||||
}
|
||||
.leaflet-touch .leaflet-control-layers-toggle {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
}
|
||||
.leaflet-control-layers .leaflet-control-layers-list,
|
||||
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
|
||||
display: none;
|
||||
}
|
||||
.leaflet-control-layers-expanded .leaflet-control-layers-list {
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
.leaflet-control-layers-expanded {
|
||||
padding: 6px 10px 6px 6px;
|
||||
color: #333;
|
||||
background: #fff;
|
||||
}
|
||||
.leaflet-control-layers-scrollbar {
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.leaflet-control-layers-selector {
|
||||
margin-top: 2px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
.leaflet-control-layers label {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
font-size: 1.08333em;
|
||||
}
|
||||
.leaflet-control-layers-separator {
|
||||
height: 0;
|
||||
border-top: 1px solid #ddd;
|
||||
margin: 5px -10px 5px -6px;
|
||||
}
|
||||
|
||||
/* Default icon URLs */
|
||||
.leaflet-default-icon-path { /* used only in path-guessing heuristic, see L.Icon.Default */
|
||||
background-image: url(images/marker-icon.png);
|
||||
}
|
||||
|
||||
|
||||
/* attribution and scale controls */
|
||||
|
||||
.leaflet-container .leaflet-control-attribution {
|
||||
background: #fff;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
margin: 0;
|
||||
}
|
||||
.leaflet-control-attribution,
|
||||
.leaflet-control-scale-line {
|
||||
padding: 0 5px;
|
||||
color: #333;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.leaflet-control-attribution a {
|
||||
text-decoration: none;
|
||||
}
|
||||
.leaflet-control-attribution a:hover,
|
||||
.leaflet-control-attribution a:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.leaflet-control-attribution svg {
|
||||
display: inline !important;
|
||||
}
|
||||
.leaflet-left .leaflet-control-scale {
|
||||
margin-left: 5px;
|
||||
}
|
||||
.leaflet-bottom .leaflet-control-scale {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.leaflet-control-scale-line {
|
||||
border: 2px solid #777;
|
||||
border-top: none;
|
||||
line-height: 1.1;
|
||||
padding: 2px 5px 1px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
|
||||
background: #fff;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
.leaflet-control-scale-line:not(:first-child) {
|
||||
border-top: 2px solid #777;
|
||||
border-bottom: none;
|
||||
margin-top: -2px;
|
||||
}
|
||||
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
|
||||
border-bottom: 2px solid #777;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-control-attribution,
|
||||
.leaflet-touch .leaflet-control-layers,
|
||||
.leaflet-touch .leaflet-bar {
|
||||
box-shadow: none;
|
||||
}
|
||||
.leaflet-touch .leaflet-control-layers,
|
||||
.leaflet-touch .leaflet-bar {
|
||||
border: 2px solid rgba(0,0,0,0.2);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
|
||||
/* popup */
|
||||
|
||||
.leaflet-popup {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.leaflet-popup-content-wrapper {
|
||||
padding: 1px;
|
||||
text-align: left;
|
||||
border-radius: 12px;
|
||||
}
|
||||
.leaflet-popup-content {
|
||||
margin: 13px 24px 13px 20px;
|
||||
line-height: 1.3;
|
||||
font-size: 13px;
|
||||
font-size: 1.08333em;
|
||||
min-height: 1px;
|
||||
}
|
||||
.leaflet-popup-content p {
|
||||
margin: 17px 0;
|
||||
margin: 1.3em 0;
|
||||
}
|
||||
.leaflet-popup-tip-container {
|
||||
width: 40px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
margin-top: -1px;
|
||||
margin-left: -20px;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
.leaflet-popup-tip {
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
padding: 1px;
|
||||
|
||||
margin: -10px auto 0;
|
||||
pointer-events: auto;
|
||||
|
||||
-webkit-transform: rotate(45deg);
|
||||
-moz-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.leaflet-popup-content-wrapper,
|
||||
.leaflet-popup-tip {
|
||||
background: white;
|
||||
color: #333;
|
||||
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
|
||||
}
|
||||
.leaflet-container a.leaflet-popup-close-button {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
border: none;
|
||||
text-align: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
font: 16px/24px Tahoma, Verdana, sans-serif;
|
||||
color: #757575;
|
||||
text-decoration: none;
|
||||
background: transparent;
|
||||
}
|
||||
.leaflet-container a.leaflet-popup-close-button:hover,
|
||||
.leaflet-container a.leaflet-popup-close-button:focus {
|
||||
color: #585858;
|
||||
}
|
||||
.leaflet-popup-scrolled {
|
||||
overflow: auto;
|
||||
border-bottom: 1px solid #ddd;
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-popup-content-wrapper {
|
||||
-ms-zoom: 1;
|
||||
}
|
||||
.leaflet-oldie .leaflet-popup-tip {
|
||||
width: 24px;
|
||||
margin: 0 auto;
|
||||
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
|
||||
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-control-zoom,
|
||||
.leaflet-oldie .leaflet-control-layers,
|
||||
.leaflet-oldie .leaflet-popup-content-wrapper,
|
||||
.leaflet-oldie .leaflet-popup-tip {
|
||||
border: 1px solid #999;
|
||||
}
|
||||
|
||||
|
||||
/* div icon */
|
||||
|
||||
.leaflet-div-icon {
|
||||
background: #fff;
|
||||
border: 1px solid #666;
|
||||
}
|
||||
|
||||
|
||||
/* Tooltip */
|
||||
/* Base styles for the element that has a tooltip */
|
||||
.leaflet-tooltip {
|
||||
position: absolute;
|
||||
padding: 6px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #fff;
|
||||
border-radius: 3px;
|
||||
color: #222;
|
||||
white-space: nowrap;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.4);
|
||||
}
|
||||
.leaflet-tooltip.leaflet-interactive {
|
||||
cursor: pointer;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.leaflet-tooltip-top:before,
|
||||
.leaflet-tooltip-bottom:before,
|
||||
.leaflet-tooltip-left:before,
|
||||
.leaflet-tooltip-right:before {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
border: 6px solid transparent;
|
||||
background: transparent;
|
||||
content: "";
|
||||
}
|
||||
|
||||
/* Directions */
|
||||
|
||||
.leaflet-tooltip-bottom {
|
||||
margin-top: 6px;
|
||||
}
|
||||
.leaflet-tooltip-top {
|
||||
margin-top: -6px;
|
||||
}
|
||||
.leaflet-tooltip-bottom:before,
|
||||
.leaflet-tooltip-top:before {
|
||||
left: 50%;
|
||||
margin-left: -6px;
|
||||
}
|
||||
.leaflet-tooltip-top:before {
|
||||
bottom: 0;
|
||||
margin-bottom: -12px;
|
||||
border-top-color: #fff;
|
||||
}
|
||||
.leaflet-tooltip-bottom:before {
|
||||
top: 0;
|
||||
margin-top: -12px;
|
||||
margin-left: -6px;
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
.leaflet-tooltip-left {
|
||||
margin-left: -6px;
|
||||
}
|
||||
.leaflet-tooltip-right {
|
||||
margin-left: 6px;
|
||||
}
|
||||
.leaflet-tooltip-left:before,
|
||||
.leaflet-tooltip-right:before {
|
||||
top: 50%;
|
||||
margin-top: -6px;
|
||||
}
|
||||
.leaflet-tooltip-left:before {
|
||||
right: 0;
|
||||
margin-right: -12px;
|
||||
border-left-color: #fff;
|
||||
}
|
||||
.leaflet-tooltip-right:before {
|
||||
left: 0;
|
||||
margin-left: -12px;
|
||||
border-right-color: #fff;
|
||||
}
|
||||
|
||||
/* Printing */
|
||||
|
||||
@media print {
|
||||
/* Prevent printers from removing background-images of controls. */
|
||||
.leaflet-control {
|
||||
-webkit-print-color-adjust: exact;
|
||||
color-adjust: exact;
|
||||
}
|
||||
}
|
||||
6
public/resources/leaflet.js
Normal file
6
public/resources/leaflet.js
Normal file
File diff suppressed because one or more lines are too long
1
public/resources/leaflet.js.map
Normal file
1
public/resources/leaflet.js.map
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1,40 +0,0 @@
|
|||
.mapbox-gl-inspect_popup {
|
||||
color: #333;
|
||||
display: table;
|
||||
}
|
||||
|
||||
.mapbox-gl-inspect_feature:not(:last-child) {
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.mapbox-gl-inspect_layer:before {
|
||||
content: '#';
|
||||
}
|
||||
|
||||
.mapbox-gl-inspect_layer {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.mapbox-gl-inspect_property {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.mapbox-gl-inspect_property-value {
|
||||
display: table-cell;
|
||||
|
||||
}
|
||||
|
||||
.mapbox-gl-inspect_property-name {
|
||||
display: table-cell;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.mapboxgl-ctrl-inspect {
|
||||
background-image: url('data:image/svg+xml;charset=utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="#333333%22%20preserveAspectRatio=%22xMidYMid%20meet%22%20viewBox=%22-10%20-10%2060%2060%22%3E%3Cg%3E%3Cpath%20d=%22m15%2021.6q0-2%201.5-3.5t3.5-1.5%203.5%201.5%201.5%203.5-1.5%203.6-3.5%201.4-3.5-1.4-1.5-3.6z%20m18.4%2011.1l-6.4-6.5q1.4-2.1%201.4-4.6%200-3.4-2.5-5.8t-5.9-2.4-5.9%202.4-2.5%205.8%202.5%205.9%205.9%202.5q2.4%200%204.6-1.4l7.4%207.4q-0.9%200.6-2%200.6h-20q-1.3%200-2.3-0.9t-1.1-2.3l0.1-26.8q0-1.3%201-2.3t2.3-0.9h13.4l10%2010v19.3z%22%3E%3C/path%3E%3C/g%3E%3C/svg%3E');
|
||||
}
|
||||
|
||||
.mapboxgl-ctrl-map {
|
||||
background-image: url('data:image/svg+xml;charset=utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="#333333%22%20viewBox=%22-10%20-10%2060%2060%22%20preserveAspectRatio=%22xMidYMid%20meet%22%3E%3Cg%3E%3Cpath%20d=%22m25%2031.640000000000004v-19.766666666666673l-10-3.511666666666663v19.766666666666666z%20m9.140000000000008-26.640000000000004q0.8599999999999923%200%200.8599999999999923%200.8600000000000003v25.156666666666666q0%200.625-0.625%200.783333333333335l-9.375%203.1999999999999993-10-3.5133333333333354-8.906666666666668%203.4383333333333326-0.2333333333333334%200.07833333333333314q-0.8616666666666664%200-0.8616666666666664-0.8599999999999994v-25.156666666666663q0-0.625%200.6233333333333331-0.7833333333333332l9.378333333333334-3.198333333333334%2010%203.5133333333333336%208.905000000000001-3.4383333333333344z%22%3E%3C/path%3E%3C/g%3E%3C/svg%3E');
|
||||
}
|
||||
|
||||
1
public/resources/mapbox-gl-inspect.min.js
vendored
1
public/resources/mapbox-gl-inspect.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
public/resources/maplibre-gl-compat.css
Normal file
1
public/resources/maplibre-gl-compat.css
Normal file
File diff suppressed because one or more lines are too long
45
public/resources/maplibre-gl-compat.js
Normal file
45
public/resources/maplibre-gl-compat.js
Normal file
File diff suppressed because one or more lines are too long
1
public/resources/maplibre-gl-compat.js.map
Normal file
1
public/resources/maplibre-gl-compat.js.map
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"maplibre-gl-compat.js","sources":["../rollup/maplibregl.js"],"sourcesContent":["//\n// Our custom intro provides a specialized \"define()\" function, called by the\n// AMD modules below, that sets up the worker blob URL and then executes the\n// main module, storing its exported value as 'maplibregl'\n\n// The three \"chunks\" imported here are produced by a first Rollup pass,\n// which outputs them as AMD modules.\n\n// Shared dependencies, i.e.:\n/*\ndefine(['exports'], function (exports) {\n // Code for all common dependencies\n // Each module's exports are attached attached to 'exports' (with\n // names rewritten to avoid collisions, etc.)\n})\n*/\nimport './build/maplibregl/shared';\n\n// Worker and its unique dependencies, i.e.:\n/*\ndefine(['./shared.js'], function (__shared__js) {\n // Code for worker script and its unique dependencies.\n // Expects the output of 'shared' module to be passed in as an argument,\n // since all references to common deps look like, e.g.,\n // __shared__js.shapeText().\n});\n*/\n// When this wrapper function is passed to our custom define() above,\n// it gets stringified, together with the shared wrapper (using\n// Function.toString()), and the resulting string of code is made into a\n// Blob URL that gets used by the main module to create the web workers.\nimport './build/maplibregl/worker';\n\n// Main module and its unique dependencies\n/*\ndefine(['./shared.js'], function (__shared__js) {\n // Code for main GL JS module and its unique dependencies.\n // Expects the output of 'shared' module to be passed in as an argument,\n // since all references to common deps look like, e.g.,\n // __shared__js.shapeText().\n //\n // Returns the actual maplibregl (i.e. src/index.js)\n});\n*/\nimport './build/maplibregl/index';\n\nexport default maplibregl;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AA6CA;AACA,mBAAe,UAAU;;;;;;;;"}
|
||||
1
public/resources/maplibre-gl-inspect-compat.min.js
vendored
Normal file
1
public/resources/maplibre-gl-inspect-compat.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
40
public/resources/maplibre-gl-inspect.css
Normal file
40
public/resources/maplibre-gl-inspect.css
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
.maplibregl-inspect_popup {
|
||||
color: #333;
|
||||
display: table;
|
||||
}
|
||||
|
||||
.maplibregl-inspect_feature:not(:last-child) {
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.maplibregl-inspect_layer:before {
|
||||
content: '#';
|
||||
}
|
||||
|
||||
.maplibregl-inspect_layer {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.maplibregl-inspect_property {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.maplibregl-inspect_property-value {
|
||||
display: table-cell;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.maplibregl-inspect_property-name {
|
||||
display: table-cell;
|
||||
padding-right: 10px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.maplibregl-ctrl-inspect {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23333333' preserveAspectRatio='xMidYMid meet' viewBox='-10 -10 60 60'%3E%3Cg%3E%3Cpath d='m15 21.6q0-2 1.5-3.5t3.5-1.5 3.5 1.5 1.5 3.5-1.5 3.6-3.5 1.4-3.5-1.4-1.5-3.6z m18.4 11.1l-6.4-6.5q1.4-2.1 1.4-4.6 0-3.4-2.5-5.8t-5.9-2.4-5.9 2.4-2.5 5.8 2.5 5.9 5.9 2.5q2.4 0 4.6-1.4l7.4 7.4q-0.9 0.6-2 0.6h-20q-1.3 0-2.3-0.9t-1.1-2.3l0.1-26.8q0-1.3 1-2.3t2.3-0.9h13.4l10 10v19.3z'%3E%3C/path%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.maplibregl-ctrl-map {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23333333' viewBox='-10 -10 60 60' preserveAspectRatio='xMidYMid meet'%3E%3Cg%3E%3Cpath d='m25 31.640000000000004v-19.766666666666673l-10-3.511666666666663v19.766666666666666z m9.140000000000008-26.640000000000004q0.8599999999999923 0 0.8599999999999923 0.8600000000000003v25.156666666666666q0 0.625-0.625 0.783333333333335l-9.375 3.1999999999999993-10-3.5133333333333354-8.906666666666668 3.4383333333333326-0.2333333333333334 0.07833333333333314q-0.8616666666666664 0-0.8616666666666664-0.8599999999999994v-25.156666666666663q0-0.625 0.6233333333333331-0.7833333333333332l9.378333333333334-3.198333333333334 10 3.5133333333333336 8.905000000000001-3.4383333333333344z'%3E%3C/path%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
1
public/resources/maplibre-gl-inspect.min.js
vendored
Normal file
1
public/resources/maplibre-gl-inspect.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/resources/maplibre-gl.css
Normal file
1
public/resources/maplibre-gl.css
Normal file
File diff suppressed because one or more lines are too long
44
public/resources/maplibre-gl.js
Normal file
44
public/resources/maplibre-gl.js
Normal file
File diff suppressed because one or more lines are too long
1
public/resources/maplibre-gl.js.map
Normal file
1
public/resources/maplibre-gl.js.map
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"maplibre-gl.js","sources":["../rollup/maplibregl.js"],"sourcesContent":["//\n// Our custom intro provides a specialized \"define()\" function, called by the\n// AMD modules below, that sets up the worker blob URL and then executes the\n// main module, storing its exported value as 'maplibregl'\n\n// The three \"chunks\" imported here are produced by a first Rollup pass,\n// which outputs them as AMD modules.\n\n// Shared dependencies, i.e.:\n/*\ndefine(['exports'], function (exports) {\n // Code for all common dependencies\n // Each module's exports are attached attached to 'exports' (with\n // names rewritten to avoid collisions, etc.)\n})\n*/\nimport './build/maplibregl/shared';\n\n// Worker and its unique dependencies, i.e.:\n/*\ndefine(['./shared.js'], function (__shared__js) {\n // Code for worker script and its unique dependencies.\n // Expects the output of 'shared' module to be passed in as an argument,\n // since all references to common deps look like, e.g.,\n // __shared__js.shapeText().\n});\n*/\n// When this wrapper function is passed to our custom define() above,\n// it gets stringified, together with the shared wrapper (using\n// Function.toString()), and the resulting string of code is made into a\n// Blob URL that gets used by the main module to create the web workers.\nimport './build/maplibregl/worker';\n\n// Main module and its unique dependencies\n/*\ndefine(['./shared.js'], function (__shared__js) {\n // Code for main GL JS module and its unique dependencies.\n // Expects the output of 'shared' module to be passed in as an argument,\n // since all references to common deps look like, e.g.,\n // __shared__js.shapeText().\n //\n // Returns the actual maplibregl (i.e. src/index.js)\n});\n*/\nimport './build/maplibregl/index';\n\nexport default maplibregl;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AA6CA;AACA,mBAAe,UAAU;;;;;;;;"}
|
||||
|
|
@ -5,10 +5,10 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{name}} - TileServer GL</title>
|
||||
{{#is_vector}}
|
||||
<link rel="stylesheet" type="text/css" href="{{public_url}}mapbox-gl.css{{&key_query}}" />
|
||||
<link rel="stylesheet" type="text/css" href="{{public_url}}mapbox-gl-inspect.css{{&key_query}}" />
|
||||
<script src="{{public_url}}mapbox-gl.js{{&key_query}}"></script>
|
||||
<script src="{{public_url}}mapbox-gl-inspect.min.js{{&key_query}}"></script>
|
||||
<link rel="stylesheet" type="text/css" href="{{public_url}}maplibre-gl.css{{&key_query}}" />
|
||||
<link rel="stylesheet" type="text/css" href="{{public_url}}maplibre-gl-inspect.css{{&key_query}}" />
|
||||
<script>if (typeof Symbol !== 'undefined') { document.write('<script src="{{public_url}}maplibre-gl.js{{&key_query}}"><\/script>'); } else { document.write('<script src="{{public_url}}maplibre-gl-compat.js{{&key_query}}"><\/script>'); }</script>
|
||||
<script>if (typeof Symbol !== 'undefined') { document.write('<script src="{{public_url}}maplibre-gl-inspect.min.js{{&key_query}}"><\/script>'); } else { document.write('<script src="{{public_url}}maplibre-gl-inspect-compat.min.js{{&key_query}}"><\/script>'); }</script>
|
||||
<style>
|
||||
body {background:#fff;color:#333;font-family:Arial, sans-serif;}
|
||||
#map {position:absolute;top:0;left:0;right:250px;bottom:0;}
|
||||
|
|
@ -18,9 +18,10 @@
|
|||
</style>
|
||||
{{/is_vector}}
|
||||
{{^is_vector}}
|
||||
<link rel="stylesheet" type="text/css" href="{{public_url}}mapbox.css{{&key_query}}" />
|
||||
<script src="{{public_url}}mapbox.js{{&key_query}}"></script>
|
||||
<link rel="stylesheet" type="text/css" href="{{public_url}}leaflet.css{{&key_query}}" />
|
||||
<script src="{{public_url}}leaflet.js{{&key_query}}"></script>
|
||||
<script src="{{public_url}}leaflet-hash.js{{&key_query}}"></script>
|
||||
<script src="{{public_url}}L.TileLayer.NoGap.js{{&key_query}}"></script>
|
||||
<style>
|
||||
body { margin:0; padding:0; }
|
||||
#map { position:absolute; top:0; bottom:0; width:100%; }
|
||||
|
|
@ -34,9 +35,10 @@
|
|||
<div id="layerList"></div>
|
||||
<pre id="propertyList"></pre>
|
||||
<script>
|
||||
var map = new mapboxgl.Map({
|
||||
var map = new maplibregl.Map({
|
||||
container: 'map',
|
||||
hash: true,
|
||||
maplibreLogo: true,
|
||||
style: {
|
||||
version: 8,
|
||||
sources: {
|
||||
|
|
@ -48,8 +50,8 @@
|
|||
layers: []
|
||||
}
|
||||
});
|
||||
map.addControl(new mapboxgl.NavigationControl());
|
||||
var inspect = new MapboxInspect({
|
||||
map.addControl(new maplibregl.NavigationControl());
|
||||
var inspect = new MaplibreInspect({
|
||||
showInspectMap: true,
|
||||
showInspectButton: false
|
||||
});
|
||||
|
|
@ -74,12 +76,49 @@
|
|||
<h1 style="display:none;">{{name}}</h1>
|
||||
<div id='map'></div>
|
||||
<script>
|
||||
var map = L.mapbox.map('map', '{{public_url}}data/{{id}}.json{{&key_query}}', { zoomControl: false });
|
||||
map.eachLayer(function(layer) {
|
||||
// do not add scale prefix even if retina display is detected
|
||||
layer.scalePrefix = '.';
|
||||
});
|
||||
new L.Control.Zoom({ position: 'topright' }).addTo(map);
|
||||
var map = L.map('map', { zoomControl: false });
|
||||
new L.Control.Zoom({ position: 'topright' }).addTo(map);
|
||||
|
||||
var tile_urls = [], tile_attribution, tile_minzoom, tile_maxzoom;
|
||||
var url = '{{public_url}}data/{{id}}.json{{&key_query}}';
|
||||
var req = new XMLHttpRequest();
|
||||
req.overrideMimeType("application/json");
|
||||
req.open('GET', url, true);
|
||||
req.onload = function() {
|
||||
var jsonResponse = JSON.parse(req.responseText);
|
||||
for (key in jsonResponse) {
|
||||
var keyl = key.toLowerCase();
|
||||
switch(keyl) {
|
||||
case "tiles":
|
||||
tile_urls = jsonResponse[key];
|
||||
break;
|
||||
case "attribution":
|
||||
tile_attribution = jsonResponse[key];
|
||||
break;
|
||||
case "minzoom":
|
||||
tile_minzoom = jsonResponse[key];
|
||||
break;
|
||||
case "maxzoom":
|
||||
tile_maxzoom = jsonResponse[key];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (tile_url in tile_urls) {
|
||||
L.tileLayer(tile_urls[tile_url], {
|
||||
minZoom: tile_minzoom,
|
||||
maxZoom: tile_maxzoom,
|
||||
attribution: tile_attribution
|
||||
}).addTo(map);
|
||||
}
|
||||
|
||||
map.eachLayer(function(layer) {
|
||||
// do not add scale prefix even if retina display is detected
|
||||
layer.scalePrefix = '.';
|
||||
});
|
||||
};
|
||||
req.send(null);
|
||||
|
||||
setTimeout(function() {
|
||||
new L.Hash(map);
|
||||
}, 0);
|
||||
|
|
|
|||
|
|
@ -4,11 +4,14 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{name}} - TileServer GL</title>
|
||||
<link rel="stylesheet" type="text/css" href="{{public_url}}mapbox-gl.css{{&key_query}}" />
|
||||
<script src="{{public_url}}mapbox-gl.js{{&key_query}}"></script>
|
||||
<link rel="stylesheet" type="text/css" href="{{public_url}}mapbox.css{{&key_query}}" />
|
||||
<script src="{{public_url}}mapbox.js{{&key_query}}"></script>
|
||||
<link rel="stylesheet" type="text/css" href="{{public_url}}maplibre-gl.css{{&key_query}}" />
|
||||
<link rel="stylesheet" type="text/css" href="{{public_url}}maplibre-gl-inspect.css{{&key_query}}" />
|
||||
<link rel="stylesheet" type="text/css" href="{{public_url}}leaflet.css{{&key_query}}" />
|
||||
<script>if (typeof Symbol !== 'undefined') { document.write('<script src="{{public_url}}maplibre-gl.js{{&key_query}}"><\/script>'); } else { document.write('<script src="{{public_url}}maplibre-gl-compat.js{{&key_query}}"><\/script>'); }</script>
|
||||
<script>if (typeof Symbol !== 'undefined') { document.write('<script src="{{public_url}}maplibre-gl-inspect.min.js{{&key_query}}"><\/script>'); } else { document.write('<script src="{{public_url}}maplibre-gl-inspect-compat.min.js{{&key_query}}"><\/script>'); }</script>
|
||||
<script src="{{public_url}}leaflet.js{{&key_query}}"></script>
|
||||
<script src="{{public_url}}leaflet-hash.js{{&key_query}}"></script>
|
||||
<script src="{{public_url}}L.TileLayer.NoGap.js{{&key_query}}"></script>
|
||||
<style>
|
||||
body { margin:0; padding:0; }
|
||||
#map { position:absolute; top:0; bottom:0; width:100%; }
|
||||
|
|
@ -22,21 +25,72 @@
|
|||
var preference =
|
||||
q.indexOf('vector') >= 0 ? 'vector' :
|
||||
(q.indexOf('raster') >= 0 ? 'raster' :
|
||||
(mapboxgl.supported() ? 'vector' : 'raster'));
|
||||
(maplibregl.supported() ? 'vector' : 'raster'));
|
||||
if (preference == 'vector') {
|
||||
mapboxgl.setRTLTextPlugin('{{public_url}}mapbox-gl-rtl-text.js{{&key_query}}');
|
||||
var map = new mapboxgl.Map({
|
||||
maplibregl.setRTLTextPlugin('{{public_url}}mapbox-gl-rtl-text.js{{&key_query}}');
|
||||
var map = new maplibregl.Map({
|
||||
container: 'map',
|
||||
style: '{{public_url}}styles/{{id}}/style.json{{&key_query}}',
|
||||
hash: true
|
||||
hash: true,
|
||||
maplibreLogo: true
|
||||
});
|
||||
map.addControl(new mapboxgl.NavigationControl());
|
||||
map.addControl(new maplibregl.NavigationControl({
|
||||
visualizePitch: true,
|
||||
showZoom: true,
|
||||
showCompass: true
|
||||
}));
|
||||
map.addControl(new MaplibreInspect({
|
||||
showMapPopupOnHover: false,
|
||||
showInspectMapPopupOnHover: false,
|
||||
selectThreshold: 5
|
||||
}));
|
||||
} else {
|
||||
var map = L.mapbox.map('map', '{{public_url}}styles/{{id}}.json{{&key_query}}', { zoomControl: false });
|
||||
new L.Control.Zoom({ position: 'topright' }).addTo(map);
|
||||
setTimeout(function() {
|
||||
new L.Hash(map);
|
||||
}, 0);
|
||||
var map = L.map('map', { zoomControl: false });
|
||||
new L.Control.Zoom({ position: 'topright' }).addTo(map);
|
||||
|
||||
var tile_urls = [], tile_attribution, tile_minzoom, tile_maxzoom;
|
||||
var url = '{{public_url}}styles/{{id}}.json{{&key_query}}';
|
||||
var req = new XMLHttpRequest();
|
||||
req.overrideMimeType("application/json");
|
||||
req.open('GET', url, true);
|
||||
req.onload = function() {
|
||||
var jsonResponse = JSON.parse(req.responseText);
|
||||
for (key in jsonResponse) {
|
||||
var keyl = key.toLowerCase();
|
||||
switch(keyl) {
|
||||
case "tiles":
|
||||
tile_urls = jsonResponse[key];
|
||||
break;
|
||||
case "attribution":
|
||||
tile_attribution = jsonResponse[key];
|
||||
break;
|
||||
case "minzoom":
|
||||
tile_minzoom = jsonResponse[key];
|
||||
break;
|
||||
case "maxzoom":
|
||||
tile_maxzoom = jsonResponse[key];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (tile_url in tile_urls) {
|
||||
L.tileLayer(tile_urls[tile_url], {
|
||||
minZoom: tile_minzoom,
|
||||
maxZoom: tile_maxzoom,
|
||||
attribution: tile_attribution
|
||||
}).addTo(map);
|
||||
}
|
||||
|
||||
map.eachLayer(function(layer) {
|
||||
// do not add scale prefix even if retina display is detected
|
||||
layer.scalePrefix = '.';
|
||||
});
|
||||
};
|
||||
req.send(null);
|
||||
|
||||
setTimeout(function() {
|
||||
new L.Hash(map);
|
||||
}, 0);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ var packageJson = require('./package');
|
|||
packageJson.name += '-light';
|
||||
packageJson.description = 'Map tile server for JSON GL styles - serving vector tiles';
|
||||
delete packageJson.dependencies['canvas'];
|
||||
delete packageJson.dependencies['@mapbox/mapbox-gl-native'];
|
||||
delete packageJson.dependencies['@maplibre/maplibre-gl-native'];
|
||||
delete packageJson.dependencies['sharp'];
|
||||
|
||||
delete packageJson.optionalDependencies;
|
||||
|
|
@ -34,6 +34,7 @@ var str = JSON.stringify(packageJson, undefined, 2);
|
|||
fs.writeFileSync('light/package.json', str);
|
||||
fs.renameSync('light/README_light.md', 'light/README.md');
|
||||
fs.renameSync('light/Dockerfile_light', 'light/Dockerfile');
|
||||
fs.renameSync('light/docker-entrypoint_light.sh', 'light/docker-entrypoint.sh');
|
||||
|
||||
// for Build tileserver-gl-light docker image, don't publish
|
||||
if (process.argv.length > 2 && process.argv[2] == "--no-publish") {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ if (args.length >= 3 && args[2][0] !== '-') {
|
|||
args.splice(2, 0, '--mbtiles');
|
||||
}
|
||||
|
||||
const opts = require('commander')
|
||||
const { program } = require('commander');
|
||||
program
|
||||
.description('tileserver-gl startup options')
|
||||
.usage('tileserver-gl [mbtiles] [options]')
|
||||
.option(
|
||||
|
|
@ -68,7 +69,8 @@ const opts = require('commander')
|
|||
packageJson.version,
|
||||
'-v, --version'
|
||||
)
|
||||
.parse(args);
|
||||
program.parse(process.argv);
|
||||
const opts = program.opts();
|
||||
|
||||
console.log(`Starting ${packageJson.name} v${packageJson.version}`);
|
||||
|
||||
|
|
@ -103,7 +105,7 @@ const startWithMBTiles = (mbtilesFile) => {
|
|||
console.log(`ERROR: Not valid MBTiles file: ${mbtilesFile}`);
|
||||
process.exit(1);
|
||||
}
|
||||
const instance = new MBTiles(mbtilesFile, (err) => {
|
||||
const instance = new MBTiles(mbtilesFile + '?mode=ro', (err) => {
|
||||
if (err) {
|
||||
console.log('ERROR: Unable to open MBTiles.');
|
||||
console.log(` Make sure ${path.basename(mbtilesFile)} is valid MBTiles.`);
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ module.exports = {
|
|||
}
|
||||
let source;
|
||||
const sourceInfoPromise = new Promise((resolve, reject) => {
|
||||
source = new MBTiles(mbtilesFile, err => {
|
||||
source = new MBTiles(mbtilesFile + '?mode=ro', err => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ const clone = require('clone');
|
|||
const Color = require('color');
|
||||
const express = require('express');
|
||||
const mercator = new (require('@mapbox/sphericalmercator'))();
|
||||
const mbgl = require('@mapbox/mapbox-gl-native');
|
||||
const mbgl = require('@maplibre/maplibre-gl-native');
|
||||
const MBTiles = require('@mapbox/mbtiles');
|
||||
const proj4 = require('proj4');
|
||||
const request = require('request');
|
||||
|
|
@ -229,9 +229,7 @@ module.exports = {
|
|||
|
||||
const app = express().disable('x-powered-by');
|
||||
|
||||
const respondImage = (item, z, lon, lat, bearing, pitch,
|
||||
width, height, scale, format, res, next,
|
||||
opt_overlay) => {
|
||||
const respondImage = (item, z, lon, lat, bearing, pitch, width, height, scale, format, res, next, opt_overlay, opt_mode='tile') => {
|
||||
if (Math.abs(lon) > 180 || Math.abs(lat) > 85.06 ||
|
||||
lon !== lon || lat !== lat) {
|
||||
return res.status(400).send('Invalid center');
|
||||
|
|
@ -248,7 +246,12 @@ module.exports = {
|
|||
return res.status(400).send('Invalid format');
|
||||
}
|
||||
|
||||
const pool = item.map.renderers[scale];
|
||||
let pool;
|
||||
if (opt_mode === 'tile') {
|
||||
pool = item.map.renderers[scale];
|
||||
} else {
|
||||
pool = item.map.renderers_static[scale];
|
||||
}
|
||||
pool.acquire((err, renderer) => {
|
||||
const mbglZ = Math.max(0, z - 1);
|
||||
const params = {
|
||||
|
|
@ -302,9 +305,11 @@ module.exports = {
|
|||
});
|
||||
|
||||
if (z > 2 && tileMargin > 0) {
|
||||
const [_, y] = mercator.px(params.center, z);
|
||||
let yoffset = Math.max(Math.min(0, y - 128 - tileMargin), y + 128 + tileMargin - Math.pow(2, z + 8));
|
||||
image.extract({
|
||||
left: tileMargin * scale,
|
||||
top: tileMargin * scale,
|
||||
top: (tileMargin + yoffset) * scale,
|
||||
width: width * scale,
|
||||
height: height * scale
|
||||
});
|
||||
|
|
@ -383,8 +388,7 @@ module.exports = {
|
|||
((x + 0.5) / (1 << z)) * (256 << z),
|
||||
((y + 0.5) / (1 << z)) * (256 << z)
|
||||
], z);
|
||||
return respondImage(item, z, tileCenter[0], tileCenter[1], 0, 0,
|
||||
tileSize, tileSize, scale, format, res, next);
|
||||
return respondImage(item, z, tileCenter[0], tileCenter[1], 0, 0, tileSize, tileSize, scale, format, res, next);
|
||||
});
|
||||
|
||||
if (options.serveStaticMaps !== false) {
|
||||
|
|
@ -426,11 +430,9 @@ module.exports = {
|
|||
}
|
||||
|
||||
const path = extractPathFromQuery(req.query, transformer);
|
||||
const overlay = renderOverlay(z, x, y, bearing, pitch, w, h, scale,
|
||||
path, req.query);
|
||||
const overlay = renderOverlay(z, x, y, bearing, pitch, w, h, scale, path, req.query);
|
||||
|
||||
return respondImage(item, z, x, y, bearing, pitch, w, h, scale, format,
|
||||
res, next, overlay);
|
||||
return respondImage(item, z, x, y, bearing, pitch, w, h, scale, format, res, next, overlay, 'static');
|
||||
});
|
||||
|
||||
const serveBounds = (req, res, next) => {
|
||||
|
|
@ -468,10 +470,8 @@ module.exports = {
|
|||
pitch = 0;
|
||||
|
||||
const path = extractPathFromQuery(req.query, transformer);
|
||||
const overlay = renderOverlay(z, x, y, bearing, pitch, w, h, scale,
|
||||
path, req.query);
|
||||
return respondImage(item, z, x, y, bearing, pitch, w, h, scale, format,
|
||||
res, next, overlay);
|
||||
const overlay = renderOverlay(z, x, y, bearing, pitch, w, h, scale, path, req.query);
|
||||
return respondImage(item, z, x, y, bearing, pitch, w, h, scale, format, res, next, overlay, 'static');
|
||||
};
|
||||
|
||||
const boundsPattern =
|
||||
|
|
@ -542,11 +542,9 @@ module.exports = {
|
|||
x = center[0],
|
||||
y = center[1];
|
||||
|
||||
const overlay = renderOverlay(z, x, y, bearing, pitch, w, h, scale,
|
||||
path, req.query);
|
||||
const overlay = renderOverlay(z, x, y, bearing, pitch, w, h, scale, path, req.query);
|
||||
|
||||
return respondImage(item, z, x, y, bearing, pitch, w, h, scale, format,
|
||||
res, next, overlay);
|
||||
return respondImage(item, z, x, y, bearing, pitch, w, h, scale, format, res, next, overlay, 'static');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -566,14 +564,15 @@ module.exports = {
|
|||
add: (options, repo, params, id, publicUrl, dataResolver) => {
|
||||
const map = {
|
||||
renderers: [],
|
||||
renderers_static: [],
|
||||
sources: {}
|
||||
};
|
||||
|
||||
let styleJSON;
|
||||
const createPool = (ratio, min, max) => {
|
||||
const createPool = (ratio, mode, min, max) => {
|
||||
const createRenderer = (ratio, createCallback) => {
|
||||
const renderer = new mbgl.Map({
|
||||
mode: "tile",
|
||||
mode: mode,
|
||||
ratio: ratio,
|
||||
request: (req, callback) => {
|
||||
const protocol = req.url.split(':')[0];
|
||||
|
|
@ -764,7 +763,7 @@ module.exports = {
|
|||
if (!mbtilesFileStats.isFile() || mbtilesFileStats.size === 0) {
|
||||
throw Error(`Not valid MBTiles file: ${mbtilesFile}`);
|
||||
}
|
||||
map.sources[name] = new MBTiles(mbtilesFile, err => {
|
||||
map.sources[name] = new MBTiles(mbtilesFile + '?mode=ro', err => {
|
||||
map.sources[name].getInfo((err, info) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
|
|
@ -793,10 +792,12 @@ module.exports = {
|
|||
|
||||
if (!attributionOverride &&
|
||||
source.attribution && source.attribution.length > 0) {
|
||||
if (tileJSON.attribution.length > 0) {
|
||||
tileJSON.attribution += '; ';
|
||||
if (!tileJSON.attribution.includes(source.attribution)) {
|
||||
if (tileJSON.attribution.length > 0) {
|
||||
tileJSON.attribution += ' | ';
|
||||
}
|
||||
tileJSON.attribution += source.attribution;
|
||||
}
|
||||
tileJSON.attribution += source.attribution;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
|
|
@ -814,7 +815,8 @@ module.exports = {
|
|||
const j = Math.min(maxPoolSizes.length - 1, s - 1);
|
||||
const minPoolSize = minPoolSizes[i];
|
||||
const maxPoolSize = Math.max(minPoolSize, maxPoolSizes[j]);
|
||||
map.renderers[s] = createPool(s, minPoolSize, maxPoolSize);
|
||||
map.renderers[s] = createPool(s, 'tile', minPoolSize, maxPoolSize);
|
||||
map.renderers_static[s] = createPool(s, 'static', minPoolSize, maxPoolSize);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -826,6 +828,9 @@ module.exports = {
|
|||
item.map.renderers.forEach(pool => {
|
||||
pool.close();
|
||||
});
|
||||
item.map.renderers_static.forEach(pool => {
|
||||
pool.close();
|
||||
});
|
||||
}
|
||||
delete repo[id];
|
||||
},
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ const fs = require('fs');
|
|||
|
||||
const clone = require('clone');
|
||||
const express = require('express');
|
||||
import {validate} from '@mapbox/mapbox-gl-style-spec';
|
||||
import {validate} from '@maplibre/maplibre-gl-style-spec';
|
||||
|
||||
const utils = require('./utils');
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue