Compare commits
38 Commits
v3.0.0
...
d2d2d38daf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2d2d38daf | ||
|
|
5d5be67efc | ||
|
|
aba60f0c6a | ||
|
|
6027d89623 | ||
|
|
8680a8006a | ||
|
|
09ded526ef | ||
|
|
c89a5ae029 | ||
|
|
a3d7f8bcbd | ||
|
|
7c1420982c | ||
|
|
51baa9b67b | ||
|
|
6b3f557b1f | ||
|
|
8d2ddd8f95 | ||
|
|
9f59f67087 | ||
|
|
1d21648969 | ||
|
|
16de5be673 | ||
|
|
6b96f224ae | ||
|
|
6ff4cae9b9 | ||
|
|
644db6cd8a | ||
|
|
a98404e921 | ||
|
|
559cfc462c | ||
|
|
559c3a913e | ||
|
|
4036d528ec | ||
|
|
6ae4116ccb | ||
|
|
39bb7ffbf1 | ||
|
|
e249a3f67d | ||
|
|
04b49d8a98 | ||
|
|
309d481117 | ||
|
|
7ce4805cdd | ||
|
|
5377414710 | ||
|
|
042b8b986a | ||
|
|
038bfe29d6 | ||
|
|
f8563e1f2b | ||
|
|
10431d70d0 | ||
|
|
a5a8ae1e95 | ||
|
|
3c411cd1ac | ||
|
|
79bd70942b | ||
|
|
5585f49396 | ||
|
|
8126b31081 |
@@ -1,7 +1,6 @@
|
||||
.git
|
||||
docs/_build
|
||||
node_modules
|
||||
test_data
|
||||
light
|
||||
config.json
|
||||
*.mbtiles
|
||||
*
|
||||
!src
|
||||
!public
|
||||
!package.json
|
||||
!package-lock.json
|
||||
!docker-entrypoint.sh
|
||||
|
||||
67
Dockerfile
67
Dockerfile
@@ -1,30 +1,55 @@
|
||||
FROM node:10-stretch
|
||||
FROM node:10-buster AS builder
|
||||
|
||||
RUN export DEBIAN_FRONTEND=noninteractive \
|
||||
&& apt-get -qq update \
|
||||
&& apt-get -y --no-install-recommends install \
|
||||
apt-transport-https \
|
||||
curl \
|
||||
unzip \
|
||||
build-essential \
|
||||
python \
|
||||
libcairo2-dev \
|
||||
libgles2-mesa-dev \
|
||||
libgbm-dev \
|
||||
libllvm7 \
|
||||
libprotobuf-dev \
|
||||
&& apt-get -y --purge autoremove \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY . /usr/src/app
|
||||
|
||||
ENV NODE_ENV="production"
|
||||
|
||||
RUN cd /usr/src/app && npm install --production
|
||||
|
||||
|
||||
FROM node:10-buster-slim AS final
|
||||
|
||||
RUN export DEBIAN_FRONTEND=noninteractive \
|
||||
&& apt-get -qq update \
|
||||
&& apt-get -y --no-install-recommends install \
|
||||
libgles2-mesa \
|
||||
libegl1 \
|
||||
xvfb \
|
||||
xauth \
|
||||
&& apt-get -y --purge autoremove \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY --from=builder /usr/src/app /app
|
||||
|
||||
ENV NODE_ENV="production"
|
||||
ENV CHOKIDAR_USEPOLLING=1
|
||||
ENV CHOKIDAR_INTERVAL=500
|
||||
|
||||
VOLUME /data
|
||||
WORKDIR /data
|
||||
|
||||
EXPOSE 80
|
||||
ENTRYPOINT ["/bin/bash", "/usr/src/app/run.sh"]
|
||||
|
||||
RUN apt-get -qq update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get -y install \
|
||||
apt-transport-https \
|
||||
curl \
|
||||
unzip \
|
||||
build-essential \
|
||||
python \
|
||||
libcairo2-dev \
|
||||
libgles2-mesa-dev \
|
||||
libgbm-dev \
|
||||
libllvm3.9 \
|
||||
libprotobuf-dev \
|
||||
libxxf86vm-dev \
|
||||
xvfb \
|
||||
x11-utils \
|
||||
&& apt-get clean
|
||||
USER node:node
|
||||
|
||||
RUN mkdir -p /usr/src/app
|
||||
COPY / /usr/src/app
|
||||
RUN cd /usr/src/app && npm install --production
|
||||
ENTRYPOINT ["/app/docker-entrypoint.sh"]
|
||||
|
||||
CMD ["-p", "80"]
|
||||
|
||||
31
docker-entrypoint.sh
Executable file
31
docker-entrypoint.sh
Executable file
@@ -0,0 +1,31 @@
|
||||
#!/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
|
||||
xvfb-run -a --server-args="-screen 0 1024x768x24" -- node /app/ "$@" &
|
||||
# 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=$?
|
||||
wait
|
||||
exit ${RETVAL}
|
||||
fi
|
||||
|
||||
exec "$@"
|
||||
@@ -4,7 +4,9 @@ Configuration file
|
||||
|
||||
The configuration file defines the behavior of the application. It's a regular JSON file.
|
||||
|
||||
Example::
|
||||
Example:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"options": {
|
||||
@@ -101,7 +103,7 @@ Default is ``2048``.
|
||||
--------------
|
||||
|
||||
Additional image side length added during tile rendering that is cropped from the delivered tile. This is useful for resolving the issue with cropped labels,
|
||||
but it does come with a performance degradation, because additional, adjacent vector tiles need to be loaded to genenrate a single tile.
|
||||
but it does come with a performance degradation, because additional, adjacent vector tiles need to be loaded to generate a single tile.
|
||||
Default is ``0`` to disable this processing.
|
||||
|
||||
``minRendererPoolSizes``
|
||||
@@ -146,7 +148,7 @@ Each item in this object defines one style (map). It can have the following opti
|
||||
|
||||
* ``style`` -- name of the style json file [required]
|
||||
* ``serve_rendered`` -- whether to render the raster tiles for this style or not
|
||||
* ``serve_data`` -- whether to allow acces to the original tiles, sprites and required glyphs
|
||||
* ``serve_data`` -- whether to allow access to the original tiles, sprites and required glyphs
|
||||
* ``tilejson`` -- properties to add to the TileJSON created for the raster data
|
||||
|
||||
* ``format`` and ``bounds`` can be especially useful
|
||||
|
||||
@@ -29,8 +29,10 @@ Default preview style and configuration
|
||||
- If no configuration file is specified, a default preview style (compatible with openmaptiles) is used.
|
||||
- If no mbtiles file is specified (and is not found in the current working directory), a sample file is downloaded (showing the Zurich area)
|
||||
|
||||
Reloading configuration
|
||||
Reloading the configuration
|
||||
======
|
||||
|
||||
It is possible to reload the configuration file without restarting the whole process by sending a SIGHUP signal to the node process.
|
||||
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).
|
||||
|
||||
- The `docker kill -s HUP tileserver-gl` command can be used when running the tileserver-gl docker container.
|
||||
- The `docker-compose -s HUP tileserver-gl-service-name` can be used when tileserver-gl is run as a docker-compose service.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "tileserver-gl",
|
||||
"version": "3.0.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Map tile server for JSON GL styles - vector and server side generated raster tiles",
|
||||
"main": "src/main.js",
|
||||
"bin": "src/main.js",
|
||||
@@ -38,7 +38,7 @@
|
||||
"pbf": "3.2.1",
|
||||
"proj4": "2.6.0",
|
||||
"request": "2.88.2",
|
||||
"sharp": "0.25.1",
|
||||
"sharp": "0.26.2",
|
||||
"tileserver-gl-styles": "2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -395,7 +395,7 @@
|
||||
</TileMatrix>
|
||||
<TileMatrix>
|
||||
<ows:Identifier>18</ows:Identifier>
|
||||
<ScaleDenominator></ScaleDenominator>
|
||||
<ScaleDenominator>1066.3647919249</ScaleDenominator>
|
||||
<TopLeftCorner>90 -180</TopLeftCorner>
|
||||
<TileWidth>256</TileWidth>
|
||||
<TileHeight>256</TileHeight>
|
||||
@@ -404,4 +404,4 @@
|
||||
</TileMatrix></TileMatrixSet>
|
||||
</Contents>
|
||||
<ServiceMetadataURL xlink:href="{{baseUrl}}/wmts/{{id}}/"/>
|
||||
</Capabilities>
|
||||
</Capabilities>
|
||||
|
||||
4
run.sh
4
run.sh
@@ -8,7 +8,7 @@ _term() {
|
||||
trap _term SIGTERM
|
||||
trap _term SIGINT
|
||||
|
||||
xvfbMaxStartWaitTime=5
|
||||
xvfbMaxStartWaitTime=60
|
||||
displayNumber=99
|
||||
screenNumber=0
|
||||
|
||||
@@ -18,7 +18,7 @@ rm -rf /tmp/.X11-unix /tmp/.X${displayNumber}-lock ~/xvfb.pid
|
||||
echo "Starting Xvfb on display ${displayNumber}"
|
||||
start-stop-daemon --start --pidfile ~/xvfb.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :${displayNumber} -screen ${screenNumber} 1024x768x24 -ac +extension GLX +render -noreset
|
||||
|
||||
# Wait to be able to connect to the port. This will exit if it cannot in 15 minutes.
|
||||
# Wait to be able to connect to the port. This will exit if it cannot in 1 minute.
|
||||
timeout ${xvfbMaxStartWaitTime} bash -c "while ! xdpyinfo -display :${displayNumber} >/dev/null; do sleep 0.5; done"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Could not connect to display ${displayNumber} in ${xvfbMaxStartWaitTime} seconds time."
|
||||
|
||||
@@ -274,7 +274,7 @@ module.exports = {
|
||||
pool.release(renderer);
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
return res.status(500).send(err);
|
||||
}
|
||||
|
||||
// Fix semi-transparent outlines on raw, premultiplied input
|
||||
@@ -375,7 +375,7 @@ module.exports = {
|
||||
scale = getScale(req.params.scale),
|
||||
format = req.params.format;
|
||||
if (z < 0 || x < 0 || y < 0 ||
|
||||
z > 20 || x >= Math.pow(2, z) || y >= Math.pow(2, z)) {
|
||||
z > 22 || x >= Math.pow(2, z) || y >= Math.pow(2, z)) {
|
||||
return res.status(404).send('Out of bounds');
|
||||
}
|
||||
const tileSize = 256;
|
||||
|
||||
@@ -17,7 +17,7 @@ const fixUrl = (req, url, publicUrl, opt_nokey) => {
|
||||
}
|
||||
const queryParams = [];
|
||||
if (!opt_nokey && req.query.key) {
|
||||
queryParams.unshift(`key=${req.query.key}`);
|
||||
queryParams.unshift(`key=${encodeURIComponent(req.query.key)}`);
|
||||
}
|
||||
let query = '';
|
||||
if (queryParams.length) {
|
||||
@@ -43,7 +43,7 @@ module.exports = {
|
||||
}
|
||||
// mapbox-gl-js viewer cannot handle sprite urls with query
|
||||
if (styleJSON_.sprite) {
|
||||
styleJSON_.sprite = fixUrl(req, styleJSON_.sprite, item.publicUrl, true);
|
||||
styleJSON_.sprite = fixUrl(req, styleJSON_.sprite, item.publicUrl, false);
|
||||
}
|
||||
if (styleJSON_.glyphs) {
|
||||
styleJSON_.glyphs = fixUrl(req, styleJSON_.glyphs, item.publicUrl, false);
|
||||
|
||||
@@ -121,13 +121,13 @@ function start(opts) {
|
||||
success = serve_style.add(options, serving.styles, item, id, opts.publicUrl,
|
||||
(mbtiles, fromData) => {
|
||||
let dataItemId;
|
||||
for (const id of Object.keys(data)) {
|
||||
for (const id of Object.keys(serving.data)) {
|
||||
if (fromData) {
|
||||
if (id === mbtiles) {
|
||||
dataItemId = id;
|
||||
}
|
||||
} else {
|
||||
if (data[id].mbtiles === mbtiles) {
|
||||
if (serving.data[id].mbtiles === mbtiles) {
|
||||
dataItemId = id;
|
||||
}
|
||||
}
|
||||
@@ -136,11 +136,11 @@ function start(opts) {
|
||||
return dataItemId;
|
||||
} else {
|
||||
if (fromData || !allowMoreData) {
|
||||
console.log(`ERROR: style "${file.name}" using unknown mbtiles "${mbtiles}"! Skipping...`);
|
||||
console.log(`ERROR: style "${item.style}" using unknown mbtiles "${mbtiles}"! Skipping...`);
|
||||
return undefined;
|
||||
} else {
|
||||
let id = mbtiles.substr(0, mbtiles.lastIndexOf('.')) || mbtiles;
|
||||
while (data[id]) id += '_';
|
||||
//while (data[id]) id += '_';
|
||||
data[id] = {
|
||||
'mbtiles': mbtiles
|
||||
};
|
||||
@@ -172,6 +172,11 @@ function start(opts) {
|
||||
}
|
||||
};
|
||||
|
||||
let addData = (id, item) => {
|
||||
console.log(`Add data ${id}`);
|
||||
Promise.all([serve_data.add(options, serving.data, item, id, opts.publicUrl)]);
|
||||
};
|
||||
|
||||
for (const id of Object.keys(config.styles || {})) {
|
||||
const item = config.styles[id];
|
||||
if (!item.style || item.style.length === 0) {
|
||||
@@ -201,6 +206,21 @@ function start(opts) {
|
||||
}
|
||||
|
||||
if (options.serveAllStyles) {
|
||||
fs.readdir(options.paths.mbtiles, {withFileTypes: true}, (err, files) => {
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
for (const file of files) {
|
||||
if (file.isFile() && path.extname(file.name).toLowerCase() == '.mbtiles') {
|
||||
let id = path.basename(file.name, '.mbtiles');
|
||||
let item = {
|
||||
mbtiles: file.name
|
||||
};
|
||||
addData(id, item);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
fs.readdir(options.paths.styles, {withFileTypes: true}, (err, files) => {
|
||||
if (err) {
|
||||
return;
|
||||
@@ -212,7 +232,24 @@ function start(opts) {
|
||||
let item = {
|
||||
style: file.name
|
||||
};
|
||||
addStyle(id, item, false, false);
|
||||
addStyle(id, item, true, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const watcherData = chokidar.watch(path.join(options.paths.mbtiles, '*.mbtiles'),
|
||||
{
|
||||
});
|
||||
watcherData.on('all', (eventType, filename) => {
|
||||
if (filename) {
|
||||
let id = path.basename(filename, '.mbtiles');
|
||||
console.log(`Data "${id}" added`);
|
||||
|
||||
if (eventType == "add") {
|
||||
let item = {
|
||||
mbtiles: filename
|
||||
};
|
||||
addData(id, item);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -235,7 +272,7 @@ function start(opts) {
|
||||
let item = {
|
||||
style: filename
|
||||
};
|
||||
addStyle(id, item, false, false);
|
||||
addStyle(id, item, true, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -243,7 +280,7 @@ function start(opts) {
|
||||
|
||||
app.get('/styles.json', (req, res, next) => {
|
||||
const result = [];
|
||||
const query = req.query.key ? (`?key=${req.query.key}`) : '';
|
||||
const query = req.query.key ? (`?key=${encodeURIComponent(req.query.key)}`) : '';
|
||||
for (const id of Object.keys(serving.styles)) {
|
||||
const styleJSON = serving.styles[id].styleJSON;
|
||||
result.push({
|
||||
@@ -319,8 +356,8 @@ function start(opts) {
|
||||
data['public_url'] = opts.publicUrl || '/';
|
||||
data['is_light'] = isLight;
|
||||
data['key_query_part'] =
|
||||
req.query.key ? `key=${req.query.key}&` : '';
|
||||
data['key_query'] = req.query.key ? `?key=${req.query.key}` : '';
|
||||
req.query.key ? `key=${encodeURIComponent(req.query.key)}&` : '';
|
||||
data['key_query'] = req.query.key ? `?key=${encodeURIComponent(req.query.key)}` : '';
|
||||
if (template === 'wmts') res.set('Content-Type', 'text/xml');
|
||||
return res.status(200).send(compiled(data));
|
||||
});
|
||||
@@ -362,7 +399,7 @@ function start(opts) {
|
||||
if (!data_.is_vector) {
|
||||
if (center) {
|
||||
const centerPx = mercator.px([center[0], center[1]], center[2]);
|
||||
data_.thumbnail = `${center[2]}/${Math.floor(centerPx[0] / 256)}/${Math.floor(centerPx[1] / 256)}.${data_.format}`;
|
||||
data_.thumbnail = `${center[2]}/${Math.floor(centerPx[0] / 256)}/${Math.floor(centerPx[1] / 256)}.${data_.tileJSON.format}`;
|
||||
}
|
||||
|
||||
data_.xyz_link = utils.getTileUrls(
|
||||
|
||||
@@ -40,10 +40,10 @@ module.exports.getTileUrls = (req, domains, path, format, publicUrl, aliases) =>
|
||||
const key = req.query.key;
|
||||
const queryParams = [];
|
||||
if (req.query.key) {
|
||||
queryParams.push(`key=${req.query.key}`);
|
||||
queryParams.push(`key=${encodeURIComponent(req.query.key)}`);
|
||||
}
|
||||
if (req.query.style) {
|
||||
queryParams.push(`style=${req.query.style}`);
|
||||
queryParams.push(`style=${encodeURIComponent(req.query.style)}`);
|
||||
}
|
||||
const query = queryParams.length > 0 ? (`?${queryParams.join('&')}`) : '';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user