Compare commits

...

38 Commits

Author SHA1 Message Date
Ivan Vazhenin
d2d2d38daf fix 2022-04-13 20:15:46 +03:00
Ivan Vazhenin
5d5be67efc fix 2022-04-12 19:50:29 +03:00
Petr Sloup
aba60f0c6a Merge pull request #560 from zstadler/docker-reload
Simplify reloading the configuration with docker
2021-12-29 09:42:16 +01:00
zstadler
6027d89623 Simplify reloading the configuration with docker
Allow `docker kill -s HUP` and `docker-compose kill -s HUP` to reach the
node process.
2021-12-22 18:38:44 +02:00
Petr Sloup
8680a8006a Merge pull request #516 from zstadler/patch-1
Document configuration reloading when using docker
2021-07-28 13:14:31 +02:00
zstadler
09ded526ef Document configuration reloading when using docker
Based on https://github.com/maptiler/tileserver-gl/issues/420#issuecomment-766332814
2021-02-02 12:35:19 +02:00
Petr Sloup
c89a5ae029 v3.1.1 2020-12-14 15:27:33 +01:00
Petr Sloup
a3d7f8bcbd Fix the docker image port to fix backward compatibility (#502) 2020-12-14 15:27:05 +01:00
Petr Sloup
7c1420982c v3.1.0 2020-12-11 16:19:48 +01:00
Petr Sloup
51baa9b67b Merge pull request #457 from joakimfors/master
Streamline Dockerfile for caching and size
2020-12-11 16:13:55 +01:00
Joakim Fors
6b3f557b1f Gracefully handle signals from Docker or terminal
Start Xvfb wrapper in background and wait for the process to complete.
Because wait exits immediately when a signal for which a trap has been
set, and the signal handler is executed directly after that, we need to
wait again for the background processes to actually finish before exiting.

The signal handler catches INT and TERM and forwards them to the node
process.

The return value from the first wait is stored and sent as exit value.
2020-12-04 20:57:06 +01:00
Joakim Fors
8d2ddd8f95 Use automatic server number 2020-12-04 16:19:30 +01:00
Joakim Fors
9f59f67087 Clean up dependencies 2020-12-04 16:19:30 +01:00
Joakim Fors
1d21648969 Run as node user instead of root 2020-12-04 16:19:30 +01:00
Joakim Fors
16de5be673 Use simplified entrypoint script 2020-12-04 16:19:30 +01:00
Joakim Fors
6b96f224ae Streamline Dockerfile for caching and size
Move package installation to top of file to enable layer caching
in Docker. Use multi stage builds to reduce final image size.
Ignore everything but essential files when creating Docker build
context.
2020-12-04 16:19:30 +01:00
Petr Sloup
6ff4cae9b9 Merge pull request #484 from candux/addKeyToSprites
add api key to sprite ressources in style json
2020-11-04 20:38:13 +01:00
Petr Sloup
644db6cd8a Merge pull request #487 from bheupers/patch-1
Render up to scale level 22
2020-11-04 18:58:32 +01:00
Petr Sloup
a98404e921 Merge pull request #488 from vcabbage/sharp-update
update sharp to avoid segfault
2020-11-04 18:56:33 +01:00
Petr Sloup
559cfc462c Merge pull request #486 from 0xflotus/patch-1
fixed small errors
2020-11-04 18:56:02 +01:00
Kale Blankenship
559c3a913e update sharp to avoid segfault
Before
e82a585cec
the server may crash with a segfault when under high load.
2020-11-04 09:36:10 -08:00
bheupers
4036d528ec Render up to scale level 22
For detailed city rendering we need to render rasterfiles up to and including level 21. Therefore I propose to change the maximum raster level to 22
2020-11-04 14:42:46 +01:00
0xflotus
6ae4116ccb fixed small errors 2020-10-23 15:05:36 +02:00
0xflotus
39bb7ffbf1 enabled syntax highlighting 2020-10-23 15:04:51 +02:00
David Weber | geOps
e249a3f67d add api key to sprite ressources in style json
this make some setups easier becouse sprites can be handeld
like any other ressources
2020-10-22 19:09:42 +02:00
Petr Sloup
04b49d8a98 Merge pull request #482 from candux/david/fixStyleServer
fix crash in serveAllFonts
2020-10-16 09:24:44 +02:00
David Weber | geOps
309d481117 fix crash in serveAllFonts
variable file does not exist. Backtrace

ReferenceError: file is not defined
  at serve_style.add (/usr/src/app/src/server.js:139:44)
  at Proxy.add (/usr/src/app/src/serve_style.js:115:28)
  at addStyle (/usr/src/app/src/server.js:121:31)
  at fs.readdir (/usr/src/app/src/server.js:215:11)
  at args (fs.js:140:20)
  at internal/util.js:370:14
  at getDirents (internal/fs/utils.js:149:7)
  at FSReqWrap.req.oncomplete (fs.js:775:7)
2020-10-16 09:15:10 +02:00
Petr Sloup
7ce4805cdd Merge pull request #479 from poljvd/patch-1
Fix missing ScaleDenominator in wmts.tmpl
2020-09-28 10:36:32 +02:00
poljvd
5377414710 Fix missing ScaleDenominator in wmts.tmpl
The missing ScaleDenominator causes GeoServer to throw an error when importing the layer.
2020-09-28 10:33:21 +02:00
Petr Sloup
042b8b986a Merge pull request #463 from korpd/fix-xss-parameter-style
Fix reflected XSS in 'style' parameter
2020-07-02 14:47:24 +02:00
Daniel Korp
038bfe29d6 Fix reflected XSS in 'style' parameter 2020-07-02 10:28:25 +02:00
Petr Sloup
f8563e1f2b Merge pull request #462 from korpd/fix-xss-461
Fix reflected XSS in 'key' parameter. Fixes #461
2020-07-02 09:50:48 +02:00
Daniel Korp
10431d70d0 Fix reflected XSS in 'key' parameter. Fixes #461 2020-07-02 09:30:33 +02:00
Petr Sloup
a5a8ae1e95 Merge pull request #413 from zstadler/xvfbMaxStartWaitTime
Increase initial timeout - xvfbMaxStartWaitTime
2020-05-20 16:12:24 +02:00
Petr Sloup
3c411cd1ac Merge pull request #441 from hfs/fix-preview-thumbnail
Fix preview thumbnail for raster data sources
2020-04-23 08:41:17 +02:00
Hermann Schwarting
79bd70942b Fix preview thumbnail for raster data sources
The data sources structure was changed in the 'dynamic_styles' branch
introducing a tileJSON object. Adapt the code filling the preview
template to the change to get the correct file format instead of
"undefined".

See also f2dc13e.

Fix #440 Preview thumbnail missing for raster data source -- file suffix
"undefined"
2020-04-22 21:08:55 +02:00
Petr Sloup
5585f49396 Update serve_rendered.js
Fix accidentally removed #414
2020-04-07 10:29:19 +02:00
zstadler
8126b31081 Resolve https://github.com/maptiler/tileserver-gl/issues/386 2020-01-28 11:29:42 +02:00
12 changed files with 151 additions and 55 deletions

View File

@@ -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

View File

@@ -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
View 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 "$@"

View File

@@ -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

View File

@@ -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.

View File

@@ -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": {

View File

@@ -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
View File

@@ -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."

View File

@@ -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;

View File

@@ -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);

View File

@@ -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}&amp;` : '';
data['key_query'] = req.query.key ? `?key=${req.query.key}` : '';
req.query.key ? `key=${encodeURIComponent(req.query.key)}&amp;` : '';
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(

View File

@@ -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('&')}`) : '';