Compare commits

...

258 Commits

Author SHA1 Message Date
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
Petr Sloup
d5a079d8f4 v3.0.0 2020-03-11 14:01:17 +01:00
Petr Sloup
29d3e72dd3 Replace last references to the old repository/dockerhub 2020-03-11 13:57:45 +01:00
Petr Sloup
c3bff5ac5f Update dependencies 2020-03-11 13:55:05 +01:00
Petr Sloup
e16de39b93 Minor documentation update 2020-03-11 13:43:23 +01:00
Petr Sloup
13415fd29f Minor tileserver-gl-light fix 2020-03-11 09:24:10 +01:00
Petr Sloup
9a9b920455 Merge pull request #419 from vipyoung/patch-1
Updated usage.rst to reflect public_url parameter
2020-02-25 14:30:09 +01:00
Sofiane Abbar
b55b8adb63 Update usage.rst
Added missing documentation for public_url.
2020-02-25 16:21:17 +03:00
Petr Sloup
9e12ee6f8c Merge pull request #417 from smellman/dev-light2
add option to pass npm publish
2020-02-21 20:47:01 +01:00
Taro Matsuzawa
0e85e0058f - added Building docker image document in README_light.md
- change URL schema from http to https
2020-02-18 08:03:52 +09:00
Taro Matsuzawa
3a94a8f9d2 add option to pass npm publish 2020-02-17 12:36:18 +09:00
Petr Sloup
28dbc78264 Merge pull request #416 from tbicr/update-glyph-pbf-composite-version
update glyph-pbf-composite
2020-02-14 17:59:21 +01:00
Paveł Tyślacki
8f77be3037 update glyph-pbf-composite
glyph-pbf-composite v0.0.2 were released without license
as license were added in v0.0.3 it nice to be updated to licensed software version
2020-02-14 18:59:49 +03:00
Petr Sloup
f2dc13e298 Minor tilejson-reading fix when decorating templates 2020-02-10 14:14:07 +01:00
Petr Sloup
cf0eedb379 Merge branch 'dynamic_styles' 2020-02-10 14:00:58 +01:00
Petr Sloup
95bb59dcfe Merge pull request #414 from xabbu42/issue-171
Close connection on errors
2020-01-28 15:46:52 +01:00
Petr Sloup
224a1300df Merge pull request #415 from xabbu42/issue-350
FIx issue #350 Semi-transparent outlines on raw, premultiplied input
2020-01-28 15:42:36 +01:00
Nathan Gass
9fd381640e Close connection on erros 2020-01-28 14:30:36 +01:00
Nathan Gass
b7c384f1ee FIx issue #350 Semi-transparent outlines on raw, premultiplied input 2020-01-28 14:25:00 +01:00
zstadler
8126b31081 Resolve https://github.com/maptiler/tileserver-gl/issues/386 2020-01-28 11:29:42 +02:00
Petr Sloup
caa641c550 Merge pull request #410 from jdesboeufs/relax-light-version-compatibility
Relax Node.js compatibility for -light version
2020-01-17 16:00:00 +01:00
Jérôme Desboeufs
d02ce0663b Relax Node.js compatibility for -light version 2020-01-17 11:22:17 +01:00
Petr Sloup
ea89d11021 Use chokidar instead of node-watch to poll the files (required for docker) 2020-01-15 09:26:59 +01:00
Petr Sloup
aa933e5154 Fix tests 2020-01-08 15:18:51 +01:00
Petr Sloup
298d09845d Add style validation and skip invalid styles 2020-01-07 17:00:08 +01:00
Petr Sloup
d7a34f3a74 Add "serveAllStyles" option + watch the directory 2020-01-07 15:59:38 +01:00
Petr Sloup
cb700181d3 Refactor style/rendered/data serving to allow for dynamic adding/removing of items 2020-01-07 14:32:30 +01:00
Petr Sloup
226b979592 Fix deprecation warnings 2019-12-30 17:18:05 +01:00
Petr Sloup
13ad268b43 Update mapbox-gl-js to v1.6.1 and mapbox-gl-rtl-text to v0.2.3 2019-12-30 17:11:45 +01:00
Petr Sloup
e2387d164b Simplify the startup procedure a little (#271) 2019-12-30 17:08:15 +01:00
Petr Sloup
515c295898 Remove obsolete old code 2019-12-30 17:00:23 +01:00
Petr Sloup
cdc7803ad8 Update travis to node 10 2019-12-30 16:54:36 +01:00
Petr Sloup
b839979351 Remove uselessly huge background images 2019-12-30 16:54:36 +01:00
Petr Sloup
0a7c403f0b Update repo links, footnote, etc. (klokantech->maptiler) 2019-12-30 16:54:35 +01:00
Petr Sloup
b3d810817d Merge pull request #404 from nyurik/refactor
Update code to ES6
2019-12-30 16:54:19 +01:00
Petr Sloup
96161fc656 Merge branch 'master' into refactor 2019-12-30 11:49:37 +01:00
Petr Sloup
58c769b448 README update 2019-12-30 09:25:12 +01:00
Petr Sloup
bdd0a5c868 Merge pull request #394 from korpd/master
Don't multiply tileMargin with scale since the renderer is already taking care of scaling
2019-12-30 09:21:04 +01:00
Petr Sloup
5048388d1f Try to reduce number of broken labels on tile edges (#344, #347) 2019-12-30 09:11:53 +01:00
Petr Sloup
4c7a227e11 Merge pull request #403 from nyurik/node10
Upgrade to node10, latest dependencies
2019-12-30 09:08:35 +01:00
Yuri Astrakhan
7f8be27844 Update code to ES6
* var  ->  let / const
* [].forEach  ->  for (... of ...)
* '...' + var  ->  template strings  `...${var}`
* function  ->  arrow functions `=>`
* use === and !==  instead of == and !=
2019-12-21 14:09:20 -05:00
Yuri Astrakhan
736e8d393a Upgrade to node10, latest dependencies
use node10 (latest node that has binary built @mapbox/mapbox-gl-native v5.0.2)
use latest canvas and sharp API
2019-12-21 12:09:07 -05:00
Daniel Korp
1c24d12b0d Minor fix: Don't multiply tileMargin with scale since the renderer is already taking care of scaling 2019-11-13 15:10:13 +01:00
Petr Sloup
f77ccd06af Merge pull request #379 from stefslon/tile_margin
Add tileMargin option
2019-10-02 12:26:49 +02:00
Petr Sloup
4996848bdc Update package version to 2.6.0 2019-08-26 12:16:06 +02:00
Petr Sloup
aa7ae575d0 Flatten 3D buildings when rendering to remove artifacts 2019-08-26 12:10:49 +02:00
Petr Sloup
9603703908 Minor css fix 2019-08-26 12:10:32 +02:00
stefslon
1445c545b0 Added tileMargin option to documentation 2019-08-13 22:17:34 -04:00
stefslon
17a73b1d4a Add tileMargin option 2019-08-08 21:57:51 -04:00
Petr Sloup
e44104254e Update handlebars 2019-07-12 11:25:08 +02:00
Petr Sloup
58b536036f Merge pull request #370 from golubev/patch-1
update 'installation.rst': fix a mistake
2019-06-25 07:58:53 +02:00
Sergii Golubev
a68e095400 update 'installation.rst': fix a mistake 2019-06-24 18:56:04 +03:00
Petr Sloup
53b28b35f1 Merge pull request #369 from bradh/patch-1
Trivial typo fix in docs
2019-06-24 07:39:44 +02:00
Brad Hards
58f92ae947 Trivial typo fix in docs 2019-06-22 11:02:36 +10:00
Petr Sloup
e506014763 Minor /styles.json fix (close #361) 2019-04-29 06:48:23 +02:00
Petr Sloup
7c5e7e94e9 Merge pull request #358 from disarticulate/master
Use verbose flag to debug corrupted mbtiles
2019-04-25 10:34:28 +02:00
Eric Xanderson
e6747ebb78 Merge pull request #2 from disarticulate/patch-2
verbose output empty tile warning
2019-04-23 20:53:17 -05:00
Eric Xanderson
2a87ad19c9 Merge pull request #1 from disarticulate/patch-1
add verbose to options
2019-04-23 20:52:46 -05:00
Eric Xanderson
ba62f0bf30 verbose output empty tile warning
Empty tile warning details; definitely what you would expect if there's not a catastrophic error, but you're getting blank tiles.

such as: ```
MBTiles error, serving empty { Error: SQLITE_CORRUPT: database disk image is malformed
lily_tile_server |     at Error (native) errno: 11, code: 'SQLITE_CORRUPT' }

```
https://github.com/klokantech/tileserver-gl/issues/323
2019-04-23 20:51:39 -05:00
Eric Xanderson
08369a666d add verbose to options
pass verbose to server for better handling of warnings
2019-04-23 20:43:56 -05:00
Petr Sloup
f771d41788 Merge pull request #355 from cyclemap/master
let the user define a log file or a log format
2019-04-15 08:00:28 +02:00
adrian
9dda95ee8e let the user define a log file or a log format
log file defaults to standard out.

log format defaults to current behavior ('tiny' for production, 'dev' for dev, and not enabled in test)
2019-04-13 11:55:37 -04:00
Petr Sloup
63483a3155 Merge pull request #339 from ibesora/master
Removes body data from http 204 response
2019-01-07 14:16:30 +01:00
Isaac Besora Vilardaga
bca5191ad9 Removes body data from http 204 response 2019-01-07 13:48:22 +01:00
Petr Sloup
245d9765a9 Merge pull request #337 from mcolmant/master
Correctly retrieve the public_url parameter on start
2019-01-03 10:57:05 +01:00
Maxime Colmant
e8134dfeb0 fix: correctly retrieve the public_url parameter on start 2018-12-26 16:26:49 +01:00
Petr Sloup
a1a8996d3f Update package version to 2.5.0 2018-12-19 13:28:27 +01:00
Petr Sloup
bea528ab64 Update dependencies 2018-12-19 13:22:41 +01:00
Petr Sloup
cde0233130 Merge pull request #230 from koumoul-dev/public_url
add public_url option
2018-12-19 13:19:07 +01:00
Alban Mouton
c99a1a5425 Merge remote-tracking branch 'klokantech/master' into public_url
# Conflicts:
#	src/main.js
#	src/server.js
2018-12-19 09:53:40 +01:00
Petr Sloup
243bdbdaa0 Update Dockerfile_light to match the baseimage version 2018-12-19 09:28:37 +01:00
Petr Sloup
0631a76a51 Merge pull request #313 from guenhter/ensure-xvfb-started
Wait for Xvfb to startup
2018-12-19 09:27:46 +01:00
guenhter
8e1071aad0 Wait for Xvfb to startup 2018-12-19 09:06:53 +01:00
Petr Sloup
e283569897 Merge pull request #333 from luzik/patch-1
new mapbox-gl-native version
2018-12-18 09:10:15 +01:00
Petr Sloup
507da8567e Merge pull request #321 from loicgasser/css-break-all
Break all on long title and item
2018-12-18 09:02:42 +01:00
luzik
9cd6feb56d new mapbox-gl-native version
According to https://github.com/mapbox/mapbox-gl-native/blob/master/platform/node/CHANGELOG.md there is no broken features.
Previous version is not buildable now.

> @mapbox/mapbox-gl-native@3.5.4 install /home/ubuntu/maps/node_modules/@mapbox/mapbox-gl-native
> node-pre-gyp install --fallback-to-build=false || make node
```
node-pre-gyp ERR! install error 
node-pre-gyp ERR! stack Error: 403 status code downloading tarball https://mapbox-node-binary.s3.amazonaws.com/@mapbox/mapbox-gl-native/v3.5.4/node-v57-linux-x64-Release.tar.gz
```
2018-12-17 15:52:05 +01:00
Gasser Loïc
059b0f11a0 Break all on long title and item 2018-10-12 10:53:14 -04:00
Petr Sloup
7acbc93ba9 Update package version to 2.4.0 2018-09-14 08:23:55 +02:00
Petr Sloup
bc3d79bfdc Update dependencies 2018-09-14 08:14:28 +02:00
Petr Sloup
58168eb0a5 Merge pull request #307 from spatialillusions/master
Add support for WMTS capabilities
2018-09-14 08:03:59 +02:00
Petr Sloup
f993b01387 Merge pull request #303 from loicgasser/update_libs
Update commander and devDeps
2018-09-14 08:00:03 +02:00
Måns Beckman
782c28cd64 Add key to WMTS xml
This should add the key to the wmts xml for hosted services
2018-09-13 17:58:13 +02:00
Måns Beckman
76a0064f0c Remove unused variables 2018-09-13 17:57:06 +02:00
Petr Sloup
55fa8bf8c1 Merge pull request #278 from amirmasud/return-204-when-no-tile-found
Return 204 when mbtiles error is 'Tile does not exist'
2018-09-13 16:26:35 +02:00
Petr Sloup
0c42fdd6b3 Merge pull request #310 from guenhter/fix-restart
Ensure that container can be restarted
2018-09-13 16:25:16 +02:00
Måns Beckman
e8b3c02546 Fix formating 2018-09-11 17:51:38 +02:00
Måns Beckman
56089c0a62 Remove base64url 2018-09-11 17:47:44 +02:00
guenhter
857ff2d8d7 Ensure that container can be restarted 2018-09-10 10:31:58 +02:00
Måns Beckman
e310fa5165 Merge branch 'master' of https://github.com/spatialillusions/tileserver-gl 2018-08-21 19:33:56 +02:00
Måns Beckman
71fab2a2a1 Add support for WMTS capabilites
https://github.com/klokantech/tileserver-gl/issues/274

Clean up wmts template with correct syntax

Update endpoint URL and change content type

After a good nights sleep I considered the structure of the endpoint link for WMTS, at the same time I cleaned up the code and added the correct content type for the response.
2018-08-21 19:33:37 +02:00
Petr Sloup
1c59e3d742 Fix typo 2018-08-21 19:33:37 +02:00
Måns Beckman
6e24f6ec8e Update endpoint URL and change content type
After a good nights sleep I considered the structure of the endpoint link for WMTS, at the same time I cleaned up the code and added the correct content type for the response.
2018-08-18 12:02:54 +02:00
Måns Beckman
5d5ed1153f Clean up wmts template with correct syntax 2018-08-17 15:48:35 +02:00
Måns Beckman
baf01a117b Add support for WMTS capabilites
https://github.com/klokantech/tileserver-gl/issues/274
2018-08-17 13:11:19 +02:00
Gasser Loïc
2cc2cc1b3b Update commander to the latest version 2018-08-08 11:44:46 -04:00
Loïc Gasser
45ed8d90ca Update commander and devDeps, invert default val and function accordingly 2018-08-03 00:05:20 -04:00
Petr Sloup
8e66736955 Fix typo 2018-06-21 19:00:10 +02:00
Masud Zare
72ea5ab05d fix corresponding test scenario 2018-05-07 15:58:32 +04:30
Masud Zare
9d642fa0b3 return 204 when mbtiles error is 'Tile does not exist' 2018-05-07 15:21:41 +04:30
Petr Sloup
1d734f9b42 Merge pull request #270 from geoadmin/fix_doc
Add silent to doc
2018-04-19 08:38:44 +02:00
loicgasser
80146ed7d9 Add silent to doc 2018-03-22 16:12:06 +00:00
Petr Sloup
a28df7ef8f Merge pull request #269 from michaill/master
Silent mode (do not log requests with status == 200)
2018-03-22 13:14:40 +01:00
Michal Illovsky
c333197a7c Skipping also 304 status codes when in silent mode 2018-03-22 09:02:09 +01:00
Michal Illovsky
45df72df51 Enable silent mode with -s option to not log successful request (status=200) 2018-03-19 13:47:17 +01:00
Petr Sloup
e737753891 Merge pull request #263 from geoadmin/nomnom_commander
Replace Nomnom with Commander
2018-03-14 15:17:29 +01:00
loicgasser
edff8ce06c Fix version and positional argument for mbtiles 2018-03-12 11:53:11 +00:00
loicgasser
df05ce3f2a Add default val in description 2018-03-08 17:55:30 +00:00
loicgasser
a74ca20375 Replace nomnom with commander 2018-03-08 16:05:58 +00:00
loicgasser
1c890b0157 Update doc 2018-03-08 16:05:46 +00:00
Petr Sloup
7a9f04a024 Update MB GL JS to v0.43.0 2018-03-01 09:26:56 +01:00
Petr Pridal
1354c7f663 Update README.md 2018-02-19 15:49:08 +01:00
Petr Sloup
da3cb1e7f5 Update ISSUE_TEMPLATE.md 2018-01-23 12:17:14 +01:00
Petr Sloup
19e821a8fe Update package version to 2.3.1 2017-12-14 09:56:41 +01:00
Petr Sloup
659289d85c Update dependencies 2017-12-14 09:56:21 +01:00
Petr Sloup
734d1f01f0 Prioritize font fallbacks and try to respect proper font style 2017-12-14 09:55:57 +01:00
Petr Sloup
c1055a9647 Update package version to 2.3.0 2017-12-01 11:13:11 +01:00
Petr Sloup
2afb460191 Merge pull request #242 from samizdis/master
Addresses issue #197, send SIGTERM through to node
2017-12-01 10:52:52 +01:00
Sam Brown
8eb736b821 Addresses issue #197, send SIGTERM through to node
Node can't be PID 1, else it can't be term'ed and xvfb won't stop nicely.  So, according to https://docs.docker.com/engine/reference/builder/#entrypoint, we need to make sure it's not the bare entrypoint.

Also we need to be sure that run.sh passes the signal through to node, see https://unix.stackexchange.com/questions/146756/forward-sigterm-to-child-in-bash
2017-11-30 12:30:40 +00:00
Petr Sloup
83e20b7a4e Merge pull request #232 from rani-pinchuk/fix-zlib-incorrect-header-check
Catch incorrect header exceptions
2017-11-13 09:39:06 +01:00
Rani Pinchuk
81f65af3a8 Fix log message when incorrect header exception is caught 2017-11-09 18:00:41 +01:00
Petr Sloup
f5ea790878 Stop Xvfb when stopping the container (#197) 2017-11-08 17:01:28 +01:00
Rani Pinchuk
a0eb5800fd Catch incorrect header exceptions 2017-11-03 16:53:45 +01:00
Petr Sloup
27eb7f0ed8 Merge pull request #229 from tschaub/handle-rejection
Handle promise rejection
2017-11-01 09:15:02 +01:00
Tim Schaub
2f9059d09e Unnecessary promise wrapping 2017-10-23 10:05:30 -06:00
Tim Schaub
e11c8f9315 Reject font listing promise on error 2017-10-23 10:01:43 -06:00
Tim Schaub
650718e0f6 Avoid swallowing rejected font listing promise 2017-10-23 10:01:43 -06:00
Tim Schaub
5ed632c229 Reject source info promise on error 2017-10-23 10:01:32 -06:00
Tim Schaub
f545076986 Avoid swallowing rejected source info promise 2017-10-23 10:01:19 -06:00
Tim Schaub
3d48485475 Reject on font loading error 2017-10-23 10:01:16 -06:00
Tim Schaub
c060dedf20 Avoid swallowing rejected font loading promise 2017-10-23 10:01:01 -06:00
Tim Schaub
cd1f5fd04a Return after rejecting, catch and log 2017-10-23 09:46:04 -06:00
Alban Mouton
dc6be5047c missing public_url in tiles urls 2017-10-18 14:49:10 +02:00
Alban Mouton
47ff33166d missing public_url in templates 2017-10-18 14:20:33 +02:00
Alban Mouton
875521c5a8 add public_url option 2017-10-18 13:51:37 +02:00
Petr Sloup
82f179b07c Minor fix for correctly serving empty responses for missing tiles 2017-10-02 13:56:32 +02:00
Petr Sloup
51d6ca0880 Merge pull request #222 from otsaloma/renderer-pool-sizes
Make raster tile renderer pool sizes configurable
2017-10-02 10:04:22 +02:00
Osmo Salomaa
89878015bb Make raster tile renderer pool sizes configurable 2017-10-01 18:40:16 +03:00
Petr Pridal
ac948b6dee Create ISSUE_TEMPLATE.md 2017-09-28 00:53:05 +02:00
Petr Sloup
440775dbbf Update README.md 2017-09-25 13:23:05 +02:00
Petr Sloup
ea408f8ec9 Increase test timeout since travis can be slow on cold start when under load 2017-09-20 08:21:34 +02:00
Petr Sloup
da646868c3 Wait longer for xvfb to start up (#197) 2017-09-20 08:05:10 +02:00
Petr Sloup
d60996e3c6 Reorganize Dockerfile instructions for faster rebuilds 2017-09-20 08:03:46 +02:00
Petr Sloup
02cf45bbd9 Clean Dockerfile by basing on node:6-stretch 2017-09-20 08:01:35 +02:00
Petr Sloup
d9f8582279 Remove node-pngquant-native dependency
- Unnecessary native dependency
- It was disabled by default
- Has issues on certain platforms
- Not optimal for production use (performance)
2017-09-15 17:06:42 +02:00
Petr Sloup
54f11a2125 Update package version to 2.2.0 2017-09-01 11:37:18 +02:00
Petr Sloup
598c8c590b Update package dependencies 2017-09-01 11:30:52 +02:00
Petr Sloup
438a18eec1 Merge pull request #207 from tschaub/handle-request-failures
Try to create the appropriate data type on http errors
2017-08-24 09:33:22 +02:00
Tim Schaub
7bdb7afcb9 Reuse the function for creating empty responses 2017-08-23 11:30:05 -04:00
Tim Schaub
2e46700cd9 Try to create the appropriate data type on http errors 2017-08-22 14:17:53 -04:00
Petr Sloup
bb09f3df64 Add --rm to docker run instruction to run it more cleanly 2017-07-28 09:54:12 +02:00
Petr Sloup
42f24c2c99 Add concept of data decorator function 2017-07-28 09:53:32 +02:00
Petr Sloup
427a0f0687 Update package version to 2.1.0 2017-07-03 09:41:27 +02:00
Petr Sloup
da5ea4b426 Update Mapbox GL JS to v0.38.0 (close #188) 2017-07-03 09:39:55 +02:00
Petr Sloup
2208ff1e24 Merge pull request #187 from aleksejleonov/debian_stretch_nodejs_repo
Use debian stretch nodejs repo
2017-07-03 09:29:29 +02:00
Oleksii Leonov
cf521058be Use debian stretch nodejs repo
Dockerfile based on debian:stretch, so better to use native stretch nodejs repo instead of jessie repo.
2017-06-30 00:54:13 +03:00
Petr Sloup
a9b38022bb Give xvfb some time to start up before starting the server in docker (#185) 2017-06-29 10:50:02 +02:00
Petr Sloup
654bdda629 Update package version to 2.0.0 2017-06-23 21:45:11 +02:00
Petr Sloup
537313840e Update node version in documentation 2017-06-23 21:44:40 +02:00
Petr Sloup
d30f8464b2 Add some packages to travis and Dockerfile 2017-06-23 21:34:59 +02:00
Petr Sloup
698c527e94 Change endpoint URLs (close #154)
- styles at /style/{id}/style.json
- rendered tiles at /style/{id}/{z}/{x}/{y}.{format}
- TileJSONs at /style/{id}.json
2017-06-22 16:37:32 +02:00
Petr Sloup
8007f1386c Add required package for travis and Dockerfile 2017-06-22 16:37:32 +02:00
Petr Sloup
4f2fdf602b Update Dockerfiles to use node v6 2017-06-22 16:37:32 +02:00
Petr Sloup
6d7397647a Update travis script to use node v6 2017-06-22 16:37:32 +02:00
Petr Sloup
8fd7a9b42b Update dependencies to get ready for node v6 2017-06-22 16:37:32 +02:00
Petr Sloup
95470143b6 Merge pull request #179 from tschaub/err
Log errors to stderr and return
2017-06-20 17:29:39 +02:00
Tim Schaub
de83021c3d Log errors to stderr and return 2017-06-20 08:09:59 -07:00
Petr Sloup
7de3d8b9c7 Update package version to 1.7.0 2017-05-10 10:32:44 +02:00
Petr Sloup
bdc3d20524 Use promises instead of async in font concatenation (remove async dependency) 2017-05-10 10:22:39 +02:00
Petr Sloup
5d93b1d4f9 Add healthcheck endpoint (close #140) 2017-05-10 08:57:51 +02:00
Petr Sloup
d30027e992 Modify all serve_* modules to return a Promise (preparation for #140) 2017-05-10 08:56:43 +02:00
Petr Sloup
c233d23523 Fix broken static maps bounds overlay (for very large areas) 2017-05-05 16:15:10 +02:00
Petr Sloup
1109c77ec2 Update package version to 1.6.0 2017-05-04 12:07:58 +02:00
Petr Sloup
88cf9b37a9 Revert mapbox-gl-native package update
It broke travis (to be further investigated)
2017-05-04 11:21:57 +02:00
Petr Sloup
2d207f792b Update package dependencies 2017-05-04 11:13:24 +02:00
Petr Sloup
d67a57861d Merge pull request #155 from tschaub/reconfigure-on-sighup
Reload configuration on SIGHUP
2017-05-04 11:02:48 +02:00
Petr Sloup
27e9dbfb4e Merge pull request #157 from tschaub/address
Enclose literal IPv6 addresses in brackets
2017-05-04 08:57:17 +02:00
Tim Schaub
a199008fa3 Enclose literal IPv6 addresses in brackets 2017-04-28 07:28:49 -07:00
Tim Schaub
e88b786073 Reload configuration on SIGHUP 2017-04-28 07:13:34 -07:00
Petr Sloup
c03b0a12f8 Merge pull request #156 from tschaub/no-callback
Remove unused callback
2017-04-28 11:04:17 +02:00
Tim Schaub
a234041cd1 Remove unused callback 2017-04-26 08:02:18 -07:00
Petr Sloup
49a779970e Merge pull request #150 from pirxpilot/no-cors
add `--cors` option to allow for optional CORS handling
2017-04-18 09:32:11 +02:00
Petr Sloup
9545c2594e Handle scale in query-based static endpoint 2017-04-14 12:05:03 +02:00
Damian Krzeminski
6c23d95feb add --cors option to allow for optional CORS handling
we are using `cors` middleware with default options which works for most
applications, but does not allow for fine tuning (whitelisting origins
etc.)

this change keeps CORS handling as default to preserve compatibility but
also allows for specying `--no-cors` option which makes it possible to
handle CORS in an independent proxy (NGINX, another node app etc.)
2017-04-12 09:43:43 -07:00
Petr Sloup
366380395e Proper error message when metadata are missing in the mbtiles (close #147) 2017-04-11 19:10:40 +02:00
Petr Sloup
8ea665297f Minor fix in style path handling (allow absolute paths) 2017-04-07 18:53:11 +02:00
Petr Sloup
f6580c0342 Improved request logging 2017-04-04 18:46:18 +02:00
Petr Sloup
34a139040c Slight docker performance optimization 2017-04-04 18:46:18 +02:00
Petr Sloup
66bea8a42b Merge pull request #142 from somthanat/master
Word correction (Forwaded -> Forwarded)
2017-04-03 15:16:40 +02:00
Petr Sloup
28790fda30 Alternative query-based static endpoint 2017-04-03 15:14:40 +02:00
Somthanat Wongsa
0b16af0084 Word correction (Forwaded -> Forwarded) 2017-03-30 20:16:03 +07:00
Petr Sloup
e1654a51de Update package version to 1.5.0 2017-03-29 15:46:30 +02:00
Petr Sloup
5372bab6c5 Update dependencies 2017-03-29 15:45:32 +02:00
Petr Sloup
6a960e8593 Remove optional dependencies from package.json to avoid warnings 2017-03-17 10:42:25 +01:00
Petr Sloup
1577cfa54a Update dependencies 2017-03-15 17:20:55 +01:00
Petr Sloup
640038a115 Add support for watermarks (close #130) 2017-03-15 17:06:26 +01:00
Petr Sloup
49a8562441 Support for proj4 string in mbtiles metadata (for static maps) (close #127) 2017-03-15 16:45:26 +01:00
Petr Sloup
1e402ed207 Add possibility to change the front page (close #128) 2017-03-15 15:50:58 +01:00
Petr Sloup
f8949c1aa9 Configurable scale factors (close #121)
Also changes default maximum from `4x` to `3x`
2017-03-15 12:09:18 +01:00
Petr Sloup
7b952ee5c0 Do not add style parameter when not needed (close #134) 2017-03-15 11:39:21 +01:00
Petr Sloup
0673c8990a Add option to disable static maps (close #129) 2017-03-14 16:12:19 +01:00
Petr Sloup
37386bfb29 Hide empty headers on index (close #125) 2017-03-14 16:02:54 +01:00
Petr Sloup
b93bc5fadc Support for handling relative subdomain patterns 2017-03-14 15:50:14 +01:00
Petr Sloup
ca7a6ac515 Update package version to 1.4.1 2017-02-03 10:05:16 +01:00
Petr Sloup
9cc3f9df6a Update dependencies 2017-02-03 10:04:55 +01:00
Petr Sloup
e6ebb918fb Add mapbox-gl-rtl-text to correctly render labels in javascript (close #112) 2017-02-03 10:01:31 +01:00
Petr Sloup
ba0f441490 Update to mapbox-gl-js v0.32.1 2017-02-03 09:54:02 +01:00
Petr Sloup
e3ac9fbb4e Remove native dependency from tileserver-gl-light (close #113) 2017-02-03 09:42:31 +01:00
Petr Sloup
2f2e4b8f42 Remap data link also for the rendered tiles 2017-02-01 14:57:38 +01:00
Petr Sloup
bfadeb799b Experimental option to remap data links from inside of the styles 2017-02-01 14:25:21 +01:00
Petr Sloup
2da1bac6c7 Update package version to 1.4.0 2017-01-25 15:36:49 +01:00
Petr Sloup
6f5a7ed704 Update mapbox-gl.js to v0.31.0 2017-01-25 15:30:58 +01:00
Petr Sloup
3722c653f1 Rename /fontstacks.json to /fonts.json for better consistency (#104) 2017-01-25 15:30:58 +01:00
Petr Sloup
6231f9f7a7 Configurable optional alias for .pbf tiles (#109) 2017-01-25 15:30:58 +01:00
Petr Sloup
1079ece860 Index footer improvements (fix typo) 2017-01-25 15:30:58 +01:00
Petr Pridal
841cb82f85 Update OSM2VectorTiles to OpenMapTiles in Usage.rst 2017-01-25 01:27:31 +01:00
Petr Sloup
5c1d396ed2 Fix incorrect parenthesis (#108) 2017-01-24 11:18:56 +01:00
Petr Sloup
4992bc4194 Fix url instrumentation when serving style to be more robust (close #108) 2017-01-23 12:58:47 +01:00
Petr Sloup
f14d8b1e57 Update sharp dependency 2017-01-20 10:20:06 +01:00
Petr Sloup
14350a4338 Update mapbox-gl-native dependency 2017-01-20 10:11:10 +01:00
Petr Sloup
01fbaad962 Check configured paths exist on startup 2017-01-19 14:20:41 +01:00
Petr Sloup
26e6d56d9b Update README.md 2017-01-19 09:24:26 +01:00
Petr Sloup
66e3a3da1b Update package version to 1.3.2 2017-01-17 21:18:49 +01:00
Petr Sloup
63482a2a39 Generate version-based identifier for data when autogenerating config 2017-01-17 21:18:12 +01:00
43 changed files with 2684 additions and 2147 deletions

View File

@@ -1,7 +1,6 @@
.git *
docs/_build !src
node_modules !public
test_data !package.json
light !package-lock.json
config.json !docker-entrypoint.sh
*.mbtiles

View File

@@ -1,6 +1,6 @@
language: node_js language: node_js
node_js: node_js:
- "4" - "10"
env: env:
- CXX=g++-4.8 - CXX=g++-4.8
addons: addons:
@@ -12,10 +12,10 @@ addons:
before_install: before_install:
- sudo apt-get update -qq - sudo apt-get update -qq
- sudo apt-get install -qq libcairo2-dev libjpeg8-dev libpango1.0-dev libgif-dev build-essential g++ - sudo apt-get install -qq libcairo2-dev libjpeg8-dev libpango1.0-dev libgif-dev build-essential g++
- sudo apt-get install -qq xvfb - sudo apt-get install -qq xvfb libgles2-mesa-dev libgbm-dev libxxf86vm-dev
install: install:
- npm install - npm install
- wget -O test_data.zip https://github.com/klokantech/tileserver-gl/releases/download/v1.3.0/test_data.zip - wget -O test_data.zip https://github.com/maptiler/tileserver-gl/releases/download/v1.3.0/test_data.zip
- unzip -q test_data.zip -d test_data - unzip -q test_data.zip -d test_data
script: script:
- xvfb-run --server-args="-screen 0 1024x768x24" npm test - xvfb-run --server-args="-screen 0 1024x768x24" npm test

View File

@@ -1,30 +1,55 @@
FROM debian:stretch FROM node:10-buster AS builder
MAINTAINER Petr Sloup <petr.sloup@klokantech.com>
RUN apt-get -qq update \ RUN export DEBIAN_FRONTEND=noninteractive \
&& DEBIAN_FRONTEND=noninteractive apt-get -y install \ && apt-get -qq update \
apt-transport-https \ && apt-get -y --no-install-recommends install \
curl \ apt-transport-https \
unzip \ curl \
build-essential \ unzip \
python \ build-essential \
libcairo2-dev \ python \
libprotobuf-dev \ libcairo2-dev \
xvfb \ libgles2-mesa-dev \
&& echo "deb https://deb.nodesource.com/node_4.x jessie main" >> /etc/apt/sources.list.d/nodejs.list \ libgbm-dev \
&& echo "deb-src https://deb.nodesource.com/node_4.x jessie main" >> /etc/apt/sources.list.d/nodejs.list \ libllvm7 \
&& apt-get -qq update \ libprotobuf-dev \
&& DEBIAN_FRONTEND=noninteractive apt-get -y --allow-unauthenticated install \ && apt-get -y --purge autoremove \
nodejs \ && apt-get clean \
&& rm /etc/apt/sources.list.d/nodejs.list \ && rm -rf /var/lib/apt/lists/*
&& apt-get clean
COPY . /usr/src/app
ENV NODE_ENV="production"
RUN mkdir -p /usr/src/app
COPY / /usr/src/app
RUN cd /usr/src/app && npm install --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 VOLUME /data
WORKDIR /data WORKDIR /data
EXPOSE 80 EXPOSE 80
ENTRYPOINT ["/usr/src/app/run.sh"]
USER node:node
ENTRYPOINT ["/app/docker-entrypoint.sh"]
CMD ["-p", "80"]

View File

@@ -1,12 +1,13 @@
FROM node:4 FROM node:10-stretch
MAINTAINER Petr Sloup <petr.sloup@klokantech.com>
ENV NODE_ENV="production"
ENV CHOKIDAR_USEPOLLING=1
ENV CHOKIDAR_INTERVAL=500
EXPOSE 80
VOLUME /data
WORKDIR /data
ENTRYPOINT ["node", "/usr/src/app/", "-p", "80"]
RUN mkdir -p /usr/src/app RUN mkdir -p /usr/src/app
COPY / /usr/src/app COPY / /usr/src/app
RUN cd /usr/src/app && npm install --production RUN cd /usr/src/app && npm install --production
VOLUME /data
WORKDIR /data
EXPOSE 80
ENTRYPOINT ["node", "/usr/src/app/", "-p", "80"]

34
Dockerfile_test Normal file
View File

@@ -0,0 +1,34 @@
# Run tests inside docker without requiring full installation of dependencies on local machine
# Simply run "docker build -f Dockerfile_test ."
# WARNING: sometimes it fails with a core dumped exception
FROM node:10-stretch
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 \
&& apt-get clean
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
RUN wget -O test_data.zip https://github.com/maptiler/tileserver-gl/releases/download/v1.3.0/test_data.zip
RUN unzip -q test_data.zip -d test_data
ENV NODE_ENV="test"
COPY package.json .
RUN npm install
COPY / .
RUN xvfb-run --server-args="-screen 0 1024x768x24" npm test

21
ISSUE_TEMPLATE.md Normal file
View File

@@ -0,0 +1,21 @@
It is great you want to help us making TileServer GL project better!
This is the right place only for a software bug report or a new software feature request.
NOTE: Questions about OpenMapTiles data, OpenMapTiles Server, TileHosting and other software/products do not belong here (and will not be answered)!
The usage and installation questions belongs to https://stackoverflow.com/questions/tagged/openmaptiles
A guaranteed support and consulting from the core developers via https://openmaptiles.com/support/
Please search this GitHub repo for similar requests before posting (check also closed tickets).
When reporting a problem you have with the TileServer GL software please provide:
- Clear description of the problem: What steps will lead to reproducing the error on our computer? What is exactly wrong?
- Version of the TileServer GL software you have used
- Version and name of the operating system you use or other details of your setup
- Information about your used config / styles / vector tiles
- URL / link to the specific location in a live map demo where a bug is visible is always great
- Screenshot of the problem are cool! Drag&drop an image to the report here...
We love pull requests! If you are able to code, please send us your fix or code modification via GitHub... Thanks!

View File

@@ -2,23 +2,25 @@
# TileServer GL # TileServer GL
[![Build Status](https://travis-ci.org/klokantech/tileserver-gl.svg?branch=master)](https://travis-ci.org/klokantech/tileserver-gl) [![Build Status](https://travis-ci.org/maptiler/tileserver-gl.svg?branch=master)](https://travis-ci.org/maptiler/tileserver-gl)
[![Docker Hub](https://img.shields.io/badge/docker-hub-blue.svg)](https://hub.docker.com/r/klokantech/tileserver-gl/) [![Docker Hub](https://img.shields.io/badge/docker-hub-blue.svg)](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 Mapbox GL JS, Android, iOS, Leaflet, OpenLayers, GIS via WMTS, etc. Vector and raster maps with GL styles. Server side rendering by Mapbox GL Native. Map tile server for Mapbox GL JS, Android, iOS, Leaflet, OpenLayers, GIS via WMTS, etc.
## Get Started ## Get Started
Make sure you have Node.js version **10** installed (running `node -v` it should output something like `v10.17.0`).
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 ```bash
npm install -g tileserver-gl npm install -g tileserver-gl
``` ```
Now download vector tiles from [OSM2VectorTiles](http://osm2vectortiles.org/downloads/). Now download vector tiles from [OpenMapTiles](https://openmaptiles.org/downloads/).
```bash ```bash
curl -o zurich_switzerland.mbtiles https://osm2vectortiles-downloads.os.zhdk.cloud.switch.ch/v2.0/extracts/zurich_switzerland.mbtiles curl -o zurich_switzerland.mbtiles https://[GET-YOUR-LINK]/extracts/zurich_switzerland.mbtiles
``` ```
Start `tileserver-gl` with the downloaded vector tiles. Start `tileserver-gl` with the downloaded vector tiles.
@@ -31,10 +33,10 @@ Alternatively, you can use the `tileserver-gl-light` package instead, which is p
## Using Docker ## Using Docker
An alternative to npm to start the packed software easier is to install [Docker](http://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 in the directory with the downloaded MBTiles the command:
```bash ```bash
docker run -it -v $(pwd):/data -p 8080:80 klokantech/tileserver-gl docker run --rm -it -v $(pwd):/data -p 8080:80 maptiler/tileserver-gl
``` ```
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. 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.
@@ -43,4 +45,4 @@ On laptop you can use [Docker Kitematic](https://kitematic.com/) and search "til
## Documentation ## Documentation
You can read full documentation of this project at http://tileserver.readthedocs.io/. You can read full documentation of this project at https://tileserver.readthedocs.io/.

View File

@@ -1,6 +1,6 @@
# TileServer GL light # TileServer GL light
[![Build Status](https://travis-ci.org/klokantech/tileserver-gl.svg?branch=master)](https://travis-ci.org/klokantech/tileserver-gl) [![Build Status](https://travis-ci.org/maptiler/tileserver-gl.svg?branch=master)](https://travis-ci.org/maptiler/tileserver-gl)
[![Docker Hub](https://img.shields.io/badge/docker-hub-blue.svg)](https://hub.docker.com/r/klokantech/tileserver-gl/) [![Docker Hub](https://img.shields.io/badge/docker-hub-blue.svg)](https://hub.docker.com/r/maptiler/tileserver-gl/)
Vector maps with GL styles. Map tile server for Mapbox Android, iOS, GL JS, Leaflet, OpenLayers, etc. without server side rendering. Vector maps with GL styles. Map tile server for Mapbox Android, iOS, GL JS, Leaflet, OpenLayers, etc. without server side rendering.
@@ -11,7 +11,25 @@ Then you can simply run `tileserver-gl-light zurich_switzerland.mbtiles` to star
See also `tileserver-gl` which contains server side rendering. See also `tileserver-gl` which contains server side rendering.
Prepared vector tiles can be downloaded from [OSM2VectorTiles](http://osm2vectortiles.org/). Prepared vector tiles can be downloaded from [OpenMapTiles.com](https://openmaptiles.com/downloads/planet/).
## Building docker image
You can build TileServer GL light image from source.
```
git clone https://github.com/maptiler/tileserver-gl.git
cd tileserver-gl
node publish.js --no-publish
cd light
docker build -t tileserver-gl-light .
```
[Download from OpenMapTiles.com](https://openmaptiles.com/downloads/planet/) or [create](https://github.com/openmaptiles/openmaptiles) your vector tile, and run following in directory contains your *.mbtiles.
```
docker run --rm -it -v $(pwd):/data -p 8000:80 tileserver-gl-light
```
## Documentation ## Documentation
You can read full documentation of this project at http://tileserver.readthedocs.io/. You can read full documentation of this project at https://tileserver.readthedocs.io/.

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. The configuration file defines the behavior of the application. It's a regular JSON file.
Example:: Example:
.. code-block:: json
{ {
"options": { "options": {
@@ -21,12 +23,15 @@ Example::
], ],
"formatQuality": { "formatQuality": {
"jpeg": 80, "jpeg": 80,
"webp": 90, "webp": 90
"pngQuantization": false,
"png": 90
}, },
"maxScaleFactor": 3,
"maxSize": 2048, "maxSize": 2048,
"serveAllFonts": false "pbfAlias": "pbf",
"serveAllFonts": false,
"serveAllStyles": false,
"serveStaticMaps": true,
"tileMargin": 0
}, },
"styles": { "styles": {
"basic": { "basic": {
@@ -67,17 +72,74 @@ The value of ``root`` is used as prefix for all data types.
You can use this to optionally specify on what domains the rendered tiles are accessible. This can be used for basic load-balancing or to bypass browser's limit for the number of connections per domain. You can use this to optionally specify on what domains the rendered tiles are accessible. This can be used for basic load-balancing or to bypass browser's limit for the number of connections per domain.
``frontPage``
-----------------
Path to the html (relative to ``root`` path) to use as a front page.
Use ``true`` (or nothing) to serve the default TileServer GL front page with list of styles and data.
Use ``false`` to disable the front page altogether (404).
``formatQuality`` ``formatQuality``
----------------- -----------------
Quality of the compression of individual image formats. [0-100] Quality of the compression of individual image formats. [0-100]
The value for ``png`` is only used when ``pngQuantization`` is ``true``. ``maxScaleFactor``
-----------
Maximum scale factor to allow in raster tile and static maps requests (e.g. ``@3x`` suffix).
Also see ``maxSize`` below.
Default value is ``3``, maximum ``9``.
``maxSize`` ``maxSize``
----------- -----------
Maximum image side length to be allowed to be rendered (including scale factor). Default is ``2048``. Maximum image side length to be allowed to be rendered (including scale factor).
Be careful when changing this value since there are hardware limits that need to be considered.
Default is ``2048``.
``tileMargin``
--------------
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 generate a single tile.
Default is ``0`` to disable this processing.
``minRendererPoolSizes``
------------------------
Minimum amount of raster tile renderers per scale factor.
The value is an array: the first element is the minimum amount of renderers for scale factor one, the second for scale factor two and so on.
If the array has less elements than ``maxScaleFactor``, then the last element is used for all remaining scale factors as well.
Selecting renderer pool sizes is a trade-off between memory use and speed.
A reasonable value will depend on your hardware and your amount of styles and scale factors.
If you have plenty of memory, you'll want to set this equal to ``maxRendererPoolSizes`` to avoid increased latency due to renderer destruction and recreation.
If you need to conserve memory, you'll want something lower than ``maxRendererPoolSizes``, possibly allocating more renderers to scale factors that are more common.
Default is ``[8, 4, 2]``.
``maxRendererPoolSizes``
------------------------
Maximum amount of raster tile renderers per scale factor.
The value and considerations are similar to ``minRendererPoolSizes`` above.
If you have plenty of memory, try setting these equal to or slightly above your processor count, e.g. if you have four processors, try a value of ``[6]``.
If you need to conserve memory, try lower values for scale factors that are less common.
Default is ``[16, 8, 4]``.
``serveAllStyles``
------------------------
If this option is enabled, all the styles from the ``paths.styles`` will be served. (No recursion, only ``.json`` files are used.)
The process will also watch for changes in this directory and remove/add more styles dynamically.
It is recommended to also use the ``serveAllFonts`` option when using this option.
``watermark``
-----------
Optional string to be rendered into the raster tiles (and static maps) as watermark (bottom-left corner).
Can be used for hard-coding attributions etc. (can also be specified per-style).
Not used by default.
``styles`` ``styles``
========== ==========
@@ -86,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] * ``style`` -- name of the style json file [required]
* ``serve_rendered`` -- whether to render the raster tiles for this style or not * ``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 * ``tilejson`` -- properties to add to the TileJSON created for the raster data
* ``format`` and ``bounds`` can be especially useful * ``format`` and ``bounds`` can be especially useful
@@ -129,7 +191,7 @@ Sprites
If your style requires any sprites, make sure the style JSON contains proper path in the ``sprite`` property. If your style requires any sprites, make sure the style JSON contains proper path in the ``sprite`` property.
It can be a local path (e.g. ``my-style/sprite``) or remove http(s) location (e.g. ``https://mycdn.com/my-style/sprite``). Several possible extension are added to this path, so the following files should be present: It can be a local path (e.g. ``my-style/sprite``) or remote http(s) location (e.g. ``https://mycdn.com/my-style/sprite``). Several possible extension are added to this path, so the following files should be present:
* ``sprite.json`` * ``sprite.json``
* ``sprite.png`` * ``sprite.png``

View File

@@ -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 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-Forwaded-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 ensures the URLs generated inside TileJSON etc. are using the desired domain and protocol.

View File

@@ -6,21 +6,25 @@ If you visit the server on the configured port (default 8080) you can see your m
Styles Styles
====== ======
* Styles are served at ``/styles/{id}.json`` (+ array at ``/styles.json``) * Styles are served at ``/styles/{id}/style.json`` (+ array at ``/styles.json``)
* Sprites at ``/styles/{id}/sprite[@2x].{format}`` * Sprites at ``/styles/{id}/sprite[@2x].{format}``
* Fonts at ``/fonts/{fontstack}/{start}-{end}.pbf`` * Fonts at ``/fonts/{fontstack}/{start}-{end}.pbf``
Rendered tiles Rendered tiles
============== ==============
* Rendered tiles are served at ``/styles/{id}/rendered/{z}/{x}/{y}[@2x].{format}`` * Rendered tiles are served at ``/styles/{id}/{z}/{x}/{y}[@2x].{format}``
* The optional ``@2x`` (or ``@3x``, ``@4x``) part can be used to render HiDPI (retina) tiles * The optional ``@2x`` (or ``@3x``, ``@4x``) part can be used to render HiDPI (retina) tiles
* Available formats: ``png``, ``jpg`` (``jpeg``), ``webp`` * Available formats: ``png``, ``jpg`` (``jpeg``), ``webp``
* TileJSON at ``/styles/{id}/rendered.json`` * TileJSON at ``/styles/{id}.json``
* The rendered tiles are not available in the ``tileserver-gl-light`` version. * The rendered tiles are not available in the ``tileserver-gl-light`` version.
WMTS Capabilities
==============
* WMTS Capabilities are served at ``/styles/{id}/wmts.xml``
Static images Static images
============= =============
* Several endpoints: * Several endpoints:
@@ -39,7 +43,7 @@ Static images
* ``fill`` - color to use as the fill (e.g. ``red``, ``rgba(255,255,255,0.5)``, ``#0000ff``) * ``fill`` - color to use as the fill (e.g. ``red``, ``rgba(255,255,255,0.5)``, ``#0000ff``)
* ``stroke`` - color of the path stroke * ``stroke`` - color of the path stroke
* ``width`` - width of the stroke * ``width`` - width of the stroke
* ``padding`` - "percetange" padding for fitted endpoints (area-based and path autofit) * ``padding`` - "percentage" padding for fitted endpoints (area-based and path autofit)
* value of ``0.1`` means "add 10% size to each side to make sure the area of interest is nicely visible" * value of ``0.1`` means "add 10% size to each side to make sure the area of interest is nicely visible"
@@ -63,4 +67,11 @@ Array of all TileJSONs is at ``/index.json`` (``/rendered.json``; ``/data.json``
List of available fonts List of available fonts
======================= =======================
Array of names of the available fonts is at ``/fontstacks.json`` Array of names of the available fonts is at ``/fonts.json``
Health check
============
Endpoint reporting health status is at ``/health`` and currently returns:
* ``503`` Starting - for a short period before everything is initialized
* ``200`` OK - when the server is running

View File

@@ -7,12 +7,12 @@ Docker
When running docker image, no special installation is needed -- the docker will automatically download the image if not present. When running docker image, no special installation is needed -- the docker will automatically download the image if not present.
Just run ``docker run -it -v $(pwd):/data -p 8080:80 klokantech/tileserver-gl``. Just run ``docker run --rm -it -v $(pwd):/data -p 8080:80 maptiler/tileserver-gl``.
Additional options (see :doc:`/usage`) can be passed to the TileServer GL by appending them to the end of this command. You can, for example, do the following: Additional options (see :doc:`/usage`) can be passed to the TileServer GL by appending them to the end of this command. You can, for example, do the following:
* ``docker run ... klokantech/tileserver-gl my-tiles.mbtiles`` -- explicitly specify which mbtiles to use (if you have more in the folder) * ``docker run ... maptiler/tileserver-gl --mbtiles my-tiles.mbtiles`` -- explicitly specify which mbtiles to use (if you have more in the folder)
* ``docker run ... klokantech/tileserver-gl --verbose`` -- to see the default config created automatically * ``docker run ... maptiler/tileserver-gl --verbose`` -- to see the default config created automatically
npm npm
=== ===
@@ -41,7 +41,7 @@ Alternatively, you can use ``tileserver-gl-light`` package instead, which is pur
From source From source
=========== ===========
Make sure you have Node v4 (nvm install 4) and run:: Make sure you have Node v10 (nvm install 10) and run::
npm install npm install
node . node .

View File

@@ -4,25 +4,35 @@ Usage
Getting started Getting started
====== ======
:: ::
Usage: tileserver-gl [mbtiles] [options] Usage: main.js tileserver-gl [mbtiles] [options]
mbtiles MBTiles file (uses demo configuration); Options:
ignored if the configuration file is also specified
Options: -h, --help output usage information
-c, --config Configuration file [config.json] --mbtiles <file> MBTiles file (uses demo configuration);
-b, --bind Bind address ignored if the configuration file is also specified
-p, --port Port [8080] -c, --config <file> Configuration file [config.json]
-V, --verbose More verbose output -b, --bind <address> Bind address
-v, --version Version info -p, --port <port> Port [8080]
-C|--no-cors Disable Cross-origin resource sharing headers
-u|--public_url <url> Enable exposing the server on subpaths, not necessarily the root of the domain
-V, --verbose More verbose output
-s, --silent Less verbose output
-v, --version Version info
Default preview style and configuration
Default styles and configuration
====== ======
- If no configuration file is specified, the default styles (compatible with osm2vectortiles) are used. - 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), an extract is downloaded directly from osm2vectortiles. - 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 the configuration
======
It is possible to reload the configuration file without restarting the whole process by sending a SIGHUP signal to the node process.
- 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,52 +1,49 @@
{ {
"name": "tileserver-gl", "name": "tileserver-gl",
"version": "1.3.1", "version": "3.1.1",
"description": "Map tile server for JSON GL styles - vector and server side generated raster tiles", "description": "Map tile server for JSON GL styles - vector and server side generated raster tiles",
"main": "src/main.js", "main": "src/main.js",
"bin": "src/main.js", "bin": "src/main.js",
"authors": [
"Petr Sloup <petr.sloup@klokantech.com>"
],
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/klokantech/tileserver-gl.git" "url": "https://github.com/maptiler/tileserver-gl.git"
}, },
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"engines": { "engines": {
"node": ">=4.2.1 <5" "node": ">=10 <11"
}, },
"scripts": { "scripts": {
"test": "mocha test/**.js" "test": "mocha test/**.js --timeout 10000",
"docker": "docker build -f Dockerfile . && docker run --rm -i -p 8080:80 $(docker build -q .)"
}, },
"dependencies": { "dependencies": {
"async": "2.1.4", "@mapbox/glyph-pbf-composite": "0.0.3",
"advanced-pool": "0.3.2", "@mapbox/mapbox-gl-native": "5.0.2",
"base64url": "2.0.0", "@mapbox/mapbox-gl-style-spec": "13.12.0",
"canvas": "1.6.2", "@mapbox/mbtiles": "0.11.0",
"clone": "2.1.0", "@mapbox/sphericalmercator": "1.1.0",
"color": "1.0.3", "@mapbox/vector-tile": "1.3.1",
"cors": "2.8.1", "advanced-pool": "0.3.3",
"express": "4.14.0", "canvas": "2.6.1",
"glyph-pbf-composite": "0.0.2", "chokidar": "3.3.1",
"handlebars": "4.0.6", "clone": "2.1.2",
"mapbox-gl-native": "3.4.2", "color": "3.1.2",
"mbtiles": "0.9.0", "commander": "4.1.1",
"morgan": "1.7.0", "cors": "2.8.5",
"node-pngquant-native": "1.0.4", "esm": "3.2.25",
"nomnom": "1.8.1", "express": "4.17.1",
"pbf": "3.0.5", "handlebars": "4.7.3",
"request": "2.79.0", "http-shutdown": "1.2.2",
"sharp": "0.16.2", "morgan": "1.9.1",
"tileserver-gl-styles": "1.1.0", "pbf": "3.2.1",
"vector-tile": "1.3.0", "proj4": "2.6.0",
"@mapbox/sphericalmercator": "1.0.5" "request": "2.88.2",
}, "sharp": "0.26.2",
"optionalDependencies": { "tileserver-gl-styles": "2.0.0"
"tileshrink-gl": "./plugins/tileshrink-gl"
}, },
"devDependencies": { "devDependencies": {
"should": "^11.1.1", "mocha": "^7.1.0",
"mocha": "^3.2.0", "should": "^13.2.3",
"supertest": "^2.0.1" "supertest": "^4.0.2"
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 KiB

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 530 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="2034.203px" height="552.055px" viewBox="0 0 2034.203 552.055" enable-background="new 0 0 2034.203 552.055"
xml:space="preserve">
<g>
<path fill="#3A1888" d="M3.604-242.717"/>
</g>
<g>
<g>
<path fill="#3A1888" d="M152.645,436.647c25.674,25.668,94.015,95.335,93.983,95.406c-0.249,0.454,67.892-67.963,95.032-95.087
l-94.67-94.665L152.645,436.647z"/>
<path fill="#03A1C4" d="M246.99,342.301l94.67,94.665c0.141-0.157,0.314-0.336,0.466-0.477l94.578-94.583l-94.66-94.662
L246.99,342.301z"/>
<path fill="#05D0DF" d="M436.704,341.907l0.243-0.244c52.317-52.312,52.36-137.096,0.157-189.473l-95.06,95.055L436.704,341.907z"
/>
<path fill="#761FE8" d="M151.931,247.245l-94.329,94.326c0.027,0.032,0.043,0.064,0.076,0.092l94.811,94.827
c0.054,0.049,0.108,0.098,0.157,0.157l94.345-94.346L151.931,247.245z"/>
<path fill="#FFAA01" d="M246.99,152.184l95.054,95.061l95.06-95.055c-0.076-0.054-0.103-0.108-0.157-0.162l-94.821-94.816
c-0.022-0.027-0.054-0.054-0.082-0.081L246.99,152.184z"/>
<path fill="#F1175D" d="M57.201,152.514c-51.852,52.377-51.722,136.848,0.4,189.057l94.329-94.326L57.201,152.514z"/>
<path fill="#FB3A1B" d="M246.99,152.184L152.255,57.45l-94.578,94.578c-0.163,0.162-0.309,0.336-0.476,0.486l94.729,94.73
L246.99,152.184z"/>
<path fill="#FBC935" d="M342.044,57.13C289.663,4.846,204.832,4.874,152.488,57.211l-0.233,0.238l94.735,94.734L342.044,57.13z"/>
</g>
<g>
<path fill="#333359" d="M734.146,365.616v-96.875c0-23.851-12.479-45.492-37.077-45.492c-24.224,0-38.517,21.642-38.517,45.492
v96.875h-44.761V184.347h41.46l3.301,22.021c9.542-18.353,30.458-24.949,47.685-24.949c21.669,0,43.306,8.811,53.588,33.754
c16.144-25.685,37.066-33.022,60.537-33.022c51.38,0,76.692,31.551,76.692,85.859v97.605h-44.767V268.01
c0-23.84-9.904-44.037-34.106-44.037c-24.234,0-39.279,20.917-39.279,44.768v96.875H734.146z"/>
<path fill="#333359" d="M1086.026,184.726h42.938v180.89h-42.208l-2.208-26.41c-10.266,21.269-38.516,31.535-58.702,31.914
c-53.556,0.368-93.198-32.655-93.198-96.137c0-62.375,41.477-95.029,94.313-94.662c24.212,0,47.321,11.371,57.587,29.354
L1086.026,184.726z M977.416,274.983c0,34.479,23.85,55.039,53.573,55.039c70.446,0,70.446-109.713,0-109.713
C1001.266,220.309,977.416,240.496,977.416,274.983z"/>
<path fill="#333359" d="M1166.756,441.214V184.726h41.839l2.923,24.949c13.951-20.187,38.175-28.991,58.719-28.991
c55.753,0,92.835,41.471,92.835,94.667c0,52.847-33.401,94.675-91.374,94.675c-19.065,0-47.332-5.888-60.18-25.695v96.884
H1166.756z M1318.305,275.351c0-28.253-19.082-51.378-51.37-51.378c-32.298,0-51.38,23.125-51.38,51.378
c0,28.244,20.922,51.38,51.38,51.38C1297.404,326.731,1318.305,303.595,1318.305,275.351z"/>
<path fill="#333359" d="M1443.064,129.682v54.665h61.642v15.046h-61.642v110.453c0,24.575,5.146,41.823,33.392,41.823
c8.805,0,18.709-2.938,27.882-7.339l6.24,14.666c-11.382,5.521-22.763,9.185-34.122,9.185c-38.527,0-51.002-22.752-51.002-58.335
V199.393h-38.538v-15.046h38.538v-52.831L1443.064,129.682z"/>
<path fill="#333359" d="M1570.027,125.272c0,19.082-28.986,19.082-28.986,0C1541.041,106.2,1570.027,106.2,1570.027,125.272z
M1546.188,183.612v182.004h17.962V183.612H1546.188z"/>
<path fill="#333359" d="M1633.503,108.776v256.84h-17.983v-256.84H1633.503z"/>
<path fill="#333359" d="M1918.606,184.347l0.73,32.304c11.365-24.603,37.066-34.133,60.181-34.133
c13.589-0.367,26.772,3.307,38.896,10.646l-8.08,14.671c-9.525-5.871-20.187-8.441-30.815-8.441
c-33.771,0.379-59.817,27.524-59.817,60.553v105.67h-17.979V184.347H1918.606z"/>
</g>
<g>
<path fill="none" d="M1694.655,305.711c0.006,0.016,0.014,0.031,0.02,0.047l146.748-38.832c-0.007-0.055-0.012-0.11-0.018-0.166
L1694.655,305.711z"/>
<path fill="none" d="M1765.447,197.873c-42.255,0-76.514,34.997-76.514,78.169c0,4.196,0.333,8.312,0.956,12.329l147.452-39.137
C1826.633,219.268,1798.486,197.873,1765.447,197.873z"/>
<g>
<path fill="none" d="M1765.447,198.374c-42.255,0-76.514,34.996-76.514,78.169c0,4.196,0.333,8.312,0.956,12.329l147.452-39.137
C1826.633,219.768,1798.486,198.374,1765.447,198.374z"/>
<path fill="#333359" d="M1765.447,354.709c-31.946,0-59.308-20.014-70.764-48.431l-0.1,0.004l0.091-0.024
c-0.006-0.016-0.014-0.031-0.02-0.047l146.75-38.951c0.006,0.056,0.011,0.111,0.018,0.166l15.616-4.133
c-6.306-45.918-44.904-81.253-91.59-81.253c-51.089,0-92.501,42.31-92.501,94.5s41.412,94.501,92.501,94.501
c38.213,0,71.011-23.675,85.115-57.448l-14.717-6.398C1824.179,335.126,1797.054,354.709,1765.447,354.709z M1688.934,276.542
c0-43.173,34.259-78.169,76.514-78.169c33.039,0,61.186,21.395,71.895,51.361l-147.452,39.137
C1689.267,284.854,1688.934,280.739,1688.934,276.542z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -23,12 +23,15 @@ body{
font-family:'OpenSans', sans-serif, Arial; font-family:'OpenSans', sans-serif, Arial;
font-size: 14px; font-size: 14px;
margin:0; margin:0;
background-repeat:no-repeat !important;
background-size: contain !important;
background-image: url(/images/header-map-1280px.png);
} }
a{ a{
color: #499DCE; color: #499DCE;
transition: color .2s; transition: color .2s;
} }
a:hover{ a:hover {
color: #395D73; color: #395D73;
} }
.title { .title {
@@ -47,7 +50,7 @@ a:hover{
color: #499DCE; color: #499DCE;
font-size:.8em; font-size:.8em;
} }
section{ section {
margin: 15px auto; margin: 15px auto;
width: 930px; width: 930px;
padding: 30px 0; padding: 30px 0;
@@ -70,7 +73,7 @@ section{
font-size:20px; font-size:20px;
background:#fff; background:#fff;
} }
.item{ .item {
background:#fff; background:#fff;
height: 191px; height: 191px;
border: 1px solid #ededed; border: 1px solid #ededed;
@@ -79,8 +82,9 @@ section{
.item:nth-child(odd) { .item:nth-child(odd) {
background-color:#fbfbfb; background-color:#fbfbfb;
} }
.item img{ .item img {
position: absolute; position: absolute;
display: block;
margin: 30px; margin: 30px;
width: 128px; width: 128px;
height: 128px; height: 128px;
@@ -122,10 +126,10 @@ section{
text-decoration: none; text-decoration: none;
font-weight: bold; font-weight: bold;
} }
.btn:first-child:hover{ .btn:first-child:hover {
background: #395D73; background: #395D73;
} }
footer{ footer {
width:100%; width:100%;
border-top:1px solid #ededed; border-top:1px solid #ededed;
text-align:center; text-align:center;
@@ -133,9 +137,12 @@ footer{
padding-top:10px; padding-top:10px;
font-size:12px; font-size:12px;
} }
footer img{ footer img {
width: 118px; width: 113px;
height: 32px; height: 31px;
}
footer .t {
display:none;
} }
footer p { footer p {
margin-top:0; margin-top:0;
@@ -144,27 +151,9 @@ footer a {
color: #787878; color: #787878;
text-decoration: none; text-decoration: none;
} }
.details h3, .identifier {
/* body background image */ max-width: 550px;
body { word-break: break-all;
background-repeat:no-repeat !important;
background-size: contain !important;
background-image: url(/images/header-map-640px.png);
}
@media only screen and (min-width: 641px) {
body {
background-image: url(/images/header-map-1280px.png);
}
}
@media only screen and (min-width: 1281px) {
body {
background-image: url(/images/header-map-1600px.png);
}
}
@media only screen and (min-width: 1601px) {
body {
background-image: url(/images/header-map-2560px.png);
}
} }
/* Responsive */ /* Responsive */
@@ -182,31 +171,31 @@ body {
.title.light:after { .title.light:after {
font-size:.6em; font-size:.6em;
} }
.title img{ .title img {
width: 200px; width: 200px;
} }
.subtitle{ .subtitle {
font-size: 20px; font-size: 20px;
margin: 0 0 35px 0; margin: 0 0 35px 0;
} }
.item{ .item {
height: 245px; height: 245px;
} }
.viewers{ .viewers {
float: left; float: left;
text-align: left; text-align: left;
width: 100%; width: 100%;
margin-left: 30px; margin-left: 30px;
margin-top: 15px; margin-top: 15px;
} }
.viewers a{ .viewers a {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
} }
.btn{ .btn {
margin: 0 20px 0 0; margin: 0 20px 0 0;
} }
.btn:first-child{ .btn:first-child {
padding: 0 20px; padding: 0 20px;
} }
} }

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

View File

@@ -5,10 +5,10 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{name}} - TileServer GL</title> <title>{{name}} - TileServer GL</title>
{{#is_vector}} {{#is_vector}}
<link rel="stylesheet" type="text/css" href="/mapbox-gl.css{{&key_query}}" /> <link rel="stylesheet" type="text/css" href="{{public_url}}mapbox-gl.css{{&key_query}}" />
<link rel="stylesheet" type="text/css" href="/mapbox-gl-inspect.css{{&key_query}}" /> <link rel="stylesheet" type="text/css" href="{{public_url}}mapbox-gl-inspect.css{{&key_query}}" />
<script src="/mapbox-gl.js{{&key_query}}"></script> <script src="{{public_url}}mapbox-gl.js{{&key_query}}"></script>
<script src="/mapbox-gl-inspect.min.js{{&key_query}}"></script> <script src="{{public_url}}mapbox-gl-inspect.min.js{{&key_query}}"></script>
<style> <style>
body {background:#fff;color:#333;font-family:Arial, sans-serif;} body {background:#fff;color:#333;font-family:Arial, sans-serif;}
#map {position:absolute;top:0;left:0;right:250px;bottom:0;} #map {position:absolute;top:0;left:0;right:250px;bottom:0;}
@@ -18,9 +18,9 @@
</style> </style>
{{/is_vector}} {{/is_vector}}
{{^is_vector}} {{^is_vector}}
<link rel="stylesheet" type="text/css" href="/mapbox.css{{&key_query}}" /> <link rel="stylesheet" type="text/css" href="{{public_url}}mapbox.css{{&key_query}}" />
<script src="/mapbox.js{{&key_query}}"></script> <script src="{{public_url}}mapbox.js{{&key_query}}"></script>
<script src="/leaflet-hash.js{{&key_query}}"></script> <script src="{{public_url}}leaflet-hash.js{{&key_query}}"></script>
<style> <style>
body { margin:0; padding:0; } body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; } #map { position:absolute; top:0; bottom:0; width:100%; }
@@ -42,7 +42,7 @@
sources: { sources: {
'vector_layer_': { 'vector_layer_': {
type: 'vector', type: 'vector',
url: '/data/{{id}}.json{{&key_query}}' url: '{{public_url}}data/{{id}}.json{{&key_query}}'
} }
}, },
layers: [] layers: []
@@ -74,7 +74,7 @@
<h1 style="display:none;">{{name}}</h1> <h1 style="display:none;">{{name}}</h1>
<div id='map'></div> <div id='map'></div>
<script> <script>
var map = L.mapbox.map('map', '/data/{{id}}.json{{&key_query}}', { zoomControl: false }); var map = L.mapbox.map('map', '{{public_url}}data/{{id}}.json{{&key_query}}', { zoomControl: false });
map.eachLayer(function(layer) { map.eachLayer(function(layer) {
// do not add scale prefix even if retina display is detected // do not add scale prefix even if retina display is detected
layer.scalePrefix = '.'; layer.scalePrefix = '.';

View File

@@ -4,7 +4,7 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>TileServer GL - Server for vector and raster maps with GL styles</title> <title>TileServer GL - Server for vector and raster maps with GL styles</title>
<link rel="stylesheet" type="text/css" href="/index.css{{&key_query}}" /> <link rel="stylesheet" type="text/css" href="{{public_url}}index.css{{&key_query}}" />
<script> <script>
function toggle_xyz(id) { function toggle_xyz(id) {
var el = document.getElementById(id); var el = document.getElementById(id);
@@ -17,93 +17,97 @@
</head> </head>
<body> <body>
<section> <section>
<h1 class="title {{#if is_light}}light{{/if}}"><img src="/images/logo.png" alt="TileServer GL" /></h1> <h1 class="title {{#if is_light}}light{{/if}}"><img src="{{public_url}}images/logo.png" alt="TileServer GL" /></h1>
<h2 class="subtitle">Vector {{#if is_light}}<s>and raster</s>{{else}}and raster{{/if}} maps with GL styles</h2> <h2 class="subtitle">Vector {{#if is_light}}<s>and raster</s>{{else}}and raster{{/if}} maps with GL styles</h2>
<h2 class="box-header">Styles</h2> {{#if styles}}
<div class="box"> <h2 class="box-header">Styles</h2>
{{#each styles}} <div class="box">
<div class="item"> {{#each styles}}
{{#if thumbnail}} <div class="item">
<img src="/styles/{{@key}}/rendered/{{thumbnail}}{{&../key_query}}" alt="{{name}} preview" /> {{#if thumbnail}}
{{else}} <img src="{{public_url}}styles/{{@key}}/{{thumbnail}}{{&../key_query}}" alt="{{name}} preview" />
<img src="/images/placeholder.png" alt="{{name}} preview" /> {{else}}
{{/if}} <img src="{{public_url}}images/placeholder.png" alt="{{name}} preview" />
<div class="details"> {{/if}}
<h3>{{name}}</h3> <div class="details">
<p class="identifier">identifier: {{@key}}</p> <h3>{{name}}</h3>
<p class="services"> <p class="identifier">identifier: {{@key}}</p>
services: <p class="services">
services:
{{#if serving_data}}
<a href="{{public_url}}styles/{{@key}}/style.json{{&../key_query}}">GL Style</a>
{{/if}}
{{#if serving_rendered}}
{{#if serving_data}}| {{/if}}<a href="{{public_url}}styles/{{@key}}.json{{&../key_query}}">TileJSON</a>
{{/if}}
{{#if serving_rendered}}
| <a href="/styles/{{@key}}/wmts.xml{{&../key_query}}">WMTS</a>
{{/if}}
{{#if xyz_link}}
| <a href="#" onclick="return toggle_xyz('xyz_style_{{@key}}');">XYZ</a>
<input id="xyz_style_{{@key}}" type="text" value="{{&xyz_link}}" style="display:none;" />
{{/if}}
</p>
</div>
<div class="viewers">
{{#if serving_data}} {{#if serving_data}}
<a href="/styles/{{@key}}.json{{&../key_query}}">GL Style</a> {{#if serving_rendered}}
<a class="btn" href="{{public_url}}styles/{{@key}}/{{&../key_query}}{{viewer_hash}}">Viewer</a>
{{/if}}
{{/if}} {{/if}}
{{#if serving_rendered}} {{#if serving_rendered}}
{{#if serving_data}}| {{/if}}<a href="/styles/{{@key}}/rendered.json{{&../key_query}}">TileJSON</a> <a class="btn" href="{{public_url}}styles/{{@key}}/?{{&../key_query_part}}raster{{viewer_hash}}">Raster</a>
{{/if}} {{/if}}
{{#if wmts_link}} {{#if serving_data}}
| <a href="{{&wmts_link}}">WMTS</a> <a class="btn" href="{{public_url}}styles/{{@key}}/?{{&../key_query_part}}vector{{viewer_hash}}">Vector</a>
{{/if}} {{/if}}
{{#if xyz_link}} </div>
| <a href="#" onclick="return toggle_xyz('xyz_style_{{@key}}');">XYZ</a>
<input id="xyz_style_{{@key}}" type="text" value="{{&xyz_link}}" style="display:none;" />
{{/if}}
</p>
</div>
<div class="viewers">
{{#if serving_data}}
{{#if serving_rendered}}
<a class="btn" href="/styles/{{@key}}/{{&../key_query}}{{viewer_hash}}">Viewer</a>
{{/if}}
{{/if}}
{{#if serving_rendered}}
<a class="btn" href="/styles/{{@key}}/?{{&../key_query_part}}raster{{viewer_hash}}">Raster</a>
{{/if}}
{{#if serving_data}}
<a class="btn" href="/styles/{{@key}}/?{{&../key_query_part}}vector{{viewer_hash}}">Vector</a>
{{/if}}
</div> </div>
{{/each}}
</div> </div>
{{/each}} {{/if}}
</div> {{#if data}}
<h2 class="box-header">Data</h2> <h2 class="box-header">Data</h2>
<div class="box"> <div class="box">
{{#each data}} {{#each data}}
<div class="item"> <div class="item">
{{#if thumbnail}} {{#if thumbnail}}
<img src="/data/{{@key}}/{{thumbnail}}{{&../key_query}}" alt="{{name}} preview" /> <img src="{{public_url}}data/{{@key}}/{{thumbnail}}{{&../key_query}}" alt="{{name}} preview" />
{{else}} {{else}}
<img src="/images/placeholder.png" alt="{{name}} preview" /> <img src="{{public_url}}images/placeholder.png" alt="{{name}} preview" />
{{/if}} {{/if}}
<div class="details"> <div class="details">
<h3>{{name}}</h3> <h3>{{name}}</h3>
<p class="identifier">identifier: {{@key}}{{#if formatted_filesize}} | size: {{formatted_filesize}}{{/if}} | type: {{#is_vector}}vector{{/is_vector}}{{^is_vector}}raster{{/is_vector}} data</p> <p class="identifier">identifier: {{@key}}{{#if formatted_filesize}} | size: {{formatted_filesize}}{{/if}} | type: {{#is_vector}}vector{{/is_vector}}{{^is_vector}}raster{{/is_vector}} data</p>
<p class="services"> <p class="services">
services: <a href="/data/{{@key}}.json{{&../key_query}}">TileJSON</a> services: <a href="{{public_url}}data/{{@key}}.json{{&../key_query}}">TileJSON</a>
{{#if wmts_link}} {{#if wmts_link}}
| <a href="{{&wmts_link}}">WMTS</a> | <a href="{{&wmts_link}}">WMTS</a>
{{/if}} {{/if}}
{{#if xyz_link}} {{#if xyz_link}}
| <a href="#" onclick="return toggle_xyz('xyz_data_{{@key}}');">XYZ</a> | <a href="#" onclick="return toggle_xyz('xyz_data_{{@key}}');">XYZ</a>
<input id="xyz_data_{{@key}}" type="text" value="{{&xyz_link}}" style="display:none;" /> <input id="xyz_data_{{@key}}" type="text" value="{{&xyz_link}}" style="display:none;" />
{{/if}} {{/if}}
</p> </p>
</div> </div>
<div class="viewers"> <div class="viewers">
{{#is_vector}} {{#is_vector}}
<a class="btn" href="/data/{{@key}}/{{&../key_query}}{{viewer_hash}}">Inspect</a> <a class="btn" href="{{public_url}}data/{{@key}}/{{&../key_query}}{{viewer_hash}}">Inspect</a>
{{/is_vector}} {{/is_vector}}
{{^is_vector}} {{^is_vector}}
<a class="btn" href="/data/{{@key}}/{{&../key_query}}{{viewer_hash}}">View</a> <a class="btn" href="{{public_url}}data/{{@key}}/{{&../key_query}}{{viewer_hash}}">View</a>
{{/is_vector}} {{/is_vector}}
</div>
</div> </div>
{{/each}}
</div> </div>
{{/each}} {{/if}}
</div>
</section> </section>
<footer> <footer>
<a href="https://www.klokantech.com/" target="_blank"><img src="/images/klokantech.png" /></a> <a href="https://www.maptiler.com/" target="_blank"><img src="{{public_url}}images/maptiler-logo.svg" /></a>
<p> <p>
<a href="https://github.com/klokantech/tileserver-gl" target="_blank">Powered by TileServer GL ({{server_version}})</a> <a href="https://www.klokantech.com/" target="_blank">open-source project from Klokan Technologies GmbH.</a> <a href="https://github.com/maptiler/tileserver-gl" target="_blank">Powered by TileServer GL ({{server_version}})</a> <a href="https://www.maptiler.com/" target="_blank">an open-source project from MapTiler.</a>
</p> </p>
</footer> </footer>
</body> </body>
</html> </html>

View File

@@ -4,11 +4,11 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{name}} - TileServer GL</title> <title>{{name}} - TileServer GL</title>
<link rel="stylesheet" type="text/css" href="/mapbox-gl.css{{&key_query}}" /> <link rel="stylesheet" type="text/css" href="{{public_url}}mapbox-gl.css{{&key_query}}" />
<script src="/mapbox-gl.js{{&key_query}}"></script> <script src="{{public_url}}mapbox-gl.js{{&key_query}}"></script>
<link rel="stylesheet" type="text/css" href="/mapbox.css{{&key_query}}" /> <link rel="stylesheet" type="text/css" href="{{public_url}}mapbox.css{{&key_query}}" />
<script src="/mapbox.js{{&key_query}}"></script> <script src="{{public_url}}mapbox.js{{&key_query}}"></script>
<script src="/leaflet-hash.js{{&key_query}}"></script> <script src="{{public_url}}leaflet-hash.js{{&key_query}}"></script>
<style> <style>
body { margin:0; padding:0; } body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; } #map { position:absolute; top:0; bottom:0; width:100%; }
@@ -24,14 +24,15 @@
(q.indexOf('raster') >= 0 ? 'raster' : (q.indexOf('raster') >= 0 ? 'raster' :
(mapboxgl.supported() ? 'vector' : 'raster')); (mapboxgl.supported() ? 'vector' : 'raster'));
if (preference == 'vector') { if (preference == 'vector') {
mapboxgl.setRTLTextPlugin('{{public_url}}mapbox-gl-rtl-text.js{{&key_query}}');
var map = new mapboxgl.Map({ var map = new mapboxgl.Map({
container: 'map', container: 'map',
style: '/styles/{{id}}.json{{&key_query}}', style: '{{public_url}}styles/{{id}}/style.json{{&key_query}}',
hash: true hash: true
}); });
map.addControl(new mapboxgl.NavigationControl()); map.addControl(new mapboxgl.NavigationControl());
} else { } else {
var map = L.mapbox.map('map', '/styles/{{id}}/rendered.json{{&key_query}}', { zoomControl: false }); var map = L.mapbox.map('map', '{{public_url}}styles/{{id}}.json{{&key_query}}', { zoomControl: false });
new L.Control.Zoom({ position: 'topright' }).addTo(map); new L.Control.Zoom({ position: 'topright' }).addTo(map);
setTimeout(function() { setTimeout(function() {
new L.Hash(map); new L.Hash(map);

407
public/templates/wmts.tmpl Normal file
View File

@@ -0,0 +1,407 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Capabilities xmlns="http://www.opengis.net/wmts/1.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:gml="http://www.opengis.net/gml" xsi:schemaLocation="http://www.opengis.net/wmts/1.0 http://schemas.opengis.net/wmts/1.0/wmtsGetCapabilities_response.xsd" version="1.0.0">
<!-- Service Identification -->
<ows:ServiceIdentification>
<ows:Title>TileServer GL</ows:Title>
<ows:ServiceType>OGC WMTS</ows:ServiceType>
<ows:ServiceTypeVersion>1.0.0</ows:ServiceTypeVersion>
</ows:ServiceIdentification>
<!-- Operations Metadata -->
<ows:OperationsMetadata>
<ows:Operation name="GetCapabilities">
<ows:DCP>
<ows:HTTP>
<ows:Get xlink:href="{{baseUrl}}/wmts/{{id}}/">
<ows:Constraint name="GetEncoding">
<ows:AllowedValues>
<ows:Value>RESTful</ows:Value>
</ows:AllowedValues>
</ows:Constraint>
</ows:Get>
</ows:HTTP>
</ows:DCP>
</ows:Operation>
<ows:Operation name="GetTile">
<ows:DCP>
<ows:HTTP>
<ows:Get xlink:href="{{baseUrl}}/styles/">
<ows:Constraint name="GetEncoding">
<ows:AllowedValues>
<ows:Value>RESTful</ows:Value>
</ows:AllowedValues>
</ows:Constraint>
</ows:Get>
</ows:HTTP>
</ows:DCP>
</ows:Operation>
</ows:OperationsMetadata>
<Contents>
<Layer>
<ows:Title>{{name}}</ows:Title>
<ows:Identifier>{{id}}</ows:Identifier>
<ows:WGS84BoundingBox crs="urn:ogc:def:crs:OGC:2:84">
<ows:LowerCorner>-180 -85.051128779807</ows:LowerCorner>
<ows:UpperCorner>180 85.051128779807</ows:UpperCorner>
</ows:WGS84BoundingBox>
<Style isDefault="true">
<ows:Identifier>default</ows:Identifier>
</Style>
<Format>image/png</Format>
<TileMatrixSetLink>
<TileMatrixSet>GoogleMapsCompatible</TileMatrixSet>
</TileMatrixSetLink>
<ResourceURL format="image/png" resourceType="tile" template="{{baseUrl}}/styles/{{id}}/{TileMatrix}/{TileCol}/{TileRow}.png{{key_query}}"/>
</Layer><TileMatrixSet>
<ows:Title>GoogleMapsCompatible</ows:Title>
<ows:Abstract>GoogleMapsCompatible EPSG:3857</ows:Abstract>
<ows:Identifier>GoogleMapsCompatible</ows:Identifier>
<ows:SupportedCRS>urn:ogc:def:crs:EPSG::3857</ows:SupportedCRS>
<TileMatrix>
<ows:Identifier>0</ows:Identifier>
<ScaleDenominator>559082264.02872</ScaleDenominator>
<TopLeftCorner>-20037508.34 20037508.34</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>1</MatrixWidth>
<MatrixHeight>1</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>1</ows:Identifier>
<ScaleDenominator>279541132.01436</ScaleDenominator>
<TopLeftCorner>-20037508.34 20037508.34</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>2</MatrixWidth>
<MatrixHeight>2</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>2</ows:Identifier>
<ScaleDenominator>139770566.00718</ScaleDenominator>
<TopLeftCorner>-20037508.34 20037508.34</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>4</MatrixWidth>
<MatrixHeight>4</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>3</ows:Identifier>
<ScaleDenominator>69885283.00359</ScaleDenominator>
<TopLeftCorner>-20037508.34 20037508.34</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>8</MatrixWidth>
<MatrixHeight>8</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>4</ows:Identifier>
<ScaleDenominator>34942641.501795</ScaleDenominator>
<TopLeftCorner>-20037508.34 20037508.34</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>16</MatrixWidth>
<MatrixHeight>16</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>5</ows:Identifier>
<ScaleDenominator>17471320.750897</ScaleDenominator>
<TopLeftCorner>-20037508.34 20037508.34</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>32</MatrixWidth>
<MatrixHeight>32</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>6</ows:Identifier>
<ScaleDenominator>8735660.3754487</ScaleDenominator>
<TopLeftCorner>-20037508.34 20037508.34</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>64</MatrixWidth>
<MatrixHeight>64</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>7</ows:Identifier>
<ScaleDenominator>4367830.1877244</ScaleDenominator>
<TopLeftCorner>-20037508.34 20037508.34</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>128</MatrixWidth>
<MatrixHeight>128</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>8</ows:Identifier>
<ScaleDenominator>2183915.0938622</ScaleDenominator>
<TopLeftCorner>-20037508.34 20037508.34</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>256</MatrixWidth>
<MatrixHeight>256</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>9</ows:Identifier>
<ScaleDenominator>1091957.5469311</ScaleDenominator>
<TopLeftCorner>-20037508.34 20037508.34</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>512</MatrixWidth>
<MatrixHeight>512</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>10</ows:Identifier>
<ScaleDenominator>545978.77346554</ScaleDenominator>
<TopLeftCorner>-20037508.34 20037508.34</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>1024</MatrixWidth>
<MatrixHeight>1024</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>11</ows:Identifier>
<ScaleDenominator>272989.38673277</ScaleDenominator>
<TopLeftCorner>-20037508.34 20037508.34</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>2048</MatrixWidth>
<MatrixHeight>2048</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>12</ows:Identifier>
<ScaleDenominator>136494.69336639</ScaleDenominator>
<TopLeftCorner>-20037508.34 20037508.34</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>4096</MatrixWidth>
<MatrixHeight>4096</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>13</ows:Identifier>
<ScaleDenominator>68247.346683193</ScaleDenominator>
<TopLeftCorner>-20037508.34 20037508.34</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>8192</MatrixWidth>
<MatrixHeight>8192</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>14</ows:Identifier>
<ScaleDenominator>34123.673341597</ScaleDenominator>
<TopLeftCorner>-20037508.34 20037508.34</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>16384</MatrixWidth>
<MatrixHeight>16384</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>15</ows:Identifier>
<ScaleDenominator>17061.836670798</ScaleDenominator>
<TopLeftCorner>-20037508.34 20037508.34</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>32768</MatrixWidth>
<MatrixHeight>32768</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>16</ows:Identifier>
<ScaleDenominator>8530.9183353991</ScaleDenominator>
<TopLeftCorner>-20037508.34 20037508.34</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>65536</MatrixWidth>
<MatrixHeight>65536</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>17</ows:Identifier>
<ScaleDenominator>4265.4591676996</ScaleDenominator>
<TopLeftCorner>-20037508.34 20037508.34</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>131072</MatrixWidth>
<MatrixHeight>131072</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>18</ows:Identifier>
<ScaleDenominator>2132.7295838498</ScaleDenominator>
<TopLeftCorner>-20037508.34 20037508.34</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>262144</MatrixWidth>
<MatrixHeight>262144</MatrixHeight>
</TileMatrix></TileMatrixSet><TileMatrixSet>
<ows:Title>WGS84</ows:Title>
<ows:Abstract>WGS84 EPSG:4326</ows:Abstract>
<ows:Identifier>WGS84</ows:Identifier>
<ows:SupportedCRS>urn:ogc:def:crs:EPSG::4326</ows:SupportedCRS>
<TileMatrix>
<ows:Identifier>0</ows:Identifier>
<ScaleDenominator>279541132.01436</ScaleDenominator>
<TopLeftCorner>90 -180</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>2</MatrixWidth>
<MatrixHeight>1</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>1</ows:Identifier>
<ScaleDenominator>139770566.00718</ScaleDenominator>
<TopLeftCorner>90 -180</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>4</MatrixWidth>
<MatrixHeight>2</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>2</ows:Identifier>
<ScaleDenominator>69885283.00359</ScaleDenominator>
<TopLeftCorner>90 -180</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>8</MatrixWidth>
<MatrixHeight>4</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>3</ows:Identifier>
<ScaleDenominator>34942641.501795</ScaleDenominator>
<TopLeftCorner>90 -180</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>16</MatrixWidth>
<MatrixHeight>8</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>4</ows:Identifier>
<ScaleDenominator>17471320.750897</ScaleDenominator>
<TopLeftCorner>90 -180</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>32</MatrixWidth>
<MatrixHeight>16</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>5</ows:Identifier>
<ScaleDenominator>8735660.3754487</ScaleDenominator>
<TopLeftCorner>90 -180</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>64</MatrixWidth>
<MatrixHeight>32</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>6</ows:Identifier>
<ScaleDenominator>4367830.1877244</ScaleDenominator>
<TopLeftCorner>90 -180</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>128</MatrixWidth>
<MatrixHeight>64</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>7</ows:Identifier>
<ScaleDenominator>2183915.0938622</ScaleDenominator>
<TopLeftCorner>90 -180</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>256</MatrixWidth>
<MatrixHeight>128</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>8</ows:Identifier>
<ScaleDenominator>1091957.5469311</ScaleDenominator>
<TopLeftCorner>90 -180</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>512</MatrixWidth>
<MatrixHeight>256</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>9</ows:Identifier>
<ScaleDenominator>545978.77346554</ScaleDenominator>
<TopLeftCorner>90 -180</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>1024</MatrixWidth>
<MatrixHeight>512</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>10</ows:Identifier>
<ScaleDenominator>272989.38673277</ScaleDenominator>
<TopLeftCorner>90 -180</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>2048</MatrixWidth>
<MatrixHeight>1024</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>11</ows:Identifier>
<ScaleDenominator>136494.69336639</ScaleDenominator>
<TopLeftCorner>90 -180</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>4096</MatrixWidth>
<MatrixHeight>2048</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>12</ows:Identifier>
<ScaleDenominator>68247.346683193</ScaleDenominator>
<TopLeftCorner>90 -180</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>8192</MatrixWidth>
<MatrixHeight>4096</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>13</ows:Identifier>
<ScaleDenominator>34123.673341597</ScaleDenominator>
<TopLeftCorner>90 -180</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>16384</MatrixWidth>
<MatrixHeight>8192</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>14</ows:Identifier>
<ScaleDenominator>17061.836670798</ScaleDenominator>
<TopLeftCorner>90 -180</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>32768</MatrixWidth>
<MatrixHeight>16384</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>15</ows:Identifier>
<ScaleDenominator>8530.9183353991</ScaleDenominator>
<TopLeftCorner>90 -180</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>65536</MatrixWidth>
<MatrixHeight>32768</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>16</ows:Identifier>
<ScaleDenominator>4265.4591676996</ScaleDenominator>
<TopLeftCorner>90 -180</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>131072</MatrixWidth>
<MatrixHeight>65536</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>17</ows:Identifier>
<ScaleDenominator>2132.7295838498</ScaleDenominator>
<TopLeftCorner>90 -180</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>262144</MatrixWidth>
<MatrixHeight>131072</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>18</ows:Identifier>
<ScaleDenominator>1066.3647919249</ScaleDenominator>
<TopLeftCorner>90 -180</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>524288</MatrixWidth>
<MatrixHeight>262144</MatrixHeight>
</TileMatrix></TileMatrixSet>
</Contents>
<ServiceMetadataURL xlink:href="{{baseUrl}}/wmts/{{id}}/"/>
</Capabilities>

View File

@@ -22,18 +22,24 @@ var packageJson = require('./package');
packageJson.name += '-light'; packageJson.name += '-light';
packageJson.description = 'Map tile server for JSON GL styles - serving vector tiles'; packageJson.description = 'Map tile server for JSON GL styles - serving vector tiles';
delete packageJson.dependencies['canvas']; delete packageJson.dependencies['canvas'];
delete packageJson.dependencies['mapbox-gl-native']; delete packageJson.dependencies['@mapbox/mapbox-gl-native'];
delete packageJson.dependencies['node-pngquant-native'];
delete packageJson.dependencies['sharp']; delete packageJson.dependencies['sharp'];
delete packageJson.optionalDependencies; delete packageJson.optionalDependencies;
delete packageJson.devDependencies; delete packageJson.devDependencies;
packageJson.engines.node = '>= 10';
var str = JSON.stringify(packageJson, undefined, 2); var str = JSON.stringify(packageJson, undefined, 2);
fs.writeFileSync('light/package.json', str); fs.writeFileSync('light/package.json', str);
fs.renameSync('light/README_light.md', 'light/README.md'); fs.renameSync('light/README_light.md', 'light/README.md');
fs.renameSync('light/Dockerfile_light', 'light/Dockerfile'); fs.renameSync('light/Dockerfile_light', 'light/Dockerfile');
// for Build tileserver-gl-light docker image, don't publish
if (process.argv.length > 2 && process.argv[2] == "--no-publish") {
process.exit(0)
}
/* PUBLISH */ /* PUBLISH */
// tileserver-gl // tileserver-gl

36
run.sh
View File

@@ -1,3 +1,37 @@
#!/bin/bash #!/bin/bash
_term() {
echo "Caught signal, stopping gracefully"
kill -TERM "$child" 2>/dev/null
}
trap _term SIGTERM
trap _term SIGINT
xvfbMaxStartWaitTime=60
displayNumber=99
screenNumber=0
# Delete files if they were not cleaned by last run
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 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."
exit 1
fi
export DISPLAY=:${displayNumber}.${screenNumber}
echo
cd /data cd /data
xvfb-run -a -e /dev/stdout --server-args="-screen 0 1024x768x24" node /usr/src/app/ -p 80 "$@" node /usr/src/app/ -p 80 "$@" &
child=$!
wait "$child"
start-stop-daemon --stop --retry 5 --pidfile ~/xvfb.pid # stop xvfb when exiting
rm ~/xvfb.pid

View File

@@ -2,79 +2,125 @@
'use strict'; 'use strict';
var fs = require('fs'), require = require('esm')(module);
path = require('path'),
request = require('request');
var mbtiles = require('mbtiles'); const fs = require('fs');
const path = require('path');
const request = require('request');
var packageJson = require('../package'); const MBTiles = require('@mapbox/mbtiles');
var opts = require('nomnom') const packageJson = require('../package');
.option('mbtiles', {
default: undefined,
help: 'MBTiles file (uses demo configuration);\n' +
'\t ignored if the configuration file is also specified',
position: 0
})
.option('config', {
abbr: 'c',
default: 'config.json',
help: 'Configuration file'
})
.option('bind', {
abbr: 'b',
default: undefined,
help: 'Bind address'
})
.option('port', {
abbr: 'p',
default: 8080,
help: 'Port'
})
.option('verbose', {
abbr: 'V',
flag: true,
help: 'More verbose output'
})
.option('version', {
abbr: 'v',
flag: true,
help: 'Version info',
callback: function() {
return packageJson.name + ' v' + packageJson.version;
}
}).parse();
const args = process.argv;
if (args.length >= 3 && args[2][0] !== '-') {
args.splice(2, 0, '--mbtiles');
}
console.log('Starting ' + packageJson.name + ' v' + packageJson.version); const opts = require('commander')
.description('tileserver-gl startup options')
.usage('tileserver-gl [mbtiles] [options]')
.option(
'--mbtiles <file>',
'MBTiles file (uses demo configuration);\n' +
'\t ignored if the configuration file is also specified'
)
.option(
'-c, --config <file>',
'Configuration file [config.json]',
'config.json'
)
.option(
'-b, --bind <address>',
'Bind address'
)
.option(
'-p, --port <port>',
'Port [8080]',
8080,
parseInt
)
.option(
'-C|--no-cors',
'Disable Cross-origin resource sharing headers'
)
.option(
'-u|--public_url <url>',
'Enable exposing the server on subpaths, not necessarily the root of the domain'
)
.option(
'-V, --verbose',
'More verbose output'
)
.option(
'-s, --silent',
'Less verbose output'
)
.option(
'-l|--log_file <file>',
'output log file (defaults to standard out)'
)
.option(
'-f|--log_format <format>',
'define the log format: https://github.com/expressjs/morgan#morganformat-options'
)
.version(
packageJson.version,
'-v, --version'
)
.parse(args);
var startServer = function(configPath, config) { console.log(`Starting ${packageJson.name} v${packageJson.version}`);
const startServer = (configPath, config) => {
let publicUrl = opts.public_url;
if (publicUrl && publicUrl.lastIndexOf('/') !== publicUrl.length - 1) {
publicUrl += '/';
}
return require('./server')({ return require('./server')({
configPath: configPath, configPath: configPath,
config: config, config: config,
bind: opts.bind, bind: opts.bind,
port: opts.port port: opts.port,
cors: opts.cors,
verbose: opts.verbose,
silent: opts.silent,
logFile: opts.log_file,
logFormat: opts.log_format,
publicUrl: publicUrl
}); });
}; };
var startWithMBTiles = function(mbtilesFile) { const startWithMBTiles = (mbtilesFile) => {
console.log('Automatically creating config file for ' + mbtilesFile); console.log(`[INFO] Automatically creating config file for ${mbtilesFile}`);
console.log(`[INFO] Only a basic preview style will be used.`);
console.log(`[INFO] See documentation to learn how to create config.json file.`);
mbtilesFile = path.resolve(process.cwd(), mbtilesFile); mbtilesFile = path.resolve(process.cwd(), mbtilesFile);
var mbtilesStats = fs.statSync(mbtilesFile); const mbtilesStats = fs.statSync(mbtilesFile);
if (!mbtilesStats.isFile() || mbtilesStats.size === 0) { if (!mbtilesStats.isFile() || mbtilesStats.size === 0) {
console.log('ERROR: Not valid MBTiles file: ' + mbtilesFile); console.log(`ERROR: Not valid MBTiles file: ${mbtilesFile}`);
process.exit(1); process.exit(1);
} }
var instance = new mbtiles(mbtilesFile, function(err) { const instance = new MBTiles(mbtilesFile, (err) => {
instance.getInfo(function(err, info) { if (err) {
var bounds = info.bounds; console.log('ERROR: Unable to open MBTiles.');
console.log(` Make sure ${path.basename(mbtilesFile)} is valid MBTiles.`);
process.exit(1);
}
var styleDir = path.resolve(__dirname, "../node_modules/tileserver-gl-styles/"); instance.getInfo((err, info) => {
if (err || !info) {
console.log('ERROR: Metadata missing in the MBTiles.');
console.log(` Make sure ${path.basename(mbtilesFile)} is valid MBTiles.`);
process.exit(1);
}
const bounds = info.bounds;
var config = { const styleDir = path.resolve(__dirname, "../node_modules/tileserver-gl-styles/");
const config = {
"options": { "options": {
"paths": { "paths": {
"root": styleDir, "root": styleDir,
@@ -87,58 +133,32 @@ var startWithMBTiles = function(mbtilesFile) {
"data": {} "data": {}
}; };
if (info.format == 'pbf' && if (info.format === 'pbf' &&
info.name.toLowerCase().indexOf('openmaptiles') > -1) { info.name.toLowerCase().indexOf('openmaptiles') > -1) {
config['data']['openmaptiles'] = {
config['data'][`v3`] = {
"mbtiles": path.basename(mbtilesFile) "mbtiles": path.basename(mbtilesFile)
}; };
var omtV = (info.version || '').split('.');
var styles = fs.readdirSync(path.resolve(styleDir, 'styles')); const styles = fs.readdirSync(path.resolve(styleDir, 'styles'));
for (var i = 0; i < styles.length; i++) { for (let styleName of styles) {
var styleName = styles[i]; const styleFileRel = styleName + '/style.json';
var styleFileRel = styleName + '/style.json'; const styleFile = path.resolve(styleDir, 'styles', styleFileRel);
var styleFile = path.resolve(styleDir, 'styles', styleFileRel);
if (fs.existsSync(styleFile)) { if (fs.existsSync(styleFile)) {
var styleJSON = require(styleFile); config['styles'][styleName] = {
var omtVersionCompatibility = "style": styleFileRel,
((styleJSON || {}).metadata || {})['openmaptiles:version'] || 'x'; "tilejson": {
var m = omtVersionCompatibility.toLowerCase().split('.'); "bounds": bounds
}
var isCompatible = !( };
m[0] != 'x' && (
m[0] != omtV[0] || (
(m[1] || 'x') != 'x' && (
m[1] != omtV[1] || (
(m[2] || 'x') != 'x' &&
m[2] != omtV[2]
)
)
)
)
);
if (isCompatible) {
var styleObject = {
"style": styleFileRel,
"tilejson": {
"bounds": bounds
}
};
config['styles'][styleName] = styleObject;
} else {
console.log('Style', styleName, 'requires OpenMapTiles version',
omtVersionCompatibility, 'but mbtiles is version', info.version);
}
} }
} }
} else { } else {
console.log('WARN: MBTiles not in "openmaptiles" format. ' + console.log(`WARN: MBTiles not in "openmaptiles" format. Serving raw data only...`);
'Serving raw data only...');
config['data'][(info.id || 'mbtiles') config['data'][(info.id || 'mbtiles')
.replace(/\//g, '_') .replace(/\//g, '_')
.replace(/\:/g, '_') .replace(/:/g, '_')
.replace(/\?/g, '_')] = { .replace(/\?/g, '_')] = {
"mbtiles": path.basename(mbtilesFile) "mbtiles": path.basename(mbtilesFile)
}; };
@@ -155,16 +175,15 @@ var startWithMBTiles = function(mbtilesFile) {
}); });
}; };
fs.stat(path.resolve(opts.config), function(err, stats) { fs.stat(path.resolve(opts.config), (err, stats) => {
if (err || !stats.isFile() || stats.size === 0) { if (err || !stats.isFile() || stats.size === 0) {
var mbtiles = opts.mbtiles; let mbtiles = opts.mbtiles;
if (!mbtiles) { if (!mbtiles) {
// try to find in the cwd // try to find in the cwd
var files = fs.readdirSync(process.cwd()); const files = fs.readdirSync(process.cwd());
for (var i=0; i < files.length; i++) { for (let filename of files) {
var filename = files[i];
if (filename.endsWith('.mbtiles')) { if (filename.endsWith('.mbtiles')) {
var mbTilesStats = fs.statSync(filename); const mbTilesStats = fs.statSync(filename);
if (mbTilesStats.isFile() && mbTilesStats.size > 0) { if (mbTilesStats.isFile() && mbTilesStats.size > 0) {
mbtiles = filename; mbtiles = filename;
break; break;
@@ -172,16 +191,15 @@ fs.stat(path.resolve(opts.config), function(err, stats) {
} }
} }
if (mbtiles) { if (mbtiles) {
console.log('No MBTiles specified, using ' + mbtiles); console.log(`No MBTiles specified, using ${mbtiles}`);
return startWithMBTiles(mbtiles); return startWithMBTiles(mbtiles);
} else { } else {
var url = 'https://github.com/klokantech/tileserver-gl/releases/download/v1.3.0/zurich_switzerland.mbtiles'; const url = 'https://github.com/maptiler/tileserver-gl/releases/download/v1.3.0/zurich_switzerland.mbtiles';
var filename = 'zurich_switzerland.mbtiles'; const filename = 'zurich_switzerland.mbtiles';
var stream = fs.createWriteStream(filename); const stream = fs.createWriteStream(filename);
console.log('Downloading sample data (' + filename + ') from ' + url); console.log(`No MBTiles found`);
stream.on('finish', function() { console.log(`[DEMO] Downloading sample data (${filename}) from ${url}`);
return startWithMBTiles(filename); stream.on('finish', () => startWithMBTiles(filename));
});
return request.get(url).pipe(stream); return request.get(url).pipe(stream);
} }
} }
@@ -189,7 +207,7 @@ fs.stat(path.resolve(opts.config), function(err, stats) {
return startWithMBTiles(mbtiles); return startWithMBTiles(mbtiles);
} }
} else { } else {
console.log('Using specified config file from ' + opts.config); console.log(`Using specified config file from ${opts.config}`);
return startServer(opts.config, null); return startServer(opts.config, null);
} }
}); });

View File

@@ -1,158 +1,171 @@
'use strict'; 'use strict';
var fs = require('fs'), const fs = require('fs');
path = require('path'), const path = require('path');
zlib = require('zlib'); const zlib = require('zlib');
var clone = require('clone'), const clone = require('clone');
express = require('express'), const express = require('express');
mbtiles = require('mbtiles'), const MBTiles = require('@mapbox/mbtiles');
pbf = require('pbf'), const Pbf = require('pbf');
VectorTile = require('vector-tile').VectorTile; const VectorTile = require('@mapbox/vector-tile').VectorTile;
var tileshrinkGl; const utils = require('./utils');
try {
tileshrinkGl = require('tileshrink-gl');
} catch (e) {}
var utils = require('./utils'); module.exports = {
init: (options, repo) => {
const app = express().disable('x-powered-by');
module.exports = function(options, repo, params, id, styles) { app.get('/:id/:z(\\d+)/:x(\\d+)/:y(\\d+).:format([\\w.]+)', (req, res, next) => {
var app = express().disable('x-powered-by'); const item = repo[req.params.id];
if (!item) {
var mbtilesFile = path.resolve(options.paths.mbtiles, params.mbtiles); return res.sendStatus(404);
var tileJSON = { }
'tiles': params.domains || options.domains let tileJSONFormat = item.tileJSON.format;
}; const z = req.params.z | 0;
const x = req.params.x | 0;
var shrinkers = {}; const y = req.params.y | 0;
let format = req.params.format;
repo[id] = tileJSON; if (format === options.pbfAlias) {
format = 'pbf';
var mbtilesFileStats = fs.statSync(mbtilesFile); }
if (!mbtilesFileStats.isFile() || mbtilesFileStats.size == 0) { if (format !== tileJSONFormat &&
throw Error('Not valid MBTiles file: ' + mbtilesFile); !(format === 'geojson' && tileJSONFormat === 'pbf')) {
} return res.status(404).send('Invalid format');
var source = new mbtiles(mbtilesFile, function(err) { }
source.getInfo(function(err, info) { if (z < item.tileJSON.minzoom || 0 || x < 0 || y < 0 ||
tileJSON['name'] = id; z > item.tileJSON.maxzoom ||
tileJSON['format'] = 'pbf'; x >= Math.pow(2, z) || y >= Math.pow(2, z)) {
return res.status(404).send('Out of bounds');
Object.assign(tileJSON, info); }
item.source.getTile(z, x, y, (err, data, headers) => {
tileJSON['tilejson'] = '2.0.0'; let isGzipped;
delete tileJSON['filesize']; if (err) {
delete tileJSON['mtime']; if (/does not exist/.test(err.message)) {
delete tileJSON['scheme']; return res.status(204).send();
} else {
Object.assign(tileJSON, params.tilejson || {}); return res.status(500).send(err.message);
utils.fixTileJSONCenter(tileJSON); }
});
});
var tilePattern = '/' + id + '/:z(\\d+)/:x(\\d+)/:y(\\d+).:format([\\w]+)';
app.get(tilePattern, function(req, res, next) {
var z = req.params.z | 0,
x = req.params.x | 0,
y = req.params.y | 0;
if (req.params.format != tileJSON.format &&
!(req.params.format == 'geojson' && tileJSON.format == 'pbf')) {
return res.status(404).send('Invalid format');
}
if (z < tileJSON.minzoom || 0 || x < 0 || y < 0 ||
z > tileJSON.maxzoom ||
x >= Math.pow(2, z) || y >= Math.pow(2, z)) {
return res.status(404).send('Out of bounds');
}
source.getTile(z, x, y, function(err, data, headers) {
if (err) {
if (/does not exist/.test(err.message)) {
return res.status(404).send(err.message);
} else { } else {
return res.status(500).send(err.message); if (data == null) {
} return res.status(404).send('Not found');
} else { } else {
if (data == null) { if (tileJSONFormat === 'pbf') {
return res.status(404).send('Not found'); isGzipped = data.slice(0, 2).indexOf(
} else { Buffer.from([0x1f, 0x8b])) === 0;
if (tileJSON['format'] == 'pbf') { if (options.dataDecoratorFunc) {
var isGzipped = data.slice(0,2).indexOf(
new Buffer([0x1f, 0x8b])) === 0;
var style = req.query.style;
if (style && tileshrinkGl) {
if (!shrinkers[style]) {
var styleJSON = styles[style];
if (styleJSON) {
var sourceName = null;
for (var sourceName_ in styleJSON.sources) {
var source = styleJSON.sources[sourceName_];
if (source &&
source.type == 'vector' &&
source.url.endsWith('/' + id + '.json')) {
sourceName = sourceName_;
}
}
shrinkers[style] = tileshrinkGl.createPBFShrinker(styleJSON, sourceName);
}
}
if (shrinkers[style]) {
if (isGzipped) { if (isGzipped) {
data = zlib.unzipSync(data); data = zlib.unzipSync(data);
isGzipped = false; isGzipped = false;
} }
data = shrinkers[style](data, z, tileJSON.maxzoom); data = options.dataDecoratorFunc(id, 'data', data, z, x, y);
//console.log(shrinkers[style].getStats());
} }
} }
} if (format === 'pbf') {
if (req.params.format == 'pbf') { headers['Content-Type'] = 'application/x-protobuf';
headers['Content-Type'] = 'application/x-protobuf'; } else if (format === 'geojson') {
} else if (req.params.format == 'geojson') { headers['Content-Type'] = 'application/json';
headers['Content-Type'] = 'application/json';
if (isGzipped) { if (isGzipped) {
data = zlib.unzipSync(data); data = zlib.unzipSync(data);
isGzipped = false; isGzipped = false;
}
var tile = new VectorTile(new pbf(data));
var geojson = {
"type": "FeatureCollection",
"features": []
};
for (var layerName in tile.layers) {
var layer = tile.layers[layerName];
for (var i = 0; i < layer.length; i++) {
var feature = layer.feature(i);
var featureGeoJSON = feature.toGeoJSON(x, y, z);
featureGeoJSON.properties.layer = layerName;
geojson.features.push(featureGeoJSON);
} }
const tile = new VectorTile(new Pbf(data));
const geojson = {
"type": "FeatureCollection",
"features": []
};
for (let layerName in tile.layers) {
const layer = tile.layers[layerName];
for (let i = 0; i < layer.length; i++) {
const feature = layer.feature(i);
const featureGeoJSON = feature.toGeoJSON(x, y, z);
featureGeoJSON.properties.layer = layerName;
geojson.features.push(featureGeoJSON);
}
}
data = JSON.stringify(geojson);
} }
data = JSON.stringify(geojson); delete headers['ETag']; // do not trust the tile ETag -- regenerate
} headers['Content-Encoding'] = 'gzip';
delete headers['ETag']; // do not trust the tile ETag -- regenerate res.set(headers);
headers['Content-Encoding'] = 'gzip';
res.set(headers);
if (!isGzipped) { if (!isGzipped) {
data = zlib.gzipSync(data); data = zlib.gzipSync(data);
isGzipped = true; isGzipped = true;
} }
return res.status(200).send(data); return res.status(200).send(data);
}
} }
});
});
app.get('/:id.json', (req, res, next) => {
const item = repo[req.params.id];
if (!item) {
return res.sendStatus(404);
}
const info = clone(item.tileJSON);
info.tiles = utils.getTileUrls(req, info.tiles,
`data/${req.params.id}`, info.format, item.publicUrl, {
'pbf': options.pbfAlias
});
return res.send(info);
});
return app;
},
add: (options, repo, params, id, publicUrl) => {
const mbtilesFile = path.resolve(options.paths.mbtiles, params.mbtiles);
let tileJSON = {
'tiles': params.domains || options.domains
};
const mbtilesFileStats = fs.statSync(mbtilesFile);
if (!mbtilesFileStats.isFile() || mbtilesFileStats.size === 0) {
throw Error(`Not valid MBTiles file: ${mbtilesFile}`);
}
let source;
const sourceInfoPromise = new Promise((resolve, reject) => {
source = new MBTiles(mbtilesFile, err => {
if (err) {
reject(err);
return;
}
source.getInfo((err, info) => {
if (err) {
reject(err);
return;
}
tileJSON['name'] = id;
tileJSON['format'] = 'pbf';
Object.assign(tileJSON, info);
tileJSON['tilejson'] = '2.0.0';
delete tileJSON['filesize'];
delete tileJSON['mtime'];
delete tileJSON['scheme'];
Object.assign(tileJSON, params.tilejson || {});
utils.fixTileJSONCenter(tileJSON);
if (options.dataDecoratorFunc) {
tileJSON = options.dataDecoratorFunc(id, 'tilejson', tileJSON);
}
resolve();
});
});
});
return sourceInfoPromise.then(() => {
repo[id] = {
tileJSON,
publicUrl,
source
} }
}); });
}); }
app.get('/' + id + '.json', function(req, res, next) {
var info = clone(tileJSON);
info.tiles = utils.getTileUrls(req, info.tiles,
'data/' + id, info.format);
return res.send(info);
});
return app;
}; };

View File

@@ -1,59 +1,60 @@
'use strict'; 'use strict';
var clone = require('clone'), const express = require('express');
express = require('express'), const fs = require('fs');
fs = require('fs'), const path = require('path');
path = require('path');
var utils = require('./utils'); const utils = require('./utils');
module.exports = function(options, allowedFonts) { module.exports = (options, allowedFonts) => {
var app = express().disable('x-powered-by'); const app = express().disable('x-powered-by');
var lastModified = new Date().toUTCString(); const lastModified = new Date().toUTCString();
var fontPath = options.paths.fonts; const fontPath = options.paths.fonts;
var existingFonts = {}; const existingFonts = {};
fs.readdir(options.paths.fonts, function(err, files) { const fontListingPromise = new Promise((resolve, reject) => {
files.forEach(function(file) { fs.readdir(options.paths.fonts, (err, files) => {
fs.stat(path.join(fontPath, file), function(err, stats) { if (err) {
if (!err) { reject(err);
return;
}
for (const file of files) {
fs.stat(path.join(fontPath, file), (err, stats) => {
if (err) {
reject(err);
return;
}
if (stats.isDirectory() && if (stats.isDirectory() &&
fs.existsSync(path.join(fontPath, file, '0-255.pbf'))) { fs.existsSync(path.join(fontPath, file, '0-255.pbf'))) {
existingFonts[path.basename(file)] = true; existingFonts[path.basename(file)] = true;
} }
} });
}); }
resolve();
}); });
}); });
app.get('/fonts/:fontstack/:range([\\d]+-[\\d]+).pbf', app.get('/fonts/:fontstack/:range([\\d]+-[\\d]+).pbf', (req, res, next) => {
function(req, res, next) { const fontstack = decodeURI(req.params.fontstack);
var fontstack = decodeURI(req.params.fontstack); const range = req.params.range;
var range = req.params.range;
return utils.getFontsPbf(options.serveAllFonts ? null : allowedFonts, utils.getFontsPbf(options.serveAllFonts ? null : allowedFonts,
fontPath, fontstack, range, existingFonts, fontPath, fontstack, range, existingFonts).then(concated => {
function(err, concated) {
if (err || concated.length === 0) {
console.log(err);
console.log(concated.length);
return res.status(400).send('');
} else {
res.header('Content-type', 'application/x-protobuf'); res.header('Content-type', 'application/x-protobuf');
res.header('Last-Modified', lastModified); res.header('Last-Modified', lastModified);
return res.send(concated); return res.send(concated);
} }, err => res.status(400).send(err)
}); );
}); });
app.get('/fontstacks.json', function(req, res, next) { app.get('/fonts.json', (req, res, next) => {
res.header('Content-type', 'application/json'); res.header('Content-type', 'application/json');
return res.send( return res.send(
Object.keys(options.serveAllFonts ? existingFonts : allowedFonts).sort() Object.keys(options.serveAllFonts ? existingFonts : allowedFonts).sort()
); );
}); });
return app; return fontListingPromise.then(() => app);
}; };

File diff suppressed because it is too large Load Diff

View File

@@ -1,114 +1,158 @@
'use strict'; 'use strict';
var path = require('path'), const path = require('path');
fs = require('fs'); const fs = require('fs');
var clone = require('clone'), const clone = require('clone');
express = require('express'); const express = require('express');
import {validate} from '@mapbox/mapbox-gl-style-spec';
const utils = require('./utils');
module.exports = function(options, repo, params, id, reportTiles, reportFont) { const httpTester = /^(http(s)?:)?\/\//;
var app = express().disable('x-powered-by');
var styleFile = path.join(options.paths.styles, params.style); const fixUrl = (req, url, publicUrl, opt_nokey) => {
if (!url || (typeof url !== 'string') || url.indexOf('local://') !== 0) {
return url;
}
const queryParams = [];
if (!opt_nokey && req.query.key) {
queryParams.unshift(`key=${encodeURIComponent(req.query.key)}`);
}
let query = '';
if (queryParams.length) {
query = `?${queryParams.join('&')}`;
}
return url.replace(
'local://', utils.getPublicUrl(publicUrl, req)) + query;
};
var styleJSON = clone(require(styleFile)); module.exports = {
Object.keys(styleJSON.sources).forEach(function(name) { init: (options, repo) => {
var source = styleJSON.sources[name]; const app = express().disable('x-powered-by');
var url = source.url;
if (url && url.lastIndexOf('mbtiles:', 0) === 0) {
var mbtilesFile = url.substring('mbtiles://'.length);
var fromData = mbtilesFile[0] == '{' &&
mbtilesFile[mbtilesFile.length - 1] == '}';
if (fromData) { app.get('/:id/style.json', (req, res, next) => {
mbtilesFile = mbtilesFile.substr(1, mbtilesFile.length - 2); const item = repo[req.params.id];
if (!item) {
return res.sendStatus(404);
} }
var identifier = reportTiles(mbtilesFile, fromData); const styleJSON_ = clone(item.styleJSON);
source.url = 'local://data/' + identifier + '.json'; for (const name of Object.keys(styleJSON_.sources)) {
} const source = styleJSON_.sources[name];
}); source.url = fixUrl(req, source.url, item.publicUrl);
}
// mapbox-gl-js viewer cannot handle sprite urls with query
if (styleJSON_.sprite) {
styleJSON_.sprite = fixUrl(req, styleJSON_.sprite, item.publicUrl, false);
}
if (styleJSON_.glyphs) {
styleJSON_.glyphs = fixUrl(req, styleJSON_.glyphs, item.publicUrl, false);
}
return res.send(styleJSON_);
});
styleJSON.layers.forEach(function(obj) { app.get('/:id/sprite:scale(@[23]x)?.:format([\\w]+)', (req, res, next) => {
if (obj['type'] == 'symbol') { const item = repo[req.params.id];
var fonts = (obj['layout'] || {})['text-font']; if (!item || !item.spritePath) {
if (fonts && fonts.length) { return res.sendStatus(404);
fonts.forEach(reportFont); }
} else { const scale = req.params.scale,
reportFont('Open Sans Regular'); format = req.params.format;
reportFont('Arial Unicode MS Regular'); const filename = `${item.spritePath + (scale || '')}.${format}`;
return fs.readFile(filename, (err, data) => {
if (err) {
console.log('Sprite load error:', filename);
return res.sendStatus(404);
} else {
if (format === 'json') res.header('Content-type', 'application/json');
if (format === 'png') res.header('Content-type', 'image/png');
return res.send(data);
}
});
});
return app;
},
remove: (repo, id) => {
delete repo[id];
},
add: (options, repo, params, id, publicUrl, reportTiles, reportFont) => {
const styleFile = path.resolve(options.paths.styles, params.style);
let styleFileData;
try {
styleFileData = fs.readFileSync(styleFile);
} catch (e) {
console.log('Error reading style file');
return false;
}
let validationErrors = validate(styleFileData);
if (validationErrors.length > 0) {
console.log(`The file "${params.style}" is not valid a valid style file:`);
for (const err of validationErrors) {
console.log(`${err.line}: ${err.message}`);
}
return false;
}
let styleJSON = JSON.parse(styleFileData);
for (const name of Object.keys(styleJSON.sources)) {
const source = styleJSON.sources[name];
const url = source.url;
if (url && url.lastIndexOf('mbtiles:', 0) === 0) {
let mbtilesFile = url.substring('mbtiles://'.length);
const fromData = mbtilesFile[0] === '{' &&
mbtilesFile[mbtilesFile.length - 1] === '}';
if (fromData) {
mbtilesFile = mbtilesFile.substr(1, mbtilesFile.length - 2);
const mapsTo = (params.mapping || {})[mbtilesFile];
if (mapsTo) {
mbtilesFile = mapsTo;
}
}
const identifier = reportTiles(mbtilesFile, fromData);
if (!identifier) {
return false;
}
source.url = `local://data/${identifier}.json`;
} }
} }
});
var spritePath; for (let obj of styleJSON.layers) {
if (obj['type'] === 'symbol') {
const fonts = (obj['layout'] || {})['text-font'];
if (fonts && fonts.length) {
fonts.forEach(reportFont);
} else {
reportFont('Open Sans Regular');
reportFont('Arial Unicode MS Regular');
}
}
}
var httpTester = /^(http(s)?:)?\/\//; let spritePath;
if (styleJSON.sprite && !httpTester.test(styleJSON.sprite)) {
spritePath = path.join(options.paths.sprites, if (styleJSON.sprite && !httpTester.test(styleJSON.sprite)) {
spritePath = path.join(options.paths.sprites,
styleJSON.sprite styleJSON.sprite
.replace('{style}', path.basename(styleFile, '.json')) .replace('{style}', path.basename(styleFile, '.json'))
.replace('{styleJsonFolder}', path.relative(options.paths.sprites, path.dirname(styleFile))) .replace('{styleJsonFolder}', path.relative(options.paths.sprites, path.dirname(styleFile)))
); );
styleJSON.sprite = 'local://styles/' + id + '/sprite'; styleJSON.sprite = `local://styles/${id}/sprite`;
} }
if (styleJSON.glyphs && !httpTester.test(styleJSON.glyphs)) { if (styleJSON.glyphs && !httpTester.test(styleJSON.glyphs)) {
styleJSON.glyphs = 'local://fonts/{fontstack}/{range}.pbf'; styleJSON.glyphs = 'local://fonts/{fontstack}/{range}.pbf';
} }
repo[id] = styleJSON; repo[id] = {
styleJSON,
app.get('/' + id + '.json', function(req, res, next) { spritePath,
var fixUrl = function(url, opt_nokey, opt_nostyle) { publicUrl,
var queryParams = []; name: styleJSON.name
if (!opt_nostyle) {
queryParams.push('style=' + id);
}
if (!opt_nokey && req.query.key) {
queryParams.unshift('key=' + req.query.key);
}
var query = '';
if (queryParams.length) {
query = '?' + queryParams.join('&');
}
return url.replace(
'local://', req.protocol + '://' + req.headers.host + '/') + query;
}; };
var styleJSON_ = clone(styleJSON); return true;
Object.keys(styleJSON_.sources).forEach(function(name) { }
var source = styleJSON_.sources[name];
source.url = fixUrl(source.url);
});
// mapbox-gl-js viewer cannot handle sprite urls with query
if (styleJSON_.sprite) {
styleJSON_.sprite = fixUrl(styleJSON_.sprite, true, true);
}
if (styleJSON_.glyphs) {
styleJSON_.glyphs = fixUrl(styleJSON_.glyphs, false, true);
}
return res.send(styleJSON_);
});
app.get('/' + id + '/sprite:scale(@[23]x)?\.:format([\\w]+)',
function(req, res, next) {
if (!spritePath) {
return res.status(404).send('File not found');
}
var scale = req.params.scale,
format = req.params.format;
var filename = spritePath + (scale || '') + '.' + format;
return fs.readFile(filename, function(err, data) {
if (err) {
console.log('Sprite load error:', filename);
return res.status(404).send('File not found');
} else {
if (format == 'json') res.header('Content-type', 'application/json');
if (format == 'png') res.header('Content-type', 'image/png');
return res.send(data);
}
});
});
return app;
}; };

View File

@@ -4,52 +4,55 @@
process.env.UV_THREADPOOL_SIZE = process.env.UV_THREADPOOL_SIZE =
Math.ceil(Math.max(4, require('os').cpus().length * 1.5)); Math.ceil(Math.max(4, require('os').cpus().length * 1.5));
var fs = require('fs'), const fs = require('fs');
path = require('path'); const path = require('path');
var base64url = require('base64url'), const chokidar = require('chokidar');
clone = require('clone'), const clone = require('clone');
cors = require('cors'), const cors = require('cors');
express = require('express'), const enableShutdown = require('http-shutdown');
handlebars = require('handlebars'), const express = require('express');
mercator = new (require('@mapbox/sphericalmercator'))(), const handlebars = require('handlebars');
morgan = require('morgan'); const mercator = new (require('@mapbox/sphericalmercator'))();
const morgan = require('morgan');
var packageJson = require('../package'), const packageJson = require('../package');
serve_font = require('./serve_font'), const serve_font = require('./serve_font');
serve_rendered = null, const serve_style = require('./serve_style');
serve_style = require('./serve_style'), const serve_data = require('./serve_data');
serve_data = require('./serve_data'), const utils = require('./utils');
utils = require('./utils');
var isLight = packageJson.name.slice(-6) == '-light'; let serve_rendered = null;
const isLight = packageJson.name.slice(-6) === '-light';
if (!isLight) { if (!isLight) {
// do not require `serve_rendered` in the light package // do not require `serve_rendered` in the light package
serve_rendered = require('./serve_rendered'); serve_rendered = require('./serve_rendered');
} }
module.exports = function(opts, callback) { function start(opts) {
console.log('Starting server'); console.log('Starting server');
var app = express().disable('x-powered-by'), const app = express().disable('x-powered-by'),
serving = { serving = {
styles: {}, styles: {},
rendered: {}, rendered: {},
data: {}, data: {},
fonts: {} fonts: {}
}; };
app.enable('trust proxy'); app.enable('trust proxy');
callback = callback || function() {}; if (process.env.NODE_ENV !== 'test') {
const defaultLogFormat = process.env.NODE_ENV === 'production' ? 'tiny' : 'dev';
if (process.env.NODE_ENV !== 'production' && const logFormat = opts.logFormat || defaultLogFormat;
process.env.NODE_ENV !== 'test') { app.use(morgan(logFormat, {
app.use(morgan('dev')); stream: opts.logFile ? fs.createWriteStream(opts.logFile, { flags: 'a' }) : process.stdout,
skip: (req, res) => opts.silent && (res.statusCode === 200 || res.statusCode === 304)
}));
} }
var config = opts.config || null; let config = opts.config || null;
var configPath = null; let configPath = null;
if (opts.configPath) { if (opts.configPath) {
configPath = path.resolve(opts.configPath); configPath = path.resolve(opts.configPath);
try { try {
@@ -65,8 +68,8 @@ module.exports = function(opts, callback) {
process.exit(1); process.exit(1);
} }
var options = config.options || {}; const options = config.options || {};
var paths = options.paths || {}; const paths = options.paths || {};
options.paths = paths; options.paths = paths;
paths.root = path.resolve( paths.root = path.resolve(
configPath ? path.dirname(configPath) : process.cwd(), configPath ? path.dirname(configPath) : process.cwd(),
@@ -76,118 +79,218 @@ module.exports = function(opts, callback) {
paths.sprites = path.resolve(paths.root, paths.sprites || ''); paths.sprites = path.resolve(paths.root, paths.sprites || '');
paths.mbtiles = path.resolve(paths.root, paths.mbtiles || ''); paths.mbtiles = path.resolve(paths.root, paths.mbtiles || '');
var data = clone(config.data || {}); const startupPromises = [];
app.use(cors()); const checkPath = type => {
if (!fs.existsSync(paths[type])) {
Object.keys(config.styles || {}).forEach(function(id) { console.error(`The specified path for "${type}" does not exist (${paths[type]}).`);
var item = config.styles[id]; process.exit(1);
if (!item.style || item.style.length == 0) {
console.log('Missing "style" property for ' + id);
return;
} }
};
checkPath('styles');
checkPath('fonts');
checkPath('sprites');
checkPath('mbtiles');
if (options.dataDecorator) {
try {
options.dataDecoratorFunc = require(path.resolve(paths.root, options.dataDecorator));
} catch (e) {}
}
const data = clone(config.data || {});
if (opts.cors) {
app.use(cors());
}
app.use('/data/', serve_data.init(options, serving.data));
app.use('/styles/', serve_style.init(options, serving.styles));
if (serve_rendered) {
startupPromises.push(
serve_rendered.init(options, serving.rendered)
.then(sub => {
app.use('/styles/', sub);
})
);
}
let addStyle = (id, item, allowMoreData, reportFonts) => {
let success = true;
if (item.serve_data !== false) { if (item.serve_data !== false) {
app.use('/styles/', serve_style(options, serving.styles, item, id, success = serve_style.add(options, serving.styles, item, id, opts.publicUrl,
function(mbtiles, fromData) { (mbtiles, fromData) => {
var dataItemId; let dataItemId;
Object.keys(data).forEach(function(id) { for (const id of Object.keys(data)) {
if (fromData) { if (fromData) {
if (id == mbtiles) { if (id === mbtiles) {
dataItemId = id; dataItemId = id;
} }
} else { } else {
if (data[id].mbtiles == mbtiles) { if (data[id].mbtiles === mbtiles) {
dataItemId = id; dataItemId = id;
} }
} }
}); }
if (dataItemId) { // mbtiles exist in the data config if (dataItemId) { // mbtiles exist in the data config
return dataItemId; return dataItemId;
} else if (fromData) {
console.log('ERROR: data "' + mbtiles + '" not found!');
process.exit(1);
} else { } else {
var id = mbtiles.substr(0, mbtiles.lastIndexOf('.')) || mbtiles; if (fromData || !allowMoreData) {
while (data[id]) id += '_'; console.log(`ERROR: style "${item.style}" using unknown mbtiles "${mbtiles}"! Skipping...`);
data[id] = { return undefined;
'mbtiles': mbtiles } else {
}; let id = mbtiles.substr(0, mbtiles.lastIndexOf('.')) || mbtiles;
return id; while (data[id]) id += '_';
} data[id] = {
}, function(font) { 'mbtiles': mbtiles
serving.fonts[font] = true; };
})); return id;
}
if (item.serve_rendered !== false) {
if (serve_rendered) {
app.use('/styles/' + id + '/',
serve_rendered(options, serving.rendered, item, id,
function(mbtiles) {
var mbtilesFile;
Object.keys(data).forEach(function(id) {
if (id == mbtiles) {
mbtilesFile = data[id].mbtiles;
} }
}); }
return mbtilesFile; }, font => {
})); if (reportFonts) {
serving.fonts[font] = true;
}
});
}
if (success && item.serve_rendered !== false) {
if (serve_rendered) {
startupPromises.push(serve_rendered.add(options, serving.rendered, item, id, opts.publicUrl,
mbtiles => {
let mbtilesFile;
for (const id of Object.keys(data)) {
if (id === mbtiles) {
mbtilesFile = data[id].mbtiles;
}
}
return mbtilesFile;
}
));
} else { } else {
item.serve_rendered = false; item.serve_rendered = false;
} }
} }
}); };
app.use('/', serve_font(options, serving.fonts)); for (const id of Object.keys(config.styles || {})) {
const item = config.styles[id];
Object.keys(data).forEach(function(id) { if (!item.style || item.style.length === 0) {
var item = data[id]; console.log(`Missing "style" property for ${id}`);
if (!item.mbtiles || item.mbtiles.length == 0) { continue;
console.log('Missing "mbtiles" property for ' + id);
return;
} }
app.use('/data/', serve_data(options, serving.data, item, id, serving.styles)); addStyle(id, item, true, true);
}); }
app.get('/styles.json', function(req, res, next) { startupPromises.push(
var result = []; serve_font(options, serving.fonts).then(sub => {
var query = req.query.key ? ('?key=' + req.query.key) : ''; app.use('/', sub);
Object.keys(serving.styles).forEach(function(id) { })
var styleJSON = serving.styles[id]; );
for (const id of Object.keys(data)) {
const item = data[id];
if (!item.mbtiles || item.mbtiles.length === 0) {
console.log(`Missing "mbtiles" property for ${id}`);
continue;
}
startupPromises.push(
serve_data.add(options, serving.data, item, id, opts.publicUrl)
);
}
if (options.serveAllStyles) {
fs.readdir(options.paths.styles, {withFileTypes: true}, (err, files) => {
if (err) {
return;
}
for (const file of files) {
if (file.isFile() &&
path.extname(file.name).toLowerCase() == '.json') {
let id = path.basename(file.name, '.json');
let item = {
style: file.name
};
addStyle(id, item, false, false);
}
}
});
const watcher = chokidar.watch(path.join(options.paths.styles, '*.json'),
{
});
watcher.on('all',
(eventType, filename) => {
if (filename) {
let id = path.basename(filename, '.json');
console.log(`Style "${id}" changed, updating...`);
serve_style.remove(serving.styles, id);
if (serve_rendered) {
serve_rendered.remove(serving.rendered, id);
}
if (eventType == "add" || eventType == "change") {
let item = {
style: filename
};
addStyle(id, item, false, false);
}
}
});
}
app.get('/styles.json', (req, res, next) => {
const result = [];
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({ result.push({
version: styleJSON.version, version: styleJSON.version,
name: styleJSON.name, name: styleJSON.name,
id: id, id: id,
url: req.protocol + '://' + req.headers.host + url: `${utils.getPublicUrl(opts.publicUrl, req)}styles/${id}/style.json${query}`
'/styles/' + id + '.json' + query
}); });
}); }
res.send(result); res.send(result);
}); });
var addTileJSONs = function(arr, req, type) { app.get('/process', (req, res, next) => {
Object.keys(serving[type]).forEach(function(id) { const result = {};
var info = clone(serving[type][id]); const id = req.query.style || '';
var path = ''; const item = {
if (type == 'rendered') { 'style': id + '.json'
path = 'styles/' + id + '/rendered'; };
result[id] = true;
addStyle(id, item, true, true);
res.send(serving.styles);
});
const addTileJSONs = (arr, req, type) => {
for (const id of Object.keys(serving[type])) {
const info = clone(serving[type][id].tileJSON);
let path = '';
if (type === 'rendered') {
path = `styles/${id}`;
} else { } else {
path = type + '/' + id; path = `${type}/${id}`;
} }
info.tiles = utils.getTileUrls(req, info.tiles, path, info.format); info.tiles = utils.getTileUrls(req, info.tiles, path, info.format, opts.publicUrl, {
'pbf': options.pbfAlias
});
arr.push(info); arr.push(info);
}); }
return arr; return arr;
}; };
app.get('/rendered.json', function(req, res, next) { app.get('/rendered.json', (req, res, next) => {
res.send(addTileJSONs([], req, 'rendered')); res.send(addTileJSONs([], req, 'rendered'));
}); });
app.get('/data.json', function(req, res, next) { app.get('/data.json', (req, res, next) => {
res.send(addTileJSONs([], req, 'data')); res.send(addTileJSONs([], req, 'data'));
}); });
app.get('/index.json', function(req, res, next) { app.get('/index.json', (req, res, next) => {
res.send(addTileJSONs(addTileJSONs([], req, 'rendered'), req, 'data')); res.send(addTileJSONs(addTileJSONs([], req, 'rendered'), req, 'data'));
}); });
@@ -195,93 +298,92 @@ module.exports = function(opts, callback) {
// serve web presentations // serve web presentations
app.use('/', express.static(path.join(__dirname, '../public/resources'))); app.use('/', express.static(path.join(__dirname, '../public/resources')));
var templates = path.join(__dirname, '../public/templates'); const templates = path.join(__dirname, '../public/templates');
var serveTemplate = function(path, template, dataGetter) { const serveTemplate = (urlPath, template, dataGetter) => {
fs.readFile(templates + '/' + template + '.tmpl', function(err, content) { let templateFile = `${templates}/${template}.tmpl`;
if (err) { if (template === 'index') {
console.log('Template not found:', err); if (options.frontPage === false) {
return;
} else if (options.frontPage &&
options.frontPage.constructor === String) {
templateFile = path.resolve(paths.root, options.frontPage);
} }
var compiled = handlebars.compile(content.toString()); }
startupPromises.push(new Promise((resolve, reject) => {
app.use(path, function(req, res, next) { fs.readFile(templateFile, (err, content) => {
var data = {}; if (err) {
if (dataGetter) { err = new Error(`Template not found: ${err.message}`);
data = dataGetter(req); reject(err);
if (!data) { return;
return res.status(404).send('Not found');
}
} }
data['server_version'] = packageJson.name + ' v' + packageJson.version; const compiled = handlebars.compile(content.toString());
data['is_light'] = isLight;
data['key_query_part'] = app.use(urlPath, (req, res, next) => {
req.query.key ? 'key=' + req.query.key + '&amp;' : ''; let data = {};
data['key_query'] = req.query.key ? '?key=' + req.query.key : ''; if (dataGetter) {
return res.status(200).send(compiled(data)); data = dataGetter(req);
if (!data) {
return res.status(404).send('Not found');
}
}
data['server_version'] = `${packageJson.name} v${packageJson.version}`;
data['public_url'] = opts.publicUrl || '/';
data['is_light'] = isLight;
data['key_query_part'] =
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));
});
resolve();
}); });
}); }));
}; };
serveTemplate('/$', 'index', function(req) { serveTemplate('/$', 'index', req => {
var styles = clone(config.styles || {}); const styles = clone(serving.styles || {});
Object.keys(styles).forEach(function(id) { for (const id of Object.keys(styles)) {
var style = styles[id]; const style = styles[id];
style.name = (serving.styles[id] || serving.rendered[id] || {}).name; style.name = (serving.styles[id] || serving.rendered[id] || {}).name;
style.serving_data = serving.styles[id]; style.serving_data = serving.styles[id];
style.serving_rendered = serving.rendered[id]; style.serving_rendered = serving.rendered[id];
if (style.serving_rendered) { if (style.serving_rendered) {
var center = style.serving_rendered.center; const center = style.serving_rendered.tileJSON.center;
if (center) { if (center) {
style.viewer_hash = '#' + center[2] + '/' + style.viewer_hash = `#${center[2]}/${center[1].toFixed(5)}/${center[0].toFixed(5)}`;
center[1].toFixed(5) + '/' +
center[0].toFixed(5);
var centerPx = mercator.px([center[0], center[1]], center[2]); const centerPx = mercator.px([center[0], center[1]], center[2]);
style.thumbnail = center[2] + '/' + style.thumbnail = `${center[2]}/${Math.floor(centerPx[0] / 256)}/${Math.floor(centerPx[1] / 256)}.png`;
Math.floor(centerPx[0] / 256) + '/' +
Math.floor(centerPx[1] / 256) + '.png';
} }
var query = req.query.key ? ('?key=' + req.query.key) : ''; style.xyz_link = utils.getTileUrls(
style.wmts_link = 'http://wmts.maptiler.com/' + req, style.serving_rendered.tileJSON.tiles,
base64url('http://' + req.headers.host + `styles/${id}`, style.serving_rendered.tileJSON.format, opts.publicUrl)[0];
'/styles/' + id + '/rendered.json' + query) + '/wmts';
var tiles = utils.getTileUrls(
req, style.serving_rendered.tiles,
'styles/' + id + '/rendered', style.serving_rendered.format);
style.xyz_link = tiles[0];
} }
}); }
var data = clone(serving.data || {}); const data = clone(serving.data || {});
Object.keys(data).forEach(function(id) { for (const id of Object.keys(data)) {
var data_ = data[id]; const data_ = data[id];
var center = data_.center; const tilejson = data[id].tileJSON;
const center = tilejson.center;
if (center) { if (center) {
data_.viewer_hash = '#' + center[2] + '/' + data_.viewer_hash = `#${center[2]}/${center[1].toFixed(5)}/${center[0].toFixed(5)}`;
center[1].toFixed(5) + '/' +
center[0].toFixed(5);
} }
data_.is_vector = data_.format == 'pbf'; data_.is_vector = tilejson.format === 'pbf';
if (!data_.is_vector) { if (!data_.is_vector) {
if (center) { if (center) {
var centerPx = mercator.px([center[0], center[1]], center[2]); const centerPx = mercator.px([center[0], center[1]], center[2]);
data_.thumbnail = center[2] + '/' + data_.thumbnail = `${center[2]}/${Math.floor(centerPx[0] / 256)}/${Math.floor(centerPx[1] / 256)}.${data_.tileJSON.format}`;
Math.floor(centerPx[0] / 256) + '/' +
Math.floor(centerPx[1] / 256) + '.' + data_.format;
} }
var query = req.query.key ? ('?key=' + req.query.key) : ''; data_.xyz_link = utils.getTileUrls(
data_.wmts_link = 'http://wmts.maptiler.com/' + req, tilejson.tiles, `data/${id}`, tilejson.format, opts.publicUrl, {
base64url('http://' + req.headers.host + 'pbf': options.pbfAlias
'/data/' + id + '.json' + query) + '/wmts'; })[0];
var tiles = utils.getTileUrls(
req, data_.tiles, 'data/' + id, data_.format);
data_.xyz_link = tiles[0];
} }
if (data_.filesize) { if (data_.filesize) {
var suffix = 'kB'; let suffix = 'kB';
var size = parseInt(data_.filesize, 10) / 1024; let size = parseInt(data_.filesize, 10) / 1024;
if (size > 1024) { if (size > 1024) {
suffix = 'MB'; suffix = 'MB';
size /= 1024; size /= 1024;
@@ -290,18 +392,18 @@ module.exports = function(opts, callback) {
suffix = 'GB'; suffix = 'GB';
size /= 1024; size /= 1024;
} }
data_.formatted_filesize = size.toFixed(2) + ' ' + suffix; data_.formatted_filesize = `${size.toFixed(2)} ${suffix}`;
} }
}); }
return { return {
styles: styles, styles: Object.keys(styles).length ? styles : null,
data: data data: Object.keys(data).length ? data : null
}; };
}); });
serveTemplate('/styles/:id/$', 'viewer', function(req) { serveTemplate('/styles/:id/$', 'viewer', req => {
var id = req.params.id; const id = req.params.id;
var style = clone((config.styles || {})[id]); const style = clone(((serving.styles || {})[id] || {}).styleJSON);
if (!style) { if (!style) {
return null; return null;
} }
@@ -317,32 +419,88 @@ module.exports = function(opts, callback) {
return res.redirect(301, '/styles/' + req.params.id + '/'); return res.redirect(301, '/styles/' + req.params.id + '/');
}); });
*/ */
serveTemplate('/styles/:id/wmts.xml', 'wmts', req => {
const id = req.params.id;
const wmts = clone((serving.styles || {})[id]);
if (!wmts) {
return null;
}
if (wmts.hasOwnProperty("serve_rendered") && !wmts.serve_rendered) {
return null;
}
wmts.id = id;
wmts.name = (serving.styles[id] || serving.rendered[id]).name;
wmts.baseUrl = `${req.get('X-Forwarded-Protocol') ? req.get('X-Forwarded-Protocol') : req.protocol}://${req.get('host')}`;
return wmts;
});
serveTemplate('/data/:id/$', 'data', function(req) { serveTemplate('/data/:id/$', 'data', req => {
var id = req.params.id; const id = req.params.id;
var data = clone(serving.data[id]); const data = clone(serving.data[id]);
if (!data) { if (!data) {
return null; return null;
} }
data.id = id; data.id = id;
data.is_vector = data.format == 'pbf'; data.is_vector = data.tileJSON.format === 'pbf';
return data; return data;
}); });
var server = app.listen(process.env.PORT || opts.port, process.env.BIND || opts.bind, function() { let startupComplete = false;
console.log('Listening at http://%s:%d/', const startupPromise = Promise.all(startupPromises).then(() => {
this.address().address, this.address().port); console.log('Startup complete');
startupComplete = true;
return callback(); });
app.get('/health', (req, res, next) => {
if (startupComplete) {
return res.status(200).send('OK');
} else {
return res.status(503).send('Starting');
}
}); });
process.on('SIGINT', function() { const server = app.listen(process.env.PORT || opts.port, process.env.BIND || opts.bind, function () {
process.exit(); let address = this.address().address;
if (address.indexOf('::') === 0) {
address = `[${address}]`; // literal IPv6 address
}
console.log(`Listening at http://${address}:${this.address().port}/`);
}); });
setTimeout(callback, 1000); // add server.shutdown() to gracefully stop serving
enableShutdown(server);
return { return {
app: app, app: app,
server: server server: server,
startupPromise: startupPromise
}; };
}
module.exports = opts => {
const running = start(opts);
running.startupPromise.catch(err => {
console.error(err.message);
process.exit(1);
});
process.on('SIGINT', () => {
process.exit();
});
process.on('SIGHUP', () => {
console.log('Stopping server and reloading config');
running.server.shutdown(() => {
for (const key in require.cache) {
delete require.cache[key];
}
const restarted = start(opts);
running.server = restarted.server;
running.app = restarted.app;
});
});
return running;
}; };

View File

@@ -1,46 +1,72 @@
'use strict'; 'use strict';
var async = require('async'), const path = require('path');
path = require('path'), const fs = require('fs');
fs = require('fs');
var clone = require('clone'), const clone = require('clone');
glyphCompose = require('glyph-pbf-composite'); const glyphCompose = require('@mapbox/glyph-pbf-composite');
module.exports.getTileUrls = function(req, domains, path, format) {
module.exports.getPublicUrl = (publicUrl, req) => publicUrl || `${req.protocol}://${req.headers.host}/`;
module.exports.getTileUrls = (req, domains, path, format, publicUrl, aliases) => {
if (domains) { if (domains) {
if (domains.constructor === String && domains.length > 0) { if (domains.constructor === String && domains.length > 0) {
domains = domains.split(','); domains = domains.split(',');
} }
const host = req.headers.host;
const hostParts = host.split('.');
const relativeSubdomainsUsable = hostParts.length > 1 &&
!/^([0-9]{1,3}\.){3}[0-9]{1,3}(\:[0-9]+)?$/.test(host);
const newDomains = [];
for (const domain of domains) {
if (domain.indexOf('*') !== -1) {
if (relativeSubdomainsUsable) {
const newParts = hostParts.slice(1);
newParts.unshift(domain.replace('*', hostParts[0]));
newDomains.push(newParts.join('.'));
}
} else {
newDomains.push(domain);
}
}
domains = newDomains;
} }
if (!domains || domains.length == 0) { if (!domains || domains.length == 0) {
domains = [req.headers.host]; domains = [req.headers.host];
} }
var key = req.query.key; const key = req.query.key;
var queryParams = []; const queryParams = [];
if (req.query.key) { if (req.query.key) {
queryParams.push('key=' + req.query.key); queryParams.push(`key=${encodeURIComponent(req.query.key)}`);
} }
if (req.query.style) { if (req.query.style) {
queryParams.push('style=' + req.query.style); queryParams.push(`style=${encodeURIComponent(req.query.style)}`);
} }
var query = queryParams.length > 0 ? ('?' + queryParams.join('&')) : ''; const query = queryParams.length > 0 ? (`?${queryParams.join('&')}`) : '';
var uris = []; if (aliases && aliases[format]) {
domains.forEach(function(domain) { format = aliases[format];
uris.push(req.protocol + '://' + domain + '/' + path + }
'/{z}/{x}/{y}.' + format + query);
}); const uris = [];
if (!publicUrl) {
for (const domain of domains) {
uris.push(`${req.protocol}://${domain}/${path}/{z}/{x}/{y}.${format}${query}`);
}
} else {
uris.push(`${publicUrl}${path}/{z}/{x}/{y}.${format}${query}`)
}
return uris; return uris;
}; };
module.exports.fixTileJSONCenter = function(tileJSON) { module.exports.fixTileJSONCenter = tileJSON => {
if (tileJSON.bounds && !tileJSON.center) { if (tileJSON.bounds && !tileJSON.center) {
var fitWidth = 1024; const fitWidth = 1024;
var tiles = fitWidth / 256; const tiles = fitWidth / 256;
tileJSON.center = [ tileJSON.center = [
(tileJSON.bounds[0] + tileJSON.bounds[2]) / 2, (tileJSON.bounds[0] + tileJSON.bounds[2]) / 2,
(tileJSON.bounds[1] + tileJSON.bounds[3]) / 2, (tileJSON.bounds[1] + tileJSON.bounds[3]) / 2,
@@ -52,47 +78,54 @@ module.exports.fixTileJSONCenter = function(tileJSON) {
} }
}; };
module.exports.getFontsPbf = function(allowedFonts, fontPath, names, range, fallbacks, callback) { const getFontPbf = (allowedFonts, fontPath, name, range, fallbacks) => new Promise((resolve, reject) => {
var getFontPbf = function(allowedFonts, name, range, callback, fallbacks) { if (!allowedFonts || (allowedFonts[name] && fallbacks)) {
if (!allowedFonts || (allowedFonts[name] && fallbacks)) { const filename = path.join(fontPath, name, `${range}.pbf`);
var filename = path.join(fontPath, name, range + '.pbf'); if (!fallbacks) {
if (!fallbacks) { fallbacks = clone(allowedFonts || {});
fallbacks = clone(allowedFonts || {}); }
} delete fallbacks[name];
delete fallbacks[name]; fs.readFile(filename, (err, data) => {
return fs.readFile(filename, function(err, data) { if (err) {
if (err) { console.error(`ERROR: Font not found: ${name}`);
console.error('ERROR: Font not found:', name); if (fallbacks && Object.keys(fallbacks).length) {
if (fallbacks && Object.keys(fallbacks).length) { let fallbackName;
var fallbackName = Object.keys(fallbacks)[0];
console.error('ERROR: Trying to use', fallbackName, 'as a fallback'); let fontStyle = name.split(' ').pop();
delete fallbacks[fallbackName]; if (['Regular', 'Bold', 'Italic'].indexOf(fontStyle) < 0) {
return getFontPbf(null, fallbackName, range, callback, fallbacks); fontStyle = 'Regular';
} else {
return callback(new Error('Font load error: ' + name));
} }
fallbackName = `Noto Sans ${fontStyle}`;
if (!fallbacks[fallbackName]) {
fallbackName = `Open Sans ${fontStyle}`;
if (!fallbacks[fallbackName]) {
fallbackName = Object.keys(fallbacks)[0];
}
}
console.error(`ERROR: Trying to use ${fallbackName} as a fallback`);
delete fallbacks[fallbackName];
getFontPbf(null, fontPath, fallbackName, range, fallbacks).then(resolve, reject);
} else { } else {
return callback(null, data); reject(`Font load error: ${name}`);
} }
}); } else {
} else { resolve(data);
return callback(new Error('Font not allowed: ' + name)); }
}
};
var fonts = names.split(',');
var queue = [];
fonts.forEach(function(font) {
queue.push(function(callback) {
getFontPbf(allowedFonts, font, range, callback, clone(allowedFonts || fallbacks));
}); });
}); } else {
reject(`Font not allowed: ${name}`);
}
});
return async.parallel(queue, function(err, results) { module.exports.getFontsPbf = (allowedFonts, fontPath, names, range, fallbacks) => {
if (err) { const fonts = names.split(',');
callback(err, new Buffer([])); const queue = [];
} else { for (const font of fonts) {
callback(err, glyphCompose.combine(results)); queue.push(
} getFontPbf(allowedFonts, fontPath, font, range, clone(allowedFonts || fallbacks))
}); );
}
return Promise.all(queue).then(values => glyphCompose.combine(values));
}; };

View File

@@ -38,6 +38,14 @@ var testTileJSON = function(url) {
}; };
describe('Metadata', function() { describe('Metadata', function() {
describe('/health', function() {
it('returns 200', function(done) {
supertest(app)
.get('/health')
.expect(200, done);
});
});
testTileJSONArray('/index.json'); testTileJSONArray('/index.json');
testTileJSONArray('/rendered.json'); testTileJSONArray('/rendered.json');
testTileJSONArray('/data.json'); testTileJSONArray('/data.json');
@@ -63,6 +71,6 @@ describe('Metadata', function() {
}); });
}); });
testTileJSON('/styles/test-style/rendered.json'); testTileJSON('/styles/test-style.json');
testTileJSON('/data/openmaptiles.json'); testTileJSON('/data/openmaptiles.json');
}); });

View File

@@ -3,18 +3,22 @@ process.env.NODE_ENV = 'test';
global.should = require('should'); global.should = require('should');
global.supertest = require('supertest'); global.supertest = require('supertest');
require = require('esm')(module);
before(function() { before(function() {
console.log('global setup'); console.log('global setup');
process.chdir('test_data'); process.chdir('test_data');
var running = require('../src/server')({ var running = require('../src/server')({
configPath: 'config.json', configPath: 'config.json',
port: 8888 port: 8888,
publicUrl: '/test/'
}); });
global.app = running.app; global.app = running.app;
global.server = running.server; global.server = running.server;
return running.startupPromise;
}); });
after(function() { after(function() {
console.log('global teardown'); console.log('global teardown');
global.server.close(function() { console.log('Done'); }); global.server.close(function() { console.log('Done'); process.exit(); });
}); });

View File

@@ -11,24 +11,25 @@ var testIs = function(url, type, status) {
var prefix = 'test-style'; var prefix = 'test-style';
describe('Styles', function() { describe('Styles', function() {
describe('/styles/' + prefix + '.json is valid style', function() { describe('/styles/' + prefix + '/style.json is valid style', function() {
testIs('/styles/' + prefix + '.json', /application\/json/); testIs('/styles/' + prefix + '/style.json', /application\/json/);
it('contains expected properties', function(done) { it('contains expected properties', function(done) {
supertest(app) supertest(app)
.get('/styles/' + prefix + '.json') .get('/styles/' + prefix + '/style.json')
.expect(function(res) { .expect(function(res) {
res.body.version.should.equal(8); res.body.version.should.equal(8);
res.body.name.should.be.String(); res.body.name.should.be.String();
res.body.sources.should.be.Object(); res.body.sources.should.be.Object();
res.body.glyphs.should.be.String(); res.body.glyphs.should.be.String();
res.body.sprite.should.be.String(); res.body.sprite.should.be.String();
res.body.sprite.should.equal('/test/styles/test-style/sprite');
res.body.layers.should.be.Array(); res.body.layers.should.be.Array();
}).end(done); }).end(done);
}); });
}); });
describe('/styles/streets.json is not served', function() { describe('/styles/streets/style.json is not served', function() {
testIs('/styles/streets.json', /./, 404); testIs('/styles/streets/style.json', /./, 404);
}); });
describe('/styles/' + prefix + '/sprite[@2x].{format}', function() { describe('/styles/' + prefix + '/sprite[@2x].{format}', function() {

View File

@@ -23,6 +23,6 @@ describe('Vector tiles', function() {
testTile(prefix, 0, 1, 0, 404); testTile(prefix, 0, 1, 0, 404);
testTile(prefix, 0, 0, 1, 404); testTile(prefix, 0, 0, 1, 404);
testTile(prefix, 14, 0, 0, 404); // non existent tile testTile(prefix, 14, 0, 0, 204); // non existent tile
}); });
}); });

View File

@@ -1,6 +1,6 @@
var testTile = function(prefix, z, x, y, format, status, scale, type) { var testTile = function(prefix, z, x, y, format, status, scale, type) {
if (scale) y += '@' + scale + 'x'; if (scale) y += '@' + scale + 'x';
var path = '/styles/' + prefix + '/rendered/' + z + '/' + x + '/' + y + '.' + format; var path = '/styles/' + prefix + '/' + z + '/' + x + '/' + y + '.' + format;
it(path + ' returns ' + status, function(done) { it(path + ' returns ' + status, function(done) {
var test = supertest(app).get(path); var test = supertest(app).get(path);
test.expect(status); test.expect(status);
@@ -26,7 +26,6 @@ describe('Raster tiles', function() {
testTile(prefix, 0, 0, 0, 'png', 200, 2); testTile(prefix, 0, 0, 0, 'png', 200, 2);
testTile(prefix, 0, 0, 0, 'png', 200, 3); testTile(prefix, 0, 0, 0, 'png', 200, 3);
testTile(prefix, 2, 1, 1, 'png', 200, 3); testTile(prefix, 2, 1, 1, 'png', 200, 3);
testTile(prefix, 0, 0, 0, 'png', 200, 4);
}); });
}); });